diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h index 44f5d7cd069eda..252f0c8e212ede 100644 --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -177,6 +177,9 @@ struct Section : public Chunk { // When they are, this flag is used to signal about that. bool IsImplicit; + // Holds the original section index. + unsigned OriginalSecNdx; + Section(ChunkKind Kind, bool IsImplicit = false) : Chunk(Kind), IsImplicit(IsImplicit) {} diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index ac3eefdeaace6b..bf6d9657e76e47 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -88,6 +88,10 @@ class ELFDumper { Expected dumpMipsABIFlags(const Elf_Shdr *Shdr); Expected dumpStackSizesSection(const Elf_Shdr *Shdr); + Expected + dumpPlaceholderSection(const Elf_Shdr *Shdr); + + bool shouldPrintSection(const ELFYAML::Section &S, const Elf_Shdr &SHdr); public: ELFDumper(const object::ELFFile &O); @@ -112,6 +116,12 @@ ELFDumper::getUniquedSectionName(const Elf_Shdr *Sec) { if (!NameOrErr) return NameOrErr; StringRef Name = *NameOrErr; + // In some specific cases we might have more than one section without a + // name (sh_name == 0). It normally doesn't happen, but when we have this case + // it doesn't make sense to uniquify their names and add noise to the output. + if (Name.empty()) + return ""; + std::string &Ret = SectionNames[SecIndex]; auto It = UsedSectionNames.insert({Name, 0}); @@ -157,6 +167,32 @@ ELFDumper::getUniquedSymbolName(const Elf_Sym *Sym, StringRef StrTable, return Name; } +template +bool ELFDumper::shouldPrintSection(const ELFYAML::Section &S, + const Elf_Shdr &SHdr) { + // We only print the SHT_NULL section at index 0 when it + // has at least one non-null field, because yaml2obj + // normally creates the zero section at index 0 implicitly. + if (S.Type == ELF::SHT_NULL && (&SHdr == &Sections[0])) { + const uint8_t *Begin = reinterpret_cast(&SHdr); + const uint8_t *End = Begin + sizeof(Elf_Shdr); + return std::find_if(Begin, End, [](uint8_t V) { return V != 0; }) != End; + } + + // Normally we use "Symbols:" and "DynamicSymbols:" to describe contents of + // symbol tables. We also build and emit corresponding string tables + // implicitly. But sometimes it is important to preserve positions and virtual + // addresses of allocatable sections, e.g. for creating program headers. + // Generally we are trying to reduce noise in the YAML output. Because + // of that we do not print non-allocatable versions of such sections and + // assume they are placed at the end. + if (S.Type == ELF::SHT_STRTAB || S.Type == ELF::SHT_SYMTAB || + S.Type == ELF::SHT_DYNSYM) + return S.Flags.getValueOr(ELFYAML::ELF_SHF(0)) & ELF::SHF_ALLOC; + + return true; +} + template Expected ELFDumper::dump() { auto Y = std::make_unique(); @@ -227,15 +263,34 @@ template Expected ELFDumper::dump() { return std::move(E); } - if (Expected>> ChunksOrErr = - dumpSections()) - Y->Chunks = std::move(*ChunksOrErr); - else + // We dump all sections first. It is simple and allows us to verify that all + // sections are valid and also to generalize the code. But we are not going to + // keep all of them in the final output (see comments for + // 'shouldPrintSection()'). Undesired chunks will be removed later. + Expected>> ChunksOrErr = + dumpSections(); + if (!ChunksOrErr) return ChunksOrErr.takeError(); + std::vector> Chunks = std::move(*ChunksOrErr); + llvm::erase_if(Chunks, [this](const std::unique_ptr &C) { + const ELFYAML::Section &S = cast(*C.get()); + return !shouldPrintSection(S, Sections[S.OriginalSecNdx]); + }); + + Y->Chunks = std::move(Chunks); return Y.release(); } +template +Expected +ELFDumper::dumpPlaceholderSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique(); + if (Error E = dumpCommonSection(Shdr, *S.get())) + return std::move(E); + return S.release(); +} + template Expected>> ELFDumper::dumpSections() { @@ -288,6 +343,13 @@ ELFDumper::dumpSections() { case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: return [this](const Elf_Shdr *S) { return dumpCallGraphProfileSection(S); }; + case ELF::SHT_STRTAB: + case ELF::SHT_SYMTAB: + case ELF::SHT_DYNSYM: + // The contents of these sections are described by other parts of the YAML + // file. But we still want to dump them, because their properties can be + // important. See comments for 'shouldPrintSection()' for more details. + return [this](const Elf_Shdr *S) { return dumpPlaceholderSection(S); }; default: return nullptr; } @@ -303,37 +365,6 @@ ELFDumper::dumpSections() { continue; } - if (Sec.sh_type == ELF::SHT_STRTAB || Sec.sh_type == ELF::SHT_SYMTAB || - Sec.sh_type == ELF::SHT_DYNSYM) { - // The contents of these sections are described by other parts of the YAML - // file. We still dump them so that their positions in the section header - // table are correctly recorded. We only dump allocatable section because - // their positions and addresses are important, e.g. for creating program - // headers. Some sections, like .symtab or .strtab normally are not - // allocatable and do not have virtual addresses. We want to avoid noise - // in the YAML output and assume that they are placed at the end. - if (Sec.sh_flags & ELF::SHF_ALLOC) { - auto S = std::make_unique(); - if (Error E = dumpCommonSection(&Sec, *S.get())) - return std::move(E); - if (Error E = Add(S.release())) - return std::move(E); - } - continue; - } - - if (Sec.sh_type == ELF::SHT_NULL) { - // We only dump the SHT_NULL section at index 0 when it - // has at least one non-null field, because yaml2obj - // normally creates the zero section at index 0 implicitly. - if (&Sec == &Sections[0]) { - const uint8_t *Begin = reinterpret_cast(&Sec); - const uint8_t *End = Begin + sizeof(Elf_Shdr); - if (std::find_if(Begin, End, [](uint8_t V) { return V != 0; }) == End) - continue; - } - } - // Recognize some special SHT_PROGBITS sections by name. if (Sec.sh_type == ELF::SHT_PROGBITS) { auto NameOrErr = getUniquedSectionName(&Sec); @@ -484,6 +515,8 @@ Error ELFDumper::dumpCommonSection(const Elf_Shdr *Shdr, if (Shdr->sh_entsize != getDefaultShEntSize(S.Type)) S.EntSize = static_cast(Shdr->sh_entsize); + S.OriginalSecNdx = Shdr - &Sections[0]; + auto NameOrErr = getUniquedSectionName(Shdr); if (!NameOrErr) return NameOrErr.takeError();