Index: llvm/include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/ELFYAML.h +++ llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -38,6 +38,7 @@ // class's with appropriate fixed underlying type. LLVM_YAML_STRONG_TYPEDEF(uint16_t, ELF_ET) LLVM_YAML_STRONG_TYPEDEF(uint32_t, ELF_PT) +LLVM_YAML_STRONG_TYPEDEF(int64_t, ELF_DT) LLVM_YAML_STRONG_TYPEDEF(uint32_t, ELF_EM) LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_ELFCLASS) LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_ELFDATA) @@ -88,6 +89,11 @@ std::vector Sections; }; +struct DynamicEntry { + ELF_DT Tag; + StringRef Val; +}; + struct Symbol { StringRef Name; ELF_STT Type; @@ -113,6 +119,7 @@ Group, RawContent, Relocation, + DynamicSection, NoBits, MipsABIFlags }; @@ -129,17 +136,29 @@ Section(SectionKind Kind) : Kind(Kind) {} virtual ~Section(); }; + struct RawContentSection : Section { yaml::BinaryRef Content; llvm::yaml::Hex64 Size; RawContentSection() : Section(SectionKind::RawContent) {} + RawContentSection(SectionKind Kind) : Section(Kind) {} static bool classof(const Section *S) { return S->Kind == SectionKind::RawContent; } }; +struct DynamicSection : RawContentSection { + std::vector DynamicEntries; + + DynamicSection() : RawContentSection(SectionKind::DynamicSection) {} + + static bool classof(const Section *S) { + return S->Kind == SectionKind::DynamicSection; + } +}; + struct NoBitsSection : Section { llvm::yaml::Hex64 Size; @@ -216,6 +235,7 @@ } // end namespace llvm LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::DynamicEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::Symbol) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::Relocation) @@ -234,6 +254,11 @@ static void enumeration(IO &IO, ELFYAML::ELF_PT &Value); }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, ELFYAML::ELF_DT &Value); +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, ELFYAML::ELF_EM &Value); @@ -341,6 +366,11 @@ static void mapping(IO &IO, ELFYAML::ProgramHeader &FileHdr); }; +template <> +struct MappingTraits { + static void mapping(IO &IO, ELFYAML::DynamicEntry &Entry); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::Symbol &Symbol); Index: llvm/lib/ObjectYAML/ELFYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFYAML.cpp +++ llvm/lib/ObjectYAML/ELFYAML.cpp @@ -55,6 +55,121 @@ IO.enumFallback(Value); } +void ScalarEnumerationTraits::enumeration( + IO &IO, ELFYAML::ELF_DT &Value) { +#define ECase(X) IO.enumCase(Value, #X, ELF::X) + ECase(DT_NULL); + ECase(DT_NEEDED); + ECase(DT_PLTRELSZ); + ECase(DT_PLTGOT); + ECase(DT_HASH); + ECase(DT_STRTAB); + ECase(DT_SYMTAB); + ECase(DT_RELA); + ECase(DT_RELASZ); + ECase(DT_RELAENT); + ECase(DT_STRSZ); + ECase(DT_SYMENT); + ECase(DT_INIT); + ECase(DT_FINI); + ECase(DT_SONAME); + ECase(DT_RPATH); + ECase(DT_SYMBOLIC); + ECase(DT_REL); + ECase(DT_RELSZ); + ECase(DT_RELENT); + ECase(DT_PLTREL); + ECase(DT_DEBUG); + ECase(DT_TEXTREL); + ECase(DT_JMPREL); + ECase(DT_BIND_NOW); + ECase(DT_INIT_ARRAY); + ECase(DT_FINI_ARRAY); + ECase(DT_INIT_ARRAYSZ); + ECase(DT_FINI_ARRAYSZ); + ECase(DT_RUNPATH); + ECase(DT_FLAGS); + ECase(DT_ENCODING); + ECase(DT_PREINIT_ARRAY); + ECase(DT_PREINIT_ARRAYSZ); + ECase(DT_SYMTAB_SHNDX); + ECase(DT_RELRSZ); + ECase(DT_RELR); + ECase(DT_RELRENT); + ECase(DT_ANDROID_REL); + ECase(DT_ANDROID_RELSZ); + ECase(DT_ANDROID_RELA); + ECase(DT_ANDROID_RELASZ); + ECase(DT_ANDROID_RELR); + ECase(DT_ANDROID_RELRSZ); + ECase(DT_ANDROID_RELRENT); + ECase(DT_GNU_HASH); + ECase(DT_TLSDESC_PLT); + ECase(DT_TLSDESC_GOT); + ECase(DT_RELACOUNT); + ECase(DT_RELCOUNT); + ECase(DT_FLAGS_1); + ECase(DT_VERSYM); + ECase(DT_VERDEF); + ECase(DT_VERDEFNUM); + ECase(DT_VERNEED); + ECase(DT_VERNEEDNUM); + ECase(DT_HEXAGON_SYMSZ); + ECase(DT_HEXAGON_VER); + ECase(DT_HEXAGON_PLT); + ECase(DT_MIPS_RLD_VERSION); + ECase(DT_MIPS_TIME_STAMP); + ECase(DT_MIPS_ICHECKSUM); + ECase(DT_MIPS_IVERSION); + ECase(DT_MIPS_FLAGS); + ECase(DT_MIPS_BASE_ADDRESS); + ECase(DT_MIPS_MSYM); + ECase(DT_MIPS_CONFLICT); + ECase(DT_MIPS_LIBLIST); + ECase(DT_MIPS_LOCAL_GOTNO); + ECase(DT_MIPS_CONFLICTNO); + ECase(DT_MIPS_LIBLISTNO); + ECase(DT_MIPS_SYMTABNO); + ECase(DT_MIPS_UNREFEXTNO); + ECase(DT_MIPS_GOTSYM); + ECase(DT_MIPS_HIPAGENO); + ECase(DT_MIPS_RLD_MAP); + ECase(DT_MIPS_DELTA_CLASS); + ECase(DT_MIPS_DELTA_CLASS_NO); + ECase(DT_MIPS_DELTA_INSTANCE); + ECase(DT_MIPS_DELTA_INSTANCE_NO); + ECase(DT_MIPS_DELTA_RELOC); + ECase(DT_MIPS_DELTA_RELOC_NO); + ECase(DT_MIPS_DELTA_SYM); + ECase(DT_MIPS_DELTA_SYM_NO); + ECase(DT_MIPS_DELTA_CLASSSYM); + ECase(DT_MIPS_DELTA_CLASSSYM_NO); + ECase(DT_MIPS_CXX_FLAGS); + ECase(DT_MIPS_PIXIE_INIT); + ECase(DT_MIPS_SYMBOL_LIB); + ECase(DT_MIPS_LOCALPAGE_GOTIDX); + ECase(DT_MIPS_LOCAL_GOTIDX); + ECase(DT_MIPS_HIDDEN_GOTIDX); + ECase(DT_MIPS_PROTECTED_GOTIDX); + ECase(DT_MIPS_OPTIONS); + ECase(DT_MIPS_INTERFACE); + ECase(DT_MIPS_DYNSTR_ALIGN); + ECase(DT_MIPS_INTERFACE_SIZE); + ECase(DT_MIPS_RLD_TEXT_RESOLVE_ADDR); + ECase(DT_MIPS_PERF_SUFFIX); + ECase(DT_MIPS_COMPACT_SIZE); + ECase(DT_MIPS_GP_VALUE); + ECase(DT_MIPS_AUX_DYNAMIC); + ECase(DT_MIPS_PLTGOT); + ECase(DT_MIPS_RWPLT); + ECase(DT_MIPS_RLD_MAP_REL); + ECase(DT_PPC64_GLINK); + // Sun machine-independent extensions. + ECase(DT_AUXILIARY); + ECase(DT_FILTER); +#undef ECase +} + void ScalarEnumerationTraits::enumeration( IO &IO, ELFYAML::ELF_EM &Value) { #define ECase(X) IO.enumCase(Value, #X, ELF::X) @@ -770,6 +885,12 @@ IO.mapOptional("Align", Phdr.Align); } +void MappingTraits::mapping( + IO &IO, ELFYAML::DynamicEntry &Entry) { + IO.mapRequired("Tag", Entry.Tag); + IO.mapRequired("Value", Entry.Val); +} + namespace { struct NormalizedOther { @@ -837,6 +958,12 @@ IO.mapOptional("Size", Section.Size, Hex64(Section.Content.binary_size())); } +static void sectionMapping(IO &IO, ELFYAML::DynamicSection &Section) { + commonSectionMapping(IO, Section); + sectionMapping(IO, (ELFYAML::RawContentSection &)Section); + IO.mapOptional("Entries", Section.DynamicEntries); +} + static void sectionMapping(IO &IO, ELFYAML::NoBitsSection &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Size", Section.Size, Hex64(0)); @@ -897,6 +1024,11 @@ Section.reset(new ELFYAML::RelocationSection()); sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_DYNAMIC: + if (!IO.outputting()) + Section.reset(new ELFYAML::DynamicSection()); + sectionMapping(IO, *cast(Section.get())); + break; case ELF::SHT_GROUP: if (!IO.outputting()) Section.reset(new ELFYAML::Group()); Index: llvm/test/tools/yaml2obj/dynamic-dtstrtab-inconsistent.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-dtstrtab-inconsistent.yaml @@ -0,0 +1,28 @@ +# RUN: not yaml2obj %s -o %t 2>&1 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .somestrings + Type: SHT_STRTAB + Flags: [ SHF_ALLOC, SHF_WRITE ] + Content: "00" + Address: 0x1000 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Link: .somestrings + Entries: + # This entry conflicts with the previously specified `Link` key. + - Tag: DT_STRTAB + Value: .dynstr + +# CHECK: --- +# CHECK-NEXT: Tag: DT_STRTAB +# CHECK-NEXT: Value: .dynstr +# CHECK-NEXT: ... +# CHECK-NEXT: error: value and linked string table are inconsistent Index: llvm/test/tools/yaml2obj/dynamic-dtstrtab-invalid.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-dtstrtab-invalid.yaml @@ -0,0 +1,28 @@ +# RUN: not yaml2obj %s -o %t 2>&1 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .somestrings + Type: SHT_STRTAB + Flags: [ SHF_ALLOC, SHF_WRITE ] + Content: "00" + Address: 0x1000 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Link: .somestrings + Entries: + # This isn't a declared section. + - Tag: DT_STRTAB + Value: .notasection + +# CHECK: --- +# CHECK-NEXT: Tag: DT_STRTAB +# CHECK-NEXT: Value: .notasection +# CHECK-NEXT: ... +# CHECK-NEXT: error: value is not an address or valid section name Index: llvm/test/tools/yaml2obj/dynamic-entries.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-entries.yaml @@ -0,0 +1,40 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=SECTION +# RUN: llvm-readobj -dynamic %t | FileCheck %s --check-prefix=DYNAMIC + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Entries: + - Tag: DT_FLAGS + Value: 0xa + - Tag: DT_DEBUG + Value: 0x1000 +# llvm-readobj requires some program headers to read .dynamic entries. +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x0000 + PAddr: 0x0000 + Align: 8 + Sections: + - Section: .dynamic + - Section: .dynstr + - Type: PT_DYNAMIC + Flags: [ PF_X, PF_R ] + VAddr: 0x0000 + PAddr: 0x0000 + Sections: + - Section: .dynamic + +# SECTION: Name: .dynamic +# SECTION: EntrySize: 16 +# DYNAMIC: FLAGS SYMBOLIC BIND_NOW +# DYNAMIC: DEBUG 0x1000{{$}} Index: llvm/test/tools/yaml2obj/dynamic-link-str-offset.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-link-str-offset.yaml @@ -0,0 +1,54 @@ +# This test links .dynamic to .somestrings and ensures that DT_SONAME is still +# correct. +# RUN: yaml2obj %s -o %t +# RUN: llvm-readobj -dynamic %t | FileCheck %s --check-prefix=DYNAMIC + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .somestrings + Type: SHT_STRTAB + Flags: [ SHF_ALLOC, SHF_WRITE ] + # "\0libsomething.so\0" + Content: "006c6962736f6d657468696e672e736f00" + Address: 0x1000 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Link: .somestrings + Entries: + - Tag: DT_SONAME + Value: 1 + - Tag: DT_STRTAB + Value: 0x1000 +# llvm-readobj requires some program headers to read .dynamic entries. +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x0000 + PAddr: 0x0000 + Align: 8 + Sections: + - Section: .dynamic + - Section: .dynstr + - Type: PT_DYNAMIC + Flags: [ PF_X, PF_R ] + VAddr: 0x0000 + PAddr: 0x0000 + Sections: + - Section: .dynamic + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Align: 8 + Sections: + - Section: .somestrings + +# DYNAMIC: Library soname: [libsomething.so] +# DYNAMIC: STRTAB 0x1000{{$}} +# DYNAMIC: STRSZ 17 (bytes) Index: llvm/test/tools/yaml2obj/dynamic-link-str-req-offset.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-link-str-req-offset.yaml @@ -0,0 +1,26 @@ +# RUN: not yaml2obj %s -o %t 2>&1 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .somestrings + Type: SHT_STRTAB + Flags: [ SHF_ALLOC, SHF_WRITE ] + Content: "00" + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Link: .somestrings + Entries: + - Tag: DT_SONAME + Value: somelib.so + +# CHECK: --- +# CHECK-NEXT: Tag: DT_SONAME +# CHECK-NEXT: Value: somelib.so +# CHECK-NEXT: ... +# CHECK-NEXT: error: must be a numerical offset since .dynamic is not linked to .dynstr Index: llvm/test/tools/yaml2obj/dynamic-soname.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-soname.yaml @@ -0,0 +1,40 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=SECTION +# RUN: llvm-readobj -dynamic %t | FileCheck %s --check-prefix=DYNAMIC + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Entries: + - Tag: DT_SONAME + Value: libsomething.so + - Tag: DT_DEBUG + Value: 0x1000 +# llvm-readobj requires some program headers to read .dynamic entries. +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x0000 + PAddr: 0x0000 + Align: 8 + Sections: + - Section: .dynamic + - Section: .dynstr + - Type: PT_DYNAMIC + Flags: [ PF_X, PF_R ] + VAddr: 0x0000 + PAddr: 0x0000 + Sections: + - Section: .dynamic + +# SECTION: Name: .dynamic +# SECTION: EntrySize: 16 +# DYNAMIC: Library soname: [libsomething.so] +# DYNAMIC: DEBUG 0x1000{{$}} Index: llvm/test/tools/yaml2obj/dynamic-strsz-gt.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-strsz-gt.yaml @@ -0,0 +1,30 @@ +# This test checks to ensure that an error occurs if the DT_STRSZ entry +# value is greater than the size of the linked section. +# RUN: not yaml2obj %s -o %t 2>&1 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .somestrings + Type: SHT_STRTAB + Flags: [ SHF_ALLOC, SHF_WRITE ] + # "\0libsomething.so\0" + Content: "006c6962736f6d657468696e672e736f00" + Address: 0x1000 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Link: .somestrings + Entries: + - Tag: DT_STRSZ + Value: 24 + +# CHECK: --- +# CHECK-NEXT: Tag: DT_STRSZ +# CHECK-NEXT: Value: '24' +# CHECK-NEXT: ... +# CHECK-NEXT: error: value is larger than linked string table size Index: llvm/test/tools/yaml2obj/dynamic-strsz-invalid.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-strsz-invalid.yaml @@ -0,0 +1,30 @@ +# This test checks to ensure that an error occurs if the DT_STRSZ entry +# value is greater than the size of the linked section. +# RUN: not yaml2obj %s -o %t 2>&1 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .somestrings + Type: SHT_STRTAB + Flags: [ SHF_ALLOC, SHF_WRITE ] + # "\0libsomething.so\0" + Content: "006c6962736f6d657468696e672e736f00" + Address: 0x1000 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Link: .somestrings + Entries: + - Tag: DT_STRSZ + Value: char + +# CHECK: --- +# CHECK-NEXT: Tag: DT_STRSZ +# CHECK-NEXT: Value: char +# CHECK-NEXT: ... +# CHECK-NEXT: error: value is not a number Index: llvm/test/tools/yaml2obj/dynamic-strsz-lt.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/dynamic-strsz-lt.yaml @@ -0,0 +1,51 @@ +# This test checks to ensure that a warning is emitted if the DT_STRSZ entry +# value is less than the size of the linked section. +# RUN: yaml2obj %s -o %t 2>&1 | FileCheck %s --check-prefix=WARNING +# RUN: llvm-readobj -dynamic %t | FileCheck %s --check-prefix=DYNAMIC + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .somestrings + Type: SHT_STRTAB + Flags: [ SHF_ALLOC, SHF_WRITE ] + # "\0libsomething.so\0" + Content: "006c6962736f6d657468696e672e736f00" + Address: 0x1000 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC, SHF_WRITE ] + Link: .somestrings + Entries: + - Tag: DT_STRSZ + Value: 5 +# llvm-readobj requires some program headers to read .dynamic entries. +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x0000 + PAddr: 0x0000 + Align: 8 + Sections: + - Section: .dynamic + - Section: .dynstr + - Type: PT_DYNAMIC + Flags: [ PF_X, PF_R ] + VAddr: 0x0000 + PAddr: 0x0000 + Sections: + - Section: .dynamic + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Align: 8 + Sections: + - Section: .somestrings +# WARNING: warning: DT_STRSZ value is less than linked string table size +# DYNAMIC: STRSZ 5 (bytes) +# DYNAMIC: STRTAB 0x1000{{$}} Index: llvm/tools/yaml2obj/yaml2elf.cpp =================================================================== --- llvm/tools/yaml2obj/yaml2elf.cpp +++ llvm/tools/yaml2obj/yaml2elf.cpp @@ -14,6 +14,7 @@ #include "yaml2obj.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELFObjectFile.h" @@ -102,6 +103,20 @@ memset(&Obj, 0, sizeof(Obj)); } +/// This function uses YAML to output a guilty object before printing an error. +/// Using this approach provides more context than a simple assert can while +/// also allowing more graceful termination. +/// +/// @param YamlObj An object with YAML traits that triggered this error. +/// @param After The error message to print after the YAML object. +/* +template bool printYamlError(T YamlObj, StringRef After) { + yaml::Output Yout(errs()); + Yout << YamlObj; + WithColor::error() << After << "\n"; + return false; +} +*/ namespace { /// "Single point of truth" for the ELF file construction. /// TODO: This class still has a ways to go before it is truly a "single @@ -128,6 +143,12 @@ /// The future ".dynstr" section. StringTableBuilder DotDynstr{StringTableBuilder::ELF}; + /// Retain a pointer to the ELFYAML::DynamicSection as state. + /// This is required as the .dynamic section can't be written until after the + /// .dynstr section. The contents of .dynamic depend on the final contents of + /// .dynstr. + ELFYAML::DynamicSection *YamlDynamicSection = nullptr; + NameToIdxMap SN2I; NameToIdxMap SymN2I; const ELFYAML::Object &Doc; @@ -139,11 +160,16 @@ void initProgramHeaders(std::vector &PHeaders); bool initSectionHeaders(std::vector &SHeaders, ContiguousBlobAccumulator &CBA); - void initSymtabSectionHeader(Elf_Shdr &SHeader, SymtabType STType, - ContiguousBlobAccumulator &CBA); + void initSymtabSectionHeader(Elf_Shdr &SHeader, SymtabType STType); + void finalizeSymtabSection(Elf_Shdr &SHeader, SymtabType STType, + ContiguousBlobAccumulator &CBA); void initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name, StringTableBuilder &STB, ContiguousBlobAccumulator &CBA); + void initDynamicSectionHeader(Elf_Shdr &SHeader); + bool finalizeDynamicSection(Elf_Shdr &DynamicHdr, + std::vector &AllShdrs, + ContiguousBlobAccumulator &CBA); void setProgramHeaderLayout(std::vector &PHeaders, std::vector &SHeaders); void addSymbols(const std::vector &Symbols, @@ -161,6 +187,7 @@ const ELFYAML::MipsABIFlags &Section, ContiguousBlobAccumulator &CBA); bool hasDynamicSymbols() const; + bool hasDynamicEntries() const; SmallVector implicitSectionNames() const; // - SHT_NULL entry (placed first, i.e. 0'th entry) @@ -169,6 +196,7 @@ // - section header string table (.shstrtab) (defaults to after .strtab) // - dynamic symbol table (.dynsym) (defaults to after .shstrtab) // - dynamic string table (.dynstr) (defaults to after .dynsym) + // - dynamic section (.dynamic) (defaults to after .dynstr) unsigned getDotSymTabSecNo() const { return SN2I.get(".symtab"); } unsigned getDotStrTabSecNo() const { return SN2I.get(".strtab"); } unsigned getDotShStrTabSecNo() const { return SN2I.get(".shstrtab"); } @@ -273,6 +301,12 @@ SHeader.sh_info = Index; if (!writeSectionContent(SHeader, *S, CBA)) return false; + } else if (auto S = dyn_cast(Sec.get())) { + // Determine whether to use `Entries` or older raw `Contents`. + if (!S->DynamicEntries.empty()) + YamlDynamicSection = S; + else + writeSectionContent(SHeader, (ELFYAML::RawContentSection &)*S, CBA); } else if (auto S = dyn_cast(Sec.get())) { unsigned SymIdx; if (SymN2I.lookup(S->Info, SymIdx) && !to_integer(S->Info, SymIdx)) { @@ -302,9 +336,7 @@ template void ELFState::initSymtabSectionHeader(Elf_Shdr &SHeader, - SymtabType STType, - ContiguousBlobAccumulator &CBA) { - zero(SHeader); + SymtabType STType) { bool IsStatic = STType == SymtabType::Static; SHeader.sh_name = DotShStrtab.getOffset(IsStatic ? ".symtab" : ".dynsym"); SHeader.sh_type = IsStatic ? ELF::SHT_SYMTAB : ELF::SHT_DYNSYM; @@ -317,12 +349,11 @@ SHeader.sh_addralign = 8; std::vector Syms; - { - // Ensure STN_UNDEF is present - Elf_Sym Sym; - zero(Sym); - Syms.push_back(Sym); - } + + // Ensure STN_UNDEF is present + Elf_Sym Sym; + zero(Sym); + Syms.push_back(Sym); // Add symbol names to .strtab or .dynstr. for (const auto &Sym : Symbols.Local) @@ -331,7 +362,23 @@ Strtab.add(Sym.Name); for (const auto &Sym : Symbols.Weak) Strtab.add(Sym.Name); - Strtab.finalize(); +} + +template +void ELFState::finalizeSymtabSection(Elf_Shdr &SHeader, + SymtabType STType, + ContiguousBlobAccumulator &CBA) { + bool IsStatic = STType == SymtabType::Static; + const auto &Symbols = IsStatic ? Doc.Symbols : Doc.DynamicSymbols; + auto &Strtab = IsStatic ? DotStrtab : DotDynstr; + + std::vector Syms; + { + // Ensure STN_UNDEF is present + Elf_Sym Sym; + zero(Sym); + Syms.push_back(Sym); + } addSymbols(Symbols.Local, Syms, ELF::STB_LOCAL, Strtab); addSymbols(Symbols.Global, Syms, ELF::STB_GLOBAL, Strtab); @@ -347,7 +394,6 @@ void ELFState::initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name, StringTableBuilder &STB, ContiguousBlobAccumulator &CBA) { - zero(SHeader); SHeader.sh_name = DotShStrtab.getOffset(Name); SHeader.sh_type = ELF::SHT_STRTAB; STB.write(CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign)); @@ -355,6 +401,146 @@ SHeader.sh_addralign = 1; } +template +void ELFState::initDynamicSectionHeader(Elf_Shdr &SHeader) { + SHeader.sh_name = DotShStrtab.getOffset(".dynamic"); + SHeader.sh_type = ELF::SHT_DYNAMIC; + SHeader.sh_entsize = sizeof(Elf_Dyn); + SHeader.sh_addralign = 8; + // For .dynamic section set link to .dynstr by default. + if (YamlDynamicSection->Link.empty()) + SHeader.sh_link = getDotDynStrSecNo(); + + // If not linked to .dynstr, this is done. + if (SHeader.sh_link != getDotDynStrSecNo()) + return; + + // If linked to .dynstr, DT_* entries whose values refer to offsets in + // .dynstr table may be populated with strings. + for (const auto &YamlEntry : YamlDynamicSection->DynamicEntries) { + switch (YamlEntry.Tag) { + case ELF::DT_NEEDED: + case ELF::DT_SONAME: + case ELF::DT_RPATH: + case ELF::DT_RUNPATH: + DotDynstr.add(YamlEntry.Val); + } + } +} + +template +bool ELFState::finalizeDynamicSection(Elf_Shdr &DynamicHdr, + std::vector &AllShdrs, + ContiguousBlobAccumulator &CBA) { + std::vector Dyn; + SmallSet FoundTags; + auto printYamlError = [](ELFYAML::DynamicEntry YamlObj, StringRef After) { + yaml::Output Yout(errs()); + Yout << YamlObj; + WithColor::error() << After << "\n"; + }; + for (const auto &YamlEntry : YamlDynamicSection->DynamicEntries) { + Elf_Dyn Ent; + Ent.d_tag = YamlEntry.Tag; + FoundTags.insert(Ent.d_tag); + uint64_t Value; + + // Handle .dynamic entries whose values represent offsets in .dynstr. + if (Ent.d_tag == ELF::DT_NEEDED || Ent.d_tag == ELF::DT_SONAME || + Ent.d_tag == ELF::DT_RPATH || Ent.d_tag == ELF::DT_RUNPATH) { + if (DynamicHdr.sh_link == getDotDynStrSecNo()) + Value = DotDynstr.getOffset(YamlEntry.Val); + else if (!to_integer(YamlEntry.Val, Value)) { + printYamlError(YamlEntry, "must be a numerical offset since " + ".dynamic is not linked to .dynstr"); + return false; + } + // Ensure correctness of DT_STRTAB entry. + } else if (Ent.d_tag == ELF::DT_STRTAB) { + // Validate that DT_STRTAB is either the name of the linked section, or it + // is the address of the linked section. + unsigned LinkedStrTab = DynamicHdr.sh_link; + // Case that DT_STRTAB value is an address. + if (to_integer(YamlEntry.Val, Value)) { + if (Value != AllShdrs[LinkedStrTab].sh_addr) { + printYamlError( + YamlEntry, + "value and linked string table address are inconsistent"); + return false; + } + // Case that DT_STRTAB is the name of a section. + } else if (!SN2I.lookup(YamlEntry.Val, LinkedStrTab)) { + if (LinkedStrTab != DynamicHdr.sh_link) { + printYamlError(YamlEntry, + "value and linked string table are inconsistent"); + return false; + } + Value = AllShdrs[LinkedStrTab].sh_addr; + // Neither a valid address nor a valid section name. + } else { + printYamlError(YamlEntry, + "value is not an address or valid section name"); + return false; + } + // Ensure correctness of DT_STRSZ entry. + } else if (Ent.d_tag == ELF::DT_STRSZ) { + unsigned LinkedStrTab = DynamicHdr.sh_link; + if (!to_integer(YamlEntry.Val, Value)) { + printYamlError(YamlEntry, "value is not a number"); + return false; + } + if (Value < AllShdrs[LinkedStrTab].sh_size) { + WithColor::warning() + << "DT_STRSZ value is less than linked string table size\n"; + } else if (Value > AllShdrs[LinkedStrTab].sh_size) { + printYamlError(YamlEntry, + "value is larger than linked string table size"); + return false; + } + // General case for unspecialized dynamic entries. + } else { + unsigned SectionIdx; + // If a section name, use that section's sh_addr. + if (!SN2I.lookup(YamlEntry.Val, SectionIdx)) { + Value = AllShdrs[SectionIdx].sh_addr; + // Otherwise, attempt to treat as a number. + } else if (!to_integer(YamlEntry.Val, Value)) { + printYamlError(YamlEntry, + "value is not a number or valid section name"); + return false; + } + } + Ent.d_un.d_val = Value; + Dyn.push_back(Ent); + } + // Automatically add missing dynamic entries that can be inferred. + if (!FoundTags.count(ELF::DT_STRTAB)) { + unsigned LinkedStrTab = DynamicHdr.sh_link; + Elf_Dyn DTStrTab; + DTStrTab.d_tag = ELF::DT_STRTAB; + DTStrTab.d_un.d_ptr = AllShdrs[LinkedStrTab].sh_addr; + Dyn.push_back(DTStrTab); + } + if (!FoundTags.count(ELF::DT_STRSZ)) { + unsigned LinkedStrTab = DynamicHdr.sh_link; + Elf_Dyn DTStrTabSz; + DTStrTabSz.d_tag = ELF::DT_STRSZ; + DTStrTabSz.d_un.d_val = AllShdrs[LinkedStrTab].sh_size; + Dyn.push_back(DTStrTabSz); + } + // Ensure DT_NULL is present. + Elf_Dyn NullEnt; + NullEnt.d_tag = ELF::DT_NULL; + NullEnt.d_un.d_val = 0; + Dyn.push_back(NullEnt); + + writeArrayData( + CBA.getOSAndAlignedOffset(DynamicHdr.sh_offset, DynamicHdr.sh_addralign), + makeArrayRef(Dyn)); + DynamicHdr.sh_size = arrayDataSize(makeArrayRef(Dyn)); + return true; +} + template void ELFState::setProgramHeaderLayout(std::vector &PHeaders, std::vector &SHeaders) { @@ -653,20 +839,51 @@ if (State.SN2I.get(Name) >= SHeaders.size()) SHeaders.push_back({}); - // Initialize the implicit sections + // Initialize and write .symtab symbol table and section header. auto Index = State.SN2I.get(".symtab"); - State.initSymtabSectionHeader(SHeaders[Index], SymtabType::Static, CBA); + State.initSymtabSectionHeader(SHeaders[Index], SymtabType::Static); + State.DotStrtab.finalize(); + State.finalizeSymtabSection(SHeaders[Index], SymtabType::Static, CBA); Index = State.SN2I.get(".strtab"); State.initStrtabSectionHeader(SHeaders[Index], ".strtab", State.DotStrtab, CBA); Index = State.SN2I.get(".shstrtab"); State.initStrtabSectionHeader(SHeaders[Index], ".shstrtab", State.DotShStrtab, CBA); + + // Populate DotDynstr by initializing both .dynsym and .dynamic sections. + // Initialize .dynsym section. if (State.hasDynamicSymbols()) { Index = State.SN2I.get(".dynsym"); - State.initSymtabSectionHeader(SHeaders[Index], SymtabType::Dynamic, CBA); + State.initSymtabSectionHeader(SHeaders[Index], SymtabType::Dynamic); SHeaders[Index].sh_flags |= ELF::SHF_ALLOC; + } + // Initialize .dynamic section. + if (State.hasDynamicEntries()) { + Index = State.SN2I.get(".dynamic"); + State.initDynamicSectionHeader(SHeaders[Index]); + SHeaders[Index].sh_flags |= ELF::SHF_ALLOC; + } + // Finalize and write .dynstr, .dynsym, and .dynamic. + if (State.hasDynamicSymbols() || State.hasDynamicEntries()) { + State.DotDynstr.finalize(); + + // Write .dynsym section header and contents. + if (State.hasDynamicSymbols()) { + Index = State.SN2I.get(".dynsym"); + State.finalizeSymtabSection(SHeaders[Index], SymtabType::Dynamic, CBA); + } + + // Write .dynstr section header and contents. Index = State.SN2I.get(".dynstr"); - State.initStrtabSectionHeader(SHeaders[Index], ".dynstr", State.DotDynstr, CBA); + State.initStrtabSectionHeader(SHeaders[Index], ".dynstr", State.DotDynstr, + CBA); SHeaders[Index].sh_flags |= ELF::SHF_ALLOC; + + // Write .dynamic section header and contents. + if (State.hasDynamicEntries()) { + Index = State.SN2I.get(".dynamic"); + if (!State.finalizeDynamicSection(SHeaders[Index], SHeaders, CBA)) + return 1; + } } // Now we can decide segment offsets @@ -685,11 +902,22 @@ Doc.DynamicSymbols.Local.size() > 0; } +template bool ELFState::hasDynamicEntries() const { + for (const auto &Sec : Doc.Sections) { + if (auto S = dyn_cast(Sec.get())) { + return !S->DynamicEntries.empty(); + } + } + return false; +} + template SmallVector ELFState::implicitSectionNames() const { - if (!hasDynamicSymbols()) - return {".symtab", ".strtab", ".shstrtab"}; - return {".symtab", ".strtab", ".shstrtab", ".dynsym", ".dynstr"}; + if (hasDynamicSymbols()) + return {".symtab", ".strtab", ".shstrtab", ".dynsym", ".dynstr"}; + if (hasDynamicEntries()) + return {".symtab", ".strtab", ".shstrtab", ".dynstr", ".dynamic"}; + return {".symtab", ".strtab", ".shstrtab"}; } static bool is64Bit(const ELFYAML::Object &Doc) {