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.startswith(".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) { + typedef typename ELFT::uint uintX_t; + 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 @@ -1016,6 +1016,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)); @@ -1141,20 +1147,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.reset(new ELFYAML::StackSizesSection()); + else + Section.reset(new ELFYAML::RawContentSection()); + } + + 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 {}; } @@ -1183,6 +1209,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,61 @@ +## 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" Index: test/tools/yaml2obj/elf-stack-sizes.yaml =================================================================== --- /dev/null +++ test/tools/yaml2obj/elf-stack-sizes.yaml @@ -0,0 +1,182 @@ +## Check how yaml2obj produce .stack_sizes sections from YAML descriptions. + +## Show we can produce a .stack_sizes section from the description with +## a valid section content. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readobj --sections --section-data %t1 | FileCheck %s --check-prefix=CONTENT-VALID + +# CONTENT-VALID: Section { +# CONTENT-VALID: Index: 1 +# CONTENT-VALID-NEXT: Name: .stack_sizes (1) +# CONTENT-VALID-NEXT: Type: SHT_PROGBITS (0x1) +# CONTENT-VALID-NEXT: Flags [ (0x0) +# CONTENT-VALID-NEXT: ] +# CONTENT-VALID-NEXT: Address: 0x0 +# CONTENT-VALID-NEXT: Offset: 0x40 +# CONTENT-VALID-NEXT: Size: 9 +# CONTENT-VALID-NEXT: Link: 0 +# CONTENT-VALID-NEXT: Info: 0 +# CONTENT-VALID-NEXT: AddressAlignment: 0 +# CONTENT-VALID-NEXT: EntrySize: 0 +# CONTENT-VALID-NEXT: SectionData ( +# CONTENT-VALID-NEXT: 0000: 10000000 00000000 20 +# CONTENT-VALID-NEXT: ) +# CONTENT-VALID-NEXT: } + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "100000000000000020" + +## Show we can produce a incorrect .stack_sizes section from the description with +## a broken (truncated) section content. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefix=CONTENT-TRUNCATED + +# CONTENT-TRUNCATED: Name: .stack_sizes +# CONTENT-TRUNCATED: Size: +# CONTENT-TRUNCATED-SAME: 8 +# CONTENT-TRUNCATED: SectionData ( +# CONTENT-TRUNCATED-NEXT: 0000: 10000000 00000000 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "1000000000000000" + +## Show we can produce an empty .stack_sizes section from the description with +## an empty section content. + +# RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: llvm-readobj --sections --section-data %t3 | FileCheck %s --check-prefix=CONTENT-EMPTY + +# CONTENT-EMPTY: Name: .stack_sizes +# CONTENT-EMPTY: Size: +# CONTENT-EMPTY-SAME: 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .stack_sizes + Type: SHT_PROGBITS + Content: "" + +## Check we can describe .stack_sizes section using pairs. + +# RUN: yaml2obj --docnum=4 %s -o %t4 +# RUN: llvm-readobj --sections --section-data %t4 | 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=5 %s -o %t5 +# RUN: llvm-readobj --sections --section-data %t5 | 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=6 %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=7 %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=8 %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,16 @@ LLVM_FALLTHROUGH; } default: { + // We still might have a special section, which has a regular type. + // Such sections are recognized by name. + 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 +447,21 @@ return Error::success(); } +template +Expected +ELFDumper::dumpSpecialSection(const Elf_Shdr *Shdr) { + if (Shdr->sh_type != ELF::SHT_PROGBITS) + return nullptr; + + 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 +480,43 @@ 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; + if (!Content.empty()) { + 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 (!Cur) { + // If the data of .stack_sizes section is broken in any way, + // we dump it as 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) {