diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -147,7 +147,8 @@ Symver, MipsABIFlags, Addrsig, - Fill + Fill, + LinkerOptions, }; ChunkKind Kind; @@ -349,6 +350,22 @@ static bool classof(const Chunk *S) { return S->Kind == ChunkKind::Addrsig; } }; +struct LinkerOption { + StringRef Key; + StringRef Value; +}; + +struct LinkerOptionsSection : Section { + Optional> Options; + Optional Content; + + LinkerOptionsSection() : Section(ChunkKind::LinkerOptions) {} + + static bool classof(const Chunk *S) { + return S->Kind == ChunkKind::LinkerOptions; + } +}; + struct SymverSection : Section { std::vector Entries; @@ -464,6 +481,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::AddrsigSymbol) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::DynamicEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::LinkerOption) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::NoteEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) @@ -631,6 +649,10 @@ static void mapping(IO &IO, ELFYAML::AddrsigSymbol &Sym); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::LinkerOption &Sym); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::Relocation &Rel); }; diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -200,6 +200,9 @@ void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::GnuHashSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::LinkerOptionsSection &Section, + ContiguousBlobAccumulator &CBA); void writeFill(ELFYAML::Fill &Fill, ContiguousBlobAccumulator &CBA); @@ -466,6 +469,8 @@ writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast(Sec)) { + writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { @@ -893,6 +898,30 @@ } template +void ELFState::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + if (Section.Content) { + SHeader.sh_size = writeContent(OS, Section.Content, None); + return; + } + + if (!Section.Options) + return; + + for (const ELFYAML::LinkerOption &LO : *Section.Options) { + OS.write(LO.Key.data(), LO.Key.size()); + OS.write('\0'); + OS.write(LO.Value.data(), LO.Value.size()); + OS.write('\0'); + SHeader.sh_size += (LO.Key.size() + LO.Value.size() + 2); + } +} + +template void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::HashSection &Section, ContiguousBlobAccumulator &CBA) { diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -1100,6 +1100,12 @@ IO.mapRequired("Size", Fill.Size); } +static void sectionMapping(IO &IO, ELFYAML::LinkerOptionsSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Options", Section.Options); + IO.mapOptional("Content", Section.Content); +} + void MappingTraits::mapping( IO &IO, ELFYAML::SectionOrType §ionOrType) { IO.mapRequired("SectionOrType", sectionOrType.sectionNameOrType); @@ -1217,6 +1223,11 @@ Section.reset(new ELFYAML::AddrsigSection()); sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_LLVM_LINKER_OPTIONS: + if (!IO.outputting()) + Section.reset(new ELFYAML::LinkerOptionsSection()); + sectionMapping(IO, *cast(Section.get())); + break; default: if (!IO.outputting()) { StringRef Name; @@ -1355,6 +1366,12 @@ return {}; } + if (const auto *Sec = dyn_cast(C.get())) { + if (Sec->Options && Sec->Content) + return "\"Options\" and \"Content\" can't be used together"; + return {}; + } + if (const auto *F = dyn_cast(C.get())) { if (!F->Pattern) return {}; @@ -1493,6 +1510,13 @@ IO.mapOptional("Index", Sym.Index); } +void MappingTraits::mapping(IO &IO, + ELFYAML::LinkerOption &Opt) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapRequired("Name", Opt.Key); + IO.mapRequired("Value", Opt.Value); +} + LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_AFL_REG) LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_ABI_FP) LLVM_YAML_STRONG_TYPEDEF(uint32_t, MIPS_AFL_EXT) diff --git a/llvm/test/tools/obj2yaml/linker-options.yaml b/llvm/test/tools/obj2yaml/linker-options.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/obj2yaml/linker-options.yaml @@ -0,0 +1,69 @@ +## Check how obj2yaml produces SHT_LLVM_LINKER_OPTIONS section descriptions. + +## Check we dump valid sections using pairs of "Name" and "Value" strings. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=VALID + +# VALID: - Name: .linker-options-valid1 +# VALID-NEXT: Type: SHT_LLVM_LINKER_OPTIONS +# VALID-NEXT: Options: +# VALID-NEXT: - Name: a +# VALID-NEXT: Value: b +# VALID-NEXT: - Name: .linker-options-valid2 +# VALID-NEXT: Type: SHT_LLVM_LINKER_OPTIONS +# VALID-NEXT: Options: +# VALID-NEXT: - Name: a +# VALID-NEXT: Value: b +# VALID-NEXT: - Name: c +# VALID-NEXT: Value: d + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .linker-options-valid1 + Type: SHT_LLVM_LINKER_OPTIONS + Content: "61006200" + - Name: .linker-options-valid2 + Type: SHT_LLVM_LINKER_OPTIONS + Content: "6100620063006400" + +## Check we dump corrupt sections using the "Content" key. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=CORRUPT + +# CORRUPT: - Name: .linker-options-empty +# CORRUPT-NEXT: Type: SHT_LLVM_LINKER_OPTIONS +# CORRUPT-NEXT: Content: '' +# CORRUPT-NEXT: - Name: .linker-options-no-null +# CORRUPT-NEXT: Type: SHT_LLVM_LINKER_OPTIONS +# CORRUPT-NEXT: Content: '610062' +# CORRUPT-NEXT: - Name: .linker-options-incomplete +# CORRUPT-NEXT: Type: SHT_LLVM_LINKER_OPTIONS +# CORRUPT-NEXT: Content: '6100' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: +## 1) Empty content. + - Name: .linker-options-empty + Type: SHT_LLVM_LINKER_OPTIONS + Content: "" +## 2) Non-null terminated content. + - Name: .linker-options-no-null + Type: SHT_LLVM_LINKER_OPTIONS + Content: "610062" +## 3) Odd number of strings in the section. +## (Hence it contains an incomplete key-value pair). + - Name: .linker-options-incomplete + Type: SHT_LLVM_LINKER_OPTIONS + Content: "6100" diff --git a/llvm/test/tools/yaml2obj/linker-options.yaml b/llvm/test/tools/yaml2obj/linker-options.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/yaml2obj/linker-options.yaml @@ -0,0 +1,120 @@ +## Check we are able to produce a valid SHT_LLVM_LINKER_OPTIONS +## section from its description. + +## Check we can use either "Options" or "Content" to describe the data. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readobj --string-dump .linker-options1 --sections --section-data %t1 \ +# RUN: | FileCheck %s --check-prefix=OPTIONS + +# OPTIONS: Name: .linker-options1 +# OPTIONS-NEXT: Type: SHT_LLVM_LINKER_OPTIONS +# OPTIONS-NEXT: Flags [ +# OPTIONS-NEXT: ] +# OPTIONS-NEXT: Address: 0x0 +# OPTIONS-NEXT: Offset: 0x40 +# OPTIONS-NEXT: Size: 34 +# OPTIONS-NEXT: Link: 0 +# OPTIONS-NEXT: Info: 0 +# OPTIONS-NEXT: AddressAlignment: 0 +# OPTIONS-NEXT: EntrySize: 0 + +# OPTIONS: Name: .linker-options2 +# OPTIONS-NEXT: Type: SHT_LLVM_LINKER_OPTIONS +# OPTIONS: SectionData ( +# OPTIONS-NEXT: 0000: 00112233 | +# OPTIONS-NEXT: ) + +# OPTIONS: String dump of section '.linker-options1': +# OPTIONS-NEXT: [ 0] option 0 +# OPTIONS-NEXT: [ 9] value 0 +# OPTIONS-NEXT: [ 11] option 1 +# OPTIONS-NEXT: [ 1a] value 1 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .linker-options1 + Type: SHT_LLVM_LINKER_OPTIONS + Options: + - Name: option 0 + Value: value 0 + - Name: option 1 + Value: value 1 + - Name: .linker-options2 + Type: SHT_LLVM_LINKER_OPTIONS + Content: "00112233" + +## Check that "Value" and "Name" fields are mandatory when using "Options" key. + +# RUN: not yaml2obj --docnum=2 %s 2>&1 | FileCheck %s --check-prefix=NOVALUE +# RUN: not yaml2obj --docnum=3 %s 2>&1 | FileCheck %s --check-prefix=NONAME + +# NOVALUE: error: missing required key 'Value' +# NONAME: error: missing required key 'Name' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .linker-options + Type: SHT_LLVM_LINKER_OPTIONS + Options: + - Name: name + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .linker-options + Type: SHT_LLVM_LINKER_OPTIONS + Options: + - Value: value + +## Check we can't use both "Options" and "Content" together. + +# RUN: not yaml2obj %s --docnum=4 2>&1 | FileCheck %s --check-prefix=BOTH + +# BOTH: error: "Options" and "Content" can't be used together + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .linker-options + Type: SHT_LLVM_LINKER_OPTIONS + Options: + - Name: name + Value: value + Content: "00112233" + +## Check we can omit both "Options" and "Content". This produces an empty section. + +# RUN: yaml2obj %s --docnum=5 2>&1 -o %t5 +# RUN: llvm-readelf --sections %t5 | FileCheck %s --check-prefix=NONE + +# NONE: [Nr] Name Type Address Off Size +# NONE: [ 1] .linker-options LLVM_LINKER_OPTIONS 0000000000000000 000040 000000 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .linker-options + Type: SHT_LLVM_LINKER_OPTIONS diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -60,6 +60,8 @@ ELFYAML::Relocation &R); Expected dumpAddrsigSection(const Elf_Shdr *Shdr); + Expected + dumpLinkerOptionsSection(const Elf_Shdr *Shdr); Expected dumpDynamicSection(const Elf_Shdr *Shdr); Expected dumpRelocSection(const Elf_Shdr *Shdr); Expected @@ -315,6 +317,14 @@ Y->Chunks.emplace_back(*SecOrErr); break; } + case ELF::SHT_LLVM_LINKER_OPTIONS: { + Expected SecOrErr = + dumpLinkerOptionsSection(&Sec); + if (!SecOrErr) + return SecOrErr.takeError(); + Y->Chunks.emplace_back(*SecOrErr); + break; + } case ELF::SHT_NULL: { // We only dump the SHT_NULL section at index 0 when it // has at least one non-null field, because yaml2obj @@ -589,6 +599,37 @@ } template +Expected +ELFDumper::dumpLinkerOptionsSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + ArrayRef Content = *ContentOrErr; + if (Content.empty() || Content.back() != 0) { + S->Content = Content; + return S.release(); + } + + SmallVector Strings; + toStringRef(Content.drop_back()).split(Strings, '\0'); + if (Strings.size() % 2 != 0) { + S->Content = Content; + return S.release(); + } + + S->Options.emplace(); + for (size_t I = 0, E = Strings.size(); I != E; I += 2) + S->Options->push_back({Strings[I], Strings[I + 1]}); + + return S.release(); +} + +template Expected ELFDumper::dumpDynamicSection(const Elf_Shdr *Shdr) { auto S = std::make_unique();