diff --git a/docs/CommandGuide/llvm-readobj.rst b/docs/CommandGuide/llvm-readobj.rst index b1918b548f8..417fcd05c8a 100644 --- a/docs/CommandGuide/llvm-readobj.rst +++ b/docs/CommandGuide/llvm-readobj.rst @@ -80,6 +80,10 @@ input. Otherwise, it will read from the specified ``filenames``. Display the ELF program headers (only for ELF object files). +.. option:: -elf-section-groups, -g + + Display section groups (only for ELF object files). + EXIT STATUS ----------- diff --git a/test/tools/llvm-readobj/Inputs/elf-groups.x86_64 b/test/tools/llvm-readobj/Inputs/elf-groups.x86_64 new file mode 100644 index 00000000000..29628ca09b0 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/elf-groups.x86_64 differ diff --git a/test/tools/llvm-readobj/elf-groups.test b/test/tools/llvm-readobj/elf-groups.test new file mode 100644 index 00000000000..e41b9dd977d --- /dev/null +++ b/test/tools/llvm-readobj/elf-groups.test @@ -0,0 +1,39 @@ +# Source file compiled: +##include +# #include +#template +#void foo(A variable) { +# std::cout << "foo is " << typeid(A).name() << "\n"; +#} +#int main () { +# char x = 'x'; +# int y = 1; +# foo(x); +# foo(y); +# return 0; +#} + +RUN: llvm-readobj -g %p/Inputs/elf-groups.x86_64 \ +RUN: | FileCheck %s +CHECK: Groups { +CHECK-NEXT: Group { +CHECK-NEXT: Name: .group (92) +CHECK-NEXT: Index: 1 +CHECK-NEXT: Type: COMDAT (0x1) +CHECK-NEXT: Signature: _Z3fooIcEvT_ +CHECK-NEXT: Section(s) in group [ +CHECK-NEXT: .text._Z3fooIcEvT_ (10) +CHECK-NEXT: .rela.text._Z3fooIcEvT_ (11) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: Group { +CHECK-NEXT: Name: .group (92) +CHECK-NEXT: Index: 2 +CHECK-NEXT: Type: COMDAT (0x1) +CHECK-NEXT: Signature: _Z3fooIiEvT_ +CHECK-NEXT: Section(s) in group [ +CHECK-NEXT: .text._Z3fooIiEvT_ (12) +CHECK-NEXT: .rela.text._Z3fooIiEvT_ (13) +CHECK-NEXT: ] +CHECK-NEXT: } + diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index f6ec9770d80..fac6bf4296e 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -59,6 +59,7 @@ public: void printGnuHashTable() override; void printLoadName() override; void printVersionInfo() override; + void printGroupSections() override; void printAttributes() override; void printMipsPLTGOT() override; @@ -792,6 +793,13 @@ static const char *getElfSectionType(unsigned Arch, unsigned Type) { } } +static const char *getGroupType(uint32_t Flag) { + if (Flag & ELF::GRP_COMDAT) + return "COMDAT"; + else + return "(unknown)"; +} + static const EnumEntry ElfSectionFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, SHF_WRITE ), LLVM_READOBJ_ENUM_ENT(ELF, SHF_ALLOC ), @@ -2250,3 +2258,41 @@ template void ELFDumper::printStackMap() const { llvm::outs(), StackMapV1Parser(*StackMapContentsArray)); } +template void ELFDumper::printGroupSections() { + DictScope Lists(W, "Groups"); + uint32_t SectionIndex = 0; + bool HasGroups = false; + for (const Elf_Shdr &Sec : Obj->sections()) { + if (Sec.sh_type == ELF::SHT_GROUP) { + HasGroups = true; + ErrorOr Symtab = + errorOrDefault(Obj->getSection(Sec.sh_link)); + ErrorOr StrTableOrErr = Obj->getStringTableForSymtab(**Symtab); + error(StrTableOrErr.getError()); + StringRef StrTable = *StrTableOrErr; + const Elf_Sym *Sym = + Obj->template getEntry(*Symtab, Sec.sh_info); + auto Data = errorOrDefault( + Obj->template getSectionContentsAsArray(&Sec)); + DictScope D(W, "Group"); + StringRef Name = errorOrDefault(Obj->getSectionName(&Sec)); + W.printNumber("Name", Name, Sec.sh_name); + W.printNumber("Index", SectionIndex); + W.printHex("Type", getGroupType(Data[0]), Data[0]); + W.startLine() << "Signature: " << StrTable.data() + Sym->st_name << "\n"; + { + ListScope L(W, "Section(s) in group"); + int Member = 1; + while (Member < Data.size()) { + auto Sec = errorOrDefault(Obj->getSection(Data[Member])); + const StringRef Name = errorOrDefault(Obj->getSectionName(Sec)); + W.startLine() << Name << " (" << std::to_string(Data[Member++]) + << ")\n"; + } + } + } + ++SectionIndex; + } + if (!HasGroups) + W.startLine() << "There are no group sections in the file.\n"; +} diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index db26d698355..b83b8d49f22 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -42,6 +42,7 @@ public: virtual void printGnuHashTable() { } virtual void printLoadName() {} virtual void printVersionInfo() {} + virtual void printGroupSections() {} // Only implemented for ARM ELF at this time. virtual void printAttributes() { } diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index fa8fee2b03a..790b1d59550 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -227,6 +227,12 @@ namespace opts { cl::desc("Display ELF version sections (if present)")); cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"), cl::aliasopt(VersionInfo)); + + cl::opt SectionGroups("elf-section-groups", + cl::desc("Display ELF section group contents")); + cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"), + cl::aliasopt(SectionGroups)); + } // namespace opts namespace llvm { @@ -334,16 +340,20 @@ static void dumpObject(const ObjectFile *Obj) { Dumper->printGnuHashTable(); if (opts::VersionInfo) Dumper->printVersionInfo(); - if (Obj->getArch() == llvm::Triple::arm && Obj->isELF()) - if (opts::ARMAttributes) - Dumper->printAttributes(); - if (isMipsArch(Obj->getArch()) && Obj->isELF()) { - if (opts::MipsPLTGOT) - Dumper->printMipsPLTGOT(); - if (opts::MipsABIFlags) - Dumper->printMipsABIFlags(); - if (opts::MipsReginfo) - Dumper->printMipsReginfo(); + if (Obj->isELF()) { + if (Obj->getArch() == llvm::Triple::arm) + if (opts::ARMAttributes) + Dumper->printAttributes(); + if (isMipsArch(Obj->getArch())) { + if (opts::MipsPLTGOT) + Dumper->printMipsPLTGOT(); + if (opts::MipsABIFlags) + Dumper->printMipsABIFlags(); + if (opts::MipsReginfo) + Dumper->printMipsReginfo(); + } + if (opts::SectionGroups) + Dumper->printGroupSections(); } if (Obj->isCOFF()) { if (opts::COFFImports)