Index: include/llvm/Object/ELF.h =================================================================== --- include/llvm/Object/ELF.h +++ include/llvm/Object/ELF.h @@ -150,15 +150,16 @@ static Expected create(StringRef Object); + bool isLE() const { + return getHeader()->getDataEncoding() == ELF::ELFDATA2LSB; + } + bool isMipsELF64() const { return getHeader()->e_machine == ELF::EM_MIPS && getHeader()->getFileClass() == ELF::ELFCLASS64; } - bool isMips64EL() const { - return isMipsELF64() && - getHeader()->getDataEncoding() == ELF::ELFDATA2LSB; - } + bool isMips64EL() const { return isMipsELF64() && isLE(); } Expected sections() const; Index: include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- include/llvm/ObjectYAML/ELFYAML.h +++ include/llvm/ObjectYAML/ELFYAML.h @@ -117,6 +117,11 @@ llvm::yaml::Hex64 Val; }; +struct StackSizeEntry { + llvm::yaml::Hex64 Address; + llvm::yaml::Hex64 Size; +}; + struct Section { enum class SectionKind { Dynamic, @@ -126,6 +131,7 @@ NoBits, Verdef, Verneed, + StackSizes, SymtabShndxSection, Symver, MipsABIFlags @@ -163,6 +169,21 @@ Optional ShSize; }; +struct StackSizesSection : Section { + Optional Content; + Optional> Entries; + + StackSizesSection() : Section(SectionKind::StackSizes) {} + + static bool classof(const Section *S) { + return S->Kind == SectionKind::StackSizes; + } + + static bool nameMatches(StringRef Name) { + return Name == ".stack_sizes"; + } +}; + struct DynamicSection : Section { std::vector Entries; Optional Content; @@ -326,6 +347,7 @@ } // end namespace ELFYAML } // end namespace llvm +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::DynamicEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) @@ -461,6 +483,10 @@ static StringRef validate(IO &IO, ELFYAML::Symbol &Symbol); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::DynamicEntry &Rel); }; Index: lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- lib/ObjectYAML/ELFEmitter.cpp +++ lib/ObjectYAML/ELFEmitter.cpp @@ -19,6 +19,7 @@ #include "llvm/ObjectYAML/ELFYAML.h" #include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/Support/EndianStream.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/YAMLTraits.h" @@ -167,6 +168,9 @@ void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::DynamicSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::StackSizesSection &Section, + ContiguousBlobAccumulator &CBA); ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH); public: @@ -411,6 +415,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 { llvm_unreachable("Unknown section type"); } @@ -781,6 +787,26 @@ SHeader.sh_size = Section.Entries.size() * SHeader.sh_entsize; } +template +void ELFState::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::StackSizesSection &Section, + ContiguousBlobAccumulator &CBA) { + using uintX_t = ELFT::uint; + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + if (Section.Content) { + Section.Content->writeAsBinary(OS); + SHeader.sh_size = Section.Content->binary_size(); + return; + } + + for (const ELFYAML::StackSizeEntry &E : *Section.Entries) { + support::endian::write(OS, E.Address, ELFT::TargetEndianness); + SHeader.sh_size += sizeof(uintX_t) + encodeULEB128(E.Size, OS); + } +} + template void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::VerdefSection &Section, Index: lib/ObjectYAML/ELFYAML.cpp =================================================================== --- lib/ObjectYAML/ELFYAML.cpp +++ lib/ObjectYAML/ELFYAML.cpp @@ -1017,6 +1017,12 @@ IO.mapOptional("Info", Section.Info); } +static void sectionMapping(IO &IO, ELFYAML::StackSizesSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Entries", Section.Entries); +} + static void sectionMapping(IO &IO, ELFYAML::NoBitsSection &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Size", Section.Size, Hex64(0)); @@ -1142,20 +1148,40 @@ sectionMapping(IO, *cast(Section.get())); break; default: - if (!IO.outputting()) - Section.reset(new ELFYAML::RawContentSection()); - sectionMapping(IO, *cast(Section.get())); + if (!IO.outputting()) { + StringRef Name; + IO.mapOptional("Name", Name, StringRef()); + + if (ELFYAML::StackSizesSection::nameMatches(Name)) + Section = std::make_unique(); + else + Section = std::make_unique(); + } + + if (auto S = dyn_cast(Section.get())) + sectionMapping(IO, *S); + else + sectionMapping(IO, *cast(Section.get())); } } StringRef MappingTraits>::validate( IO &io, std::unique_ptr &Section) { - const auto *RawSection = dyn_cast(Section.get()); - if (!RawSection) + if (const auto *RawSection = + dyn_cast(Section.get())) { + if (RawSection->Size && RawSection->Content && + (uint64_t)(*RawSection->Size) < RawSection->Content->binary_size()) + return "Section size must be greater than or equal to the content size"; return {}; - if (RawSection->Size && RawSection->Content && - (uint64_t)(*RawSection->Size) < RawSection->Content->binary_size()) - return "Section size must be greater than or equal to the content size"; + } + + if (const auto *SS = dyn_cast(Section.get())) { + if (SS->Content && SS->Entries) + return ".stack_sizes can't have both Content and Entries tags"; + if (!SS->Content && !SS->Entries) + return "for .stack_sizes either Content or Entries tag must be specified"; + return {}; + } return {}; } @@ -1184,6 +1210,13 @@ } // end anonymous namespace +void MappingTraits::mapping( + IO &IO, ELFYAML::StackSizeEntry &E) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapOptional("Address", E.Address, Hex64(0)); + IO.mapRequired("Size", E.Size); +} + void MappingTraits::mapping(IO &IO, ELFYAML::DynamicEntry &Rel) { assert(IO.getContext() && "The IO context is not initialized"); Index: test/tools/llvm-readobj/stack-sizes.test =================================================================== --- test/tools/llvm-readobj/stack-sizes.test +++ test/tools/llvm-readobj/stack-sizes.test @@ -134,7 +134,7 @@ Size: 16 - Name: .stack_sizes Type: SHT_PROGBITS - Size: 1 + Content: "00" Link: .text - Name: .rela.stack_sizes Type: SHT_RELA @@ -367,7 +367,8 @@ Size: 16 - Name: .stack_sizes Type: SHT_PROGBITS - Size: 9 + Entries: + - Size: 0 Link: .text - Name: .rela.stack_sizes Type: SHT_RELA @@ -400,10 +401,10 @@ - Name: .text Type: SHT_PROGBITS Size: 8 - - Name: .stack_sizes - Type: SHT_PROGBITS - Size: 16 - Link: .text + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "" + Link: .text - Name: .rela.stack_sizes Type: SHT_RELA Info: .stack_sizes Index: test/tools/obj2yaml/elf-stack-sizes.yaml =================================================================== --- /dev/null +++ test/tools/obj2yaml/elf-stack-sizes.yaml @@ -0,0 +1,88 @@ +## Check how obj2yaml produce YAML .stack_sizes descriptions. + +## Check that obj2yaml uses `Entries` tag to describe .stack_sizes section +## when it can extract pairs. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=VALID + +# VALID: --- !ELF +# VALID-NEXT: FileHeader: +# VALID-NEXT: Class: ELFCLASS64 +# VALID-NEXT: Data: ELFDATA2LSB +# VALID-NEXT: Type: ET_EXEC +# VALID-NEXT: Machine: EM_X86_64 +# VALID-NEXT: Sections: +# VALID-NEXT: - Name: .stack_sizes +# VALID-NEXT: Type: SHT_PROGBITS +# VALID-NEXT: Entries: +# VALID-NEXT: - Address: 0x0000000000000010 +# VALID-NEXT: Size: 0x0000000000000020 +# VALID-NEXT: - Address: 0x0000000000000030 +# VALID-NEXT: Size: 0x0000000000000040 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "100000000000000020300000000000000040" + +## Check that obj2yaml uses `Content` tag to describe .stack_sizes section +## when can't extract the entries. For example, when section data is truncated. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=INVALID + +# INVALID: --- !ELF +# INVALID-NEXT: FileHeader: +# INVALID-NEXT: Class: ELFCLASS64 +# INVALID-NEXT: Data: ELFDATA2LSB +# INVALID-NEXT: Type: ET_EXEC +# INVALID-NEXT: Machine: EM_X86_64 +# INVALID-NEXT: Sections: +# INVALID-NEXT: - Name: .stack_sizes +# INVALID-NEXT: Type: SHT_PROGBITS +# INVALID-NEXT: Content: '1000000000000000203000000000000000' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "1000000000000000203000000000000000" + +## Check obj2yaml can dump the empty .stack_sizes. + +# RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: obj2yaml %t3 | FileCheck %s --check-prefix=EMPTY + +# EMPTY: --- !ELF +# EMPTY-NEXT: FileHeader: +# EMPTY-NEXT: Class: ELFCLASS64 +# EMPTY-NEXT: Data: ELFDATA2LSB +# EMPTY-NEXT: Type: ET_EXEC +# EMPTY-NEXT: Machine: EM_X86_64 +# EMPTY-NEXT: Sections: +# EMPTY-NEXT: - Name: .stack_sizes +# EMPTY-NEXT: Type: SHT_PROGBITS +# EMPTY-NEXT: Content: '' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "" Index: test/tools/yaml2obj/elf-stack-sizes.yaml =================================================================== --- /dev/null +++ test/tools/yaml2obj/elf-stack-sizes.yaml @@ -0,0 +1,165 @@ +## Check how yaml2obj produces .stack_sizes sections. + +## The first test: +## 1) Shows we can produce a .stack_sizes section from the description with +## a valid section content. +## 2) Shows we can produce a incorrect .stack_sizes section from the description with +## a broken (truncated) section content. +## 3) Shows we can produce an empty .stack_sizes section from the description with +## an empty section content. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readobj --sections --section-data %t1 | FileCheck %s + +## Case 1: a valid content is used. +# CHECK: Section { +# CHECK: Index: 1 +# CHECK-NEXT: Name: .stack_sizes (1) +# CHECK-NEXT: Type: SHT_PROGBITS (0x1) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x0 +# CHECK-NEXT: Offset: 0x40 +# CHECK-NEXT: Size: 9 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 0 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: SectionData ( +# CHECK-NEXT: 0000: 10000000 00000000 20 +# CHECK-NEXT: ) +# CHECK-NEXT: } + +## Case 2: a truncated content is used. +# CHECK: Name: .stack_sizes +# CHECK: Size: +# CHECK-SAME: 8 +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 10000000 00000000 + +## Case 3: an empty content is used. +# CHECK: Name: .stack_sizes +# CHECK: Size: +# CHECK-SAME: 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: +## Valid. + - Name: '.stack_sizes [1]' + Type: SHT_PROGBITS + Content: "100000000000000020" +## Truncated. + - Name: '.stack_sizes [2]' + Type: SHT_PROGBITS + Content: "1000000000000000" +## Empty. + - Name: '.stack_sizes [3]' + Type: SHT_PROGBITS + Content: "" + +## Check we can describe .stack_sizes section using pairs. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefix=ENTRIES-BOTH + +# ENTRIES-BOTH: Name: .stack_sizes +# ENTRIES-BOTH: SectionData ( +# ENTRIES-BOTH-NEXT: 0000: 10000000 00000000 20300000 00000000 | +# ENTRIES-BOTH-NEXT: 0010: 0040 | + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Entries: + - Address: 0x10 + Size: 0x20 + - Address: 0x30 + Size: 0x40 + +## Check we can omit `Address` tag. In this case address part will be zeroed. + +# RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: llvm-readobj --sections --section-data %t3 | FileCheck %s --check-prefix=ENTRIES-NOADDR + +# ENTRIES-NOADDR: Name: .stack_sizes +# ENTRIES-NOADDR: SectionData ( +# ENTRIES-NOADDR-NEXT: 0000: 00000000 00000000 10000000 00000000 | +# ENTRIES-NOADDR-NEXT: 0010: 0020 | + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Entries: + - Size: 0x10 + - Size: 0x20 + +## Check that `Size` tag is mandatory when we describe .stack_sizes using Entries. + +# RUN: not yaml2obj --docnum=4 %s 2>&1 | FileCheck %s --check-prefix=ENTRIES-NOSIZE + +# ENTRIES-NOSIZE: error: missing required key 'Size' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Entries: + - Address: 0x10 + +## Check we can't use both `Content` and `Entries` tags at the same time. + +# RUN: not yaml2obj --docnum=5 %s 2>&1 | FileCheck %s --check-prefix=ENTRIES-AND-CONTENT + +# ENTRIES-AND-CONTENT: error: .stack_sizes can't have both Content and Entries tags + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "00" + Entries: + - Address: 0x10 + Size: 0x20 + +## Check we must specify either `Content` or `Entries` tag when describe .stack_sizes. + +# RUN: not yaml2obj --docnum=6 %s 2>&1 | FileCheck %s --check-prefix=NO-TAGS + +# NO-TAGS: error: for .stack_sizes either Content or Entries tag must be specified + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS Index: tools/obj2yaml/elf2yaml.cpp =================================================================== --- tools/obj2yaml/elf2yaml.cpp +++ tools/obj2yaml/elf2yaml.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/ObjectYAML/ELFYAML.h" +#include "llvm/Support/DataExtractor.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/YAMLTraits.h" @@ -67,6 +68,10 @@ Expected dumpVerneedSection(const Elf_Shdr *Shdr); Expected dumpGroup(const Elf_Shdr *Shdr); Expected dumpMipsABIFlags(const Elf_Shdr *Shdr); + Expected + dumpStackSizesSection(const Elf_Shdr *Shdr); + + Expected dumpSpecialSection(const Elf_Shdr *Shdr); public: ELFDumper(const object::ELFFile &O); @@ -284,6 +289,17 @@ LLVM_FALLTHROUGH; } default: { + // Recognize some special SHT_PROGBITS sections by name. + if (Sec.sh_type == ELF::SHT_PROGBITS) { + Expected SpecialSecOrErr = dumpSpecialSection(&Sec); + if (!SpecialSecOrErr) + return SpecialSecOrErr.takeError(); + if (*SpecialSecOrErr) { + Y->Sections.emplace_back(*SpecialSecOrErr); + break; + } + } + Expected SecOrErr = dumpContentSection(&Sec); if (!SecOrErr) @@ -432,6 +448,18 @@ return Error::success(); } +template +Expected +ELFDumper::dumpSpecialSection(const Elf_Shdr *Shdr) { + auto NameOrErr = getUniquedSectionName(Shdr); + if (!NameOrErr) + return NameOrErr.takeError(); + + if (ELFYAML::StackSizesSection::nameMatches(*NameOrErr)) + return dumpStackSizesSection(Shdr); + return nullptr; +} + template Error ELFDumper::dumpCommonRelocationSection( const Elf_Shdr *Shdr, ELFYAML::RelocationSection &S) { @@ -450,6 +478,41 @@ return Error::success(); } +template +Expected +ELFDumper::dumpStackSizesSection(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; + DataExtractor Data( + StringRef(reinterpret_cast(Content.data()), Content.size()), + Obj.isLE(), ELFT::Is64Bits ? 8 : 4); + + std::vector Entries; + DataExtractor::Cursor Cur(0); + while (Cur && Cur.tell() < Content.size()) { + uint64_t Address = Data.getAddress(Cur); + uint64_t Size = Data.getULEB128(Cur); + Entries.push_back({Address, Size}); + } + + if (Content.empty() || !Cur) { + // If .stack_sizes cannot be decoded, we dump it as an array of bytes. + consumeError(Cur.takeError()); + S->Content = yaml::BinaryRef(Content); + } else { + S->Entries = std::move(Entries); + } + + return S.release(); +} + template Expected ELFDumper::dumpDynamicSection(const Elf_Shdr *Shdr) {