Index: llvm/include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/ELFYAML.h +++ llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -153,6 +153,7 @@ StackSizes, SymtabShndxSection, Symver, + ARMIndexTable, MipsABIFlags, Addrsig, Fill, @@ -493,6 +494,23 @@ } }; +struct ARMIndexTableEntry { + llvm::yaml::Hex32 Offset; + llvm::yaml::Hex32 Value; +}; + +struct ARMIndexTableSection : Section { + Optional> Entries; + Optional Content; + Optional Size; + + ARMIndexTableSection() : Section(ChunkKind::ARMIndexTable) {} + + static bool classof(const Chunk *S) { + return S->Kind == ChunkKind::ARMIndexTable; + } +}; + // Represents .MIPS.abiflags section struct MipsABIFlags : Section { llvm::yaml::Hex16 Version; @@ -575,6 +593,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::Relocation) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::SectionOrType) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::SectionName) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ARMIndexTableEntry) namespace llvm { namespace yaml { @@ -757,6 +776,10 @@ static void mapping(IO &IO, ELFYAML::Relocation &Rel); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::ARMIndexTableEntry &E); +}; + template <> struct MappingTraits> { static void mapping(IO &IO, std::unique_ptr &C); static StringRef validate(IO &io, std::unique_ptr &C); Index: llvm/lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFEmitter.cpp +++ llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -259,6 +259,9 @@ void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::VerdefSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::ARMIndexTableSection &Section, + ContiguousBlobAccumulator &CBA); void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::MipsABIFlags &Section, ContiguousBlobAccumulator &CBA); @@ -696,6 +699,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)) { @@ -1514,6 +1519,25 @@ AuxCnt * sizeof(Elf_Vernaux); } +template +void ELFState::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::ARMIndexTableSection &Section, + ContiguousBlobAccumulator &CBA) { + if (Section.Content || Section.Size) { + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); + return; + } + + if (!Section.Entries) + return; + + for (const ELFYAML::ARMIndexTableEntry &E : *Section.Entries) { + CBA.write(E.Offset, ELFT::TargetEndianness); + CBA.write(E.Value, ELFT::TargetEndianness); + } + SHeader.sh_size = Section.Entries->size() * 8; +} + template void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::MipsABIFlags &Section, Index: llvm/lib/ObjectYAML/ELFYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFYAML.cpp +++ llvm/lib/ObjectYAML/ELFYAML.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/ARMEHABI.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MipsABIFlags.h" @@ -1266,6 +1267,13 @@ IO.mapRequired("Section", sectionName.Section); } +static void sectionMapping(IO &IO, ELFYAML::ARMIndexTableSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Size", Section.Size); + IO.mapOptional("Entries", Section.Entries); +} + static void sectionMapping(IO &IO, ELFYAML::MipsABIFlags &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Version", Section.Version, Hex16(0)); @@ -1286,6 +1294,12 @@ IO.mapOptional("Flags2", Section.Flags2, Hex32(0)); } +static StringRef getStringValue(IO &IO, const char *Key) { + StringRef Val; + IO.mapRequired(Key, Val); + return Val; +} + void MappingTraits>::mapping( IO &IO, std::unique_ptr &Section) { ELFYAML::ELF_SHT Type; @@ -1295,9 +1309,7 @@ // When the Type string does not have a "SHT_" prefix, we know it is not a // description of a regular ELF output section. Currently, we have one // special type named "Fill". See comments for Fill. - StringRef StrType; - IO.mapRequired("Type", StrType); - if (StrType == "Fill") { + if (getStringValue(IO, "Type") == "Fill") { Section.reset(new ELFYAML::Fill()); fillMapping(IO, *cast(Section.get())); return; @@ -1306,6 +1318,14 @@ IO.mapRequired("Type", Type); } + const auto &Obj = *static_cast(IO.getContext()); + if (Obj.getMachine() == ELF::EM_ARM && Type == ELF::SHT_ARM_EXIDX) { + if (!IO.outputting()) + Section.reset(new ELFYAML::ARMIndexTableSection()); + sectionMapping(IO, *cast(Section.get())); + return; + } + switch (Type) { case ELF::SHT_DYNAMIC: if (!IO.outputting()) @@ -1576,6 +1596,21 @@ return {}; } + if (const auto *IT = dyn_cast(C.get())) { + if (IT->Content || IT->Size) { + if (IT->Size && IT->Content && + (uint64_t)*IT->Size < IT->Content->binary_size()) + return "\"Size\" must be greater than or equal to the content " + "size"; + + if (IT->Entries) + return "\"Entries\" cannot be used with \"Content\" or \"Size\""; + return {}; + } + + return {}; + } + return {}; } @@ -1688,6 +1723,20 @@ IO.mapOptional("Addend", Rel.Addend, (ELFYAML::YAMLIntUInt)0); } +void MappingTraits::mapping( + IO &IO, ELFYAML::ARMIndexTableEntry &E) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapRequired("Offset", E.Offset); + + StringRef CantUnwind = "EXIDX_CANTUNWIND"; + if (IO.outputting() && (uint32_t)E.Value == ARM::EHABI::EXIDX_CANTUNWIND) + IO.mapRequired("Value", CantUnwind); + else if (!IO.outputting() && getStringValue(IO, "Value") == CantUnwind) + E.Value = ARM::EHABI::EXIDX_CANTUNWIND; + else + IO.mapRequired("Value", E.Value); +} + void MappingTraits::mapping(IO &IO, ELFYAML::Object &Object) { assert(!IO.getContext() && "The IO context is initialized already"); IO.setContext(&Object); Index: llvm/test/tools/llvm-readobj/ELF/ARM/unwind-non-relocatable.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/ARM/unwind-non-relocatable.test +++ llvm/test/tools/llvm-readobj/ELF/ARM/unwind-non-relocatable.test @@ -65,20 +65,19 @@ - Name: .ARM.exidx Type: SHT_ARM_EXIDX Address: 0x24C - ContentArray: [ -## Entry 1. Address of .ARM.exidx (0x24C) + entry offset (0) + 0x7fffffe4 == 0x230 (func1). - 0xE4, 0xFF, 0xFF, 0x7F, ## Word(0) = 0x7fffffe4 (31 bit). - 0xB0, 0xB0, 0xB0, 0x80, ## Word(1): arbitrary opcodes. -## Entry 2. Address of .ARM.exidx (0x24C) + entry offset (8) + 0x7fffffe0 == 0x234 (func2). - 0xE0, 0xFF, 0xFF, 0x7F, ## Word(0) = 0x7fffffe0 (31 bit). - 0x80, 0x84, 0x9B, 0x80, ## Word(1): arbitrary opcodes. -## Entry 3. Address of .ARM.exidx (0x24C) + entry offset (16) + 0x7fffffec == 0x248 (func2). - 0xEC, 0xFF, 0xFF, 0x7F, ## Word(0) = 0x7fffffec (31 bit). - 0xB0, 0xB0, 0xB0, 0x80, ## Word(1): arbitrary opcodes. -## Entry 4. Address of .ARM.exidx (0x24C) + entry offset (24) + 0x7fffffe8 == 0x24C. - 0xE8, 0xFF, 0xFF, 0x7F, ## Word(0) = 0x7fffffe8 (31 bit). - 0x01, 0x00, 0x00, 0x00 ## Word(1) == EXIDX_CANTUNWIND - ] + Entries: +## Address of .ARM.exidx (0x24C) + entry offset (0) + 0x7fffffe4 (31 bit) == 0x230 (func1). + - Offset: 0x7FFFFFE4 + Value: 0x80B0B0B0 ## arbitrary opcodes. +## Address of .ARM.exidx (0x24C) + entry offset (8) + 0x7fffffe0 (31 bit) == 0x234 (func2). + - Offset: 0x7FFFFFE0 + Value: 0x809B8480 ## arbitrary opcodes. +## Address of .ARM.exidx (0x24C) + entry offset (16) + 0x7fffffec (31 bit) == 0x248 (func2). + - Offset: 0x7FFFFFEC + Value: 0x80B0B0B0 ## arbitrary opcodes. +## Address of .ARM.exidx (0x24C) + entry offset (24) + 0x7fffffe8 (31 bit) == 0x24C. + - Offset: 0x7FFFFFE8 + Value: EXIDX_CANTUNWIND Symbols: - Name: func1 Type: STT_FUNC Index: llvm/test/tools/obj2yaml/ELF/arm-exidx-section.yaml =================================================================== --- /dev/null +++ llvm/test/tools/obj2yaml/ELF/arm-exidx-section.yaml @@ -0,0 +1,107 @@ +## Check how obj2yaml dumps the SHT_ARM_EXIDX section. + +## For a valid section, obj2yaml emits the "Entries" key. +## This checks that we respect data endianness and recognize the +## EXIDX_CANTUNWIND (0x1) special value properly. + +# RUN: yaml2obj --docnum=1 -DENCODE=LSB %s -o %t.le.so +# RUN: obj2yaml %t.le.so | FileCheck %s --check-prefix=LE +# RUN: yaml2obj --docnum=1 -DENCODE=MSB %s -o %t.be.so +# RUN: obj2yaml %t.be.so | FileCheck %s --check-prefix=BE + +# LE: - Name: .ARM.exidx +# LE-NEXT: Type: SHT_ARM_EXIDX +# LE-NEXT: Entries: +# LE-NEXT: - Offset: 0xDDCCBBAA +# LE-NEXT: Value: 0x01000000 +# LE-NEXT: - Offset: 0x9988FFEE +# LE-NEXT: Value: EXIDX_CANTUNWIND +# LE-NEXT: ... + +# BE: - Name: .ARM.exidx +# BE-NEXT: Type: SHT_ARM_EXIDX +# BE-NEXT: Entries: +# BE-NEXT: - Offset: 0xAABBCCDD +# BE-NEXT: Value: EXIDX_CANTUNWIND +# BE-NEXT: - Offset: 0xEEFF8899 +# BE-NEXT: Value: 0x01000000 +# BE-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2[[ENCODE=LSB]] + Type: ET_DYN + Machine: EM_ARM +Sections: + - Name: .ARM.exidx + Type: SHT_ARM_EXIDX +## 4 words: , EXIDX_CANTUNWIND in big-endian, +## and EXIDX_CANTUNWIND in little-endian. + Content: "AABBCCDD00000001EEFF889901000000" + Size: [[SIZE=]] + +## Check that we dump the content of a truncated SHT_ARM_EXIDX section +## using the "Content" key. +# RUN: yaml2obj --docnum=1 -DSIZE=17 %s -o %t.invalid-size.so +# RUN: obj2yaml %t.invalid-size.so | FileCheck %s --check-prefix=INVALID-SIZE + +# INVALID-SIZE: - Name: .ARM.exidx +# INVALID-SIZE-NEXT: Type: SHT_ARM_EXIDX +# INVALID-SIZE-NEXT: Content: AABBCCDD00000001EEFF88990100000000 +# INVALID-SIZE-NEXT: ... + +## Check how we dump an empty SHT_ARM_EXIDX section. +# RUN: yaml2obj --docnum=2 %s -o %t.empty.so +# RUN: obj2yaml %t.empty.so | FileCheck %s --check-prefix=EMPTY + +# EMPTY: - Name: .ARM.exidx +# EMPTY-NEXT: Type: SHT_ARM_EXIDX +# EMPTY-NEXT: Entries: [] +# EMPTY-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_ARM +Sections: + - Name: .ARM.exidx + Type: SHT_ARM_EXIDX + +## Check how we dump the SHT_ARM_EXIDX (0x70000001) section when +## the machine type is not EM_ARM. It is dumped as a regular +## section of an unknown type. + +# RUN: yaml2obj --docnum=3 %s -o %t.not-arm.so +# RUN: obj2yaml %t.not-arm.so | FileCheck %s --check-prefix=NOT-ARM + +# RUN: yaml2obj --docnum=3 -DMACHINE=EM_ARM %s -o %t.arm.so +# RUN: obj2yaml %t.arm.so | FileCheck %s --check-prefix=ARM + +# NOT-ARM: Sections: +# NOT-ARM-NEXT: - Name: .ARM.exidx +# NOT-ARM-NEXT: Type: 0x70000001 +# NOT-ARM-NEXT: Content: AABBCCDD11223344 +# NOT-ARM-NEXT: ... + +# ARM: - Name: .ARM.exidx +# ARM-NEXT: Type: SHT_ARM_EXIDX +# ARM-NEXT: Entries: +# ARM-NEXT: - Offset: 0xDDCCBBAA +# ARM-NEXT: Value: 0x44332211 +# ARM-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: [[MACHINE=]] +Sections: + - Name: .ARM.exidx + Type: SHT_PROGBITS + ShType: 0x70000001 ## SHT_ARM_EXIDX +## An arbitrary valid content. + Content: "AABBCCDD11223344" Index: llvm/test/tools/yaml2obj/ELF/arm-exidx-section.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/ELF/arm-exidx-section.yaml @@ -0,0 +1,129 @@ +## Test how we create SHT_ARM_EXIDX sections. + +## Test that the content of SHT_ARM_EXIDX sections, +## generated for 32/64-bit little/big endian targets is correct. +## Also check that we can use a special EXIDX_CANTUNWIND (0x1) value for a Value field of an entry. +# RUN: yaml2obj --docnum=1 -DENCODE=LSB -DCLASS=64 %s -o %t.le64 +# RUN: llvm-readobj --sections --section-data %t.le64 | FileCheck %s --check-prefixes=DEFAULT,LE +# RUN: yaml2obj --docnum=1 -DENCODE=LSB -DCLASS=32 %s -o %t.le32 +# RUN: llvm-readobj --sections --section-data %t.le32 | FileCheck %s --check-prefixes=DEFAULT,LE +# RUN: yaml2obj --docnum=1 -DENCODE=MSB -DCLASS=64 %s -o %t.be64 +# RUN: llvm-readobj --sections --section-data %t.be64 | FileCheck %s --check-prefixes=DEFAULT,BE +# RUN: yaml2obj --docnum=1 -DENCODE=MSB -DCLASS=32 %s -o %t.be32 +# RUN: llvm-readobj --sections --section-data %t.be32 | FileCheck %s --check-prefixes=DEFAULT,BE + +# DEFAULT: Name: .ARM.exidx (1) +# DEFAULT-NEXT: Type: SHT_ARM_EXIDX (0x70000001) +# DEFAULT-NEXT: Flags [ (0x0) +# DEFAULT-NEXT: ] +# DEFAULT-NEXT: Address: 0x0 +# DEFAULT-NEXT: Offset: 0x{{.*}} +# DEFAULT-NEXT: Size: 16 +# DEFAULT-NEXT: Link: 0 +# DEFAULT-NEXT: Info: 0 +# DEFAULT-NEXT: AddressAlignment: 0 +# DEFAULT-NEXT: EntrySize: 0 +# DEFAULT-NEXT: SectionData ( +# LE-NEXT: 0000: DDCCBBAA 44332211 9988FFEE 01000000 | +# BE-NEXT: 0000: AABBCCDD 11223344 EEFF8899 00000001 | +# DEFAULT-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS[[CLASS=64]] + Data: ELFDATA2[[ENCODE=LSB]] + Type: ET_DYN + Machine: [[MACHINE=EM_ARM]] +Sections: + - Name: .ARM.exidx + Type: SHT_ARM_EXIDX + Entries: + - Offset: 0xAABBCCDD + Value: 0x11223344 + - Offset: 0xEEFF8899 + Value: EXIDX_CANTUNWIND + +## Check we only recognize the SHT_ARM_EXIDX section type when machine type is EM_ARM. +# RUN: not yaml2obj --docnum=1 -DMACHINE=EM_NONE %s 2>&1 | FileCheck %s --check-prefix=UNKNOWN + +# UNKNOWN: error: invalid hex32 number +# UNKNOWN-NEXT: Type: SHT_ARM_EXIDX + +## Check we can set arbitrary section properties for a SHT_ARM_EXIDX section. +## Also check that we are able to specify none of "Entries", "Content" nor "Size" keys. + +# RUN: yaml2obj --docnum=2 %s -o %t.props.o +# RUN: llvm-readelf --sections %t.props.o | FileCheck %s --check-prefix=PROPERTIES + +# PROPERTIES: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# PROPERTIES: [ 1] .ARM.exidx ARM_EXIDX 0000000000001122 000055 000000 00 AMS 13124 0 85 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM +Sections: + - Name: .ARM.exidx + Type: SHT_ARM_EXIDX + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + Address: 0x1122 + Link: 0x3344 + AddressAlign: 0x55 + EntSize: 0x66 + +## Check we can't use "Entries" key together with "Content" or "Size" keys. + +# RUN: not yaml2obj --docnum=3 -DSIZE=0 -DENT="[]" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ENTRIES-ERR +# RUN: not yaml2obj --docnum=3 -DCONTENT="'00'" -DENT="[]" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ENTRIES-ERR + +# ENTRIES-ERR: error: "Entries" cannot be used with "Content" or "Size" + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM +Sections: + - Name: .ARM.exidx + Type: SHT_ARM_EXIDX + Size: [[SIZE=]] + Content: [[CONTENT=]] + Entries: [[ENT=]] + +## Check we can use "Content" key with "Size" key when the size is greater +## than or equal to the content size. + +# RUN: not yaml2obj --docnum=3 -DCONTENT="'00'" -DSIZE=0 %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=CONTENT-SIZE-ERR + +# CONTENT-SIZE-ERR: error: "Size" must be greater than or equal to the content size + +# RUN: yaml2obj --docnum=3 -DCONTENT="'00'" -DSIZE=1 %s -o %t.cont.size.eq.o +# RUN: llvm-readobj --sections --section-data %t.cont.size.eq.o | \ +# RUN: FileCheck %s --check-prefix=CHECK-CONTENT -DDATA="00" + +# RUN: yaml2obj --docnum=3 -DCONTENT="'00'" -DSIZE=2 %s -o %t.cont.size.gr.o +# RUN: llvm-readobj --sections --section-data %t.cont.size.gr.o | \ +# RUN: FileCheck %s --check-prefix=CHECK-CONTENT -DDATA="0000" + +# CHECK-CONTENT: Name: .ARM.exidx +# CHECK-CONTENT: SectionData ( +# CHECK-CONTENT-NEXT: 0000: [[DATA]] | +# CHECK-CONTENT-NEXT: ) + +## Check we can use "Content" key alone to emit arbitrary content. + +# RUN: yaml2obj --docnum=3 -DCONTENT="'11223344'" %s -o %t.content.o +# RUN: llvm-readobj --sections --section-data %t.content.o | \ +# RUN: FileCheck %s --check-prefix=CHECK-CONTENT -DDATA="11223344" + +## Check we can use "Size" key alone to emit content of an arbitrary size. + +# RUN: yaml2obj --docnum=3 -DSIZE=4 %s -o %t.size.o +# RUN: llvm-readobj --sections --section-data %t.size.o | \ +# RUN: FileCheck %s --check-prefix=CHECK-CONTENT -DDATA="00000000" Index: llvm/tools/obj2yaml/elf2yaml.cpp =================================================================== --- llvm/tools/obj2yaml/elf2yaml.cpp +++ llvm/tools/obj2yaml/elf2yaml.cpp @@ -95,6 +95,8 @@ Expected dumpSymverSection(const Elf_Shdr *Shdr); Expected dumpVerneedSection(const Elf_Shdr *Shdr); Expected dumpGroup(const Elf_Shdr *Shdr); + Expected + dumpARMIndexTableSection(const Elf_Shdr *Shdr); Expected dumpMipsABIFlags(const Elf_Shdr *Shdr); Expected dumpStackSizesSection(const Elf_Shdr *Shdr); @@ -460,6 +462,9 @@ auto GetDumper = [this](unsigned Type) -> std::function(const Elf_Shdr *)> { + if (Obj.getHeader().e_machine == ELF::EM_ARM && Type == ELF::SHT_ARM_EXIDX) + return [this](const Elf_Shdr *S) { return dumpARMIndexTableSection(S); }; + switch (Type) { case ELF::SHT_DYNAMIC: return [this](const Elf_Shdr *S) { return dumpDynamicSection(S); }; @@ -1346,6 +1351,33 @@ return S.release(); } +template +Expected +ELFDumper::dumpARMIndexTableSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + Expected> ContentOrErr = Obj.getSectionContents(*Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + if (ContentOrErr->size() % (sizeof(Elf_Word) * 2) != 0) { + S->Content = yaml::BinaryRef(*ContentOrErr); + return S.release(); + } + + ArrayRef Words( + reinterpret_cast(ContentOrErr->data()), + ContentOrErr->size() / sizeof(Elf_Word)); + + S->Entries.emplace(); + for (size_t I = 0, E = Words.size(); I != E; I += 2) + S->Entries->push_back({(yaml::Hex32)Words[I], (yaml::Hex32)Words[I + 1]}); + + return S.release(); +} + template Expected ELFDumper::dumpMipsABIFlags(const Elf_Shdr *Shdr) {