Index: llvm/test/tools/llvm-readobj/ELF/section-details.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-readobj/ELF/section-details.test @@ -0,0 +1,234 @@ +## Check how llvm-readelf prints section details with --section-details. + +## Check the output for 64-bit case. +# RUN: yaml2obj %s -DBITS=64 -o %t64.o +# RUN: llvm-readelf %t64.o --section-details | \ +# RUN: FileCheck %s --strict-whitespace --match-full-lines --check-prefix=GNU64 + +# GNU64:There are 19 section headers, starting at offset 0xe8: +# GNU64-EMPTY: +# GNU64-NEXT:Section Headers: +# GNU64-NEXT: [Nr] Name +# GNU64-NEXT: Type Address Offset Link +# GNU64-NEXT: Size EntSize Info Align +# GNU64-NEXT: Flags +# GNU64-NEXT: [ 0] {{$}} +# GNU64-NEXT: NULL 0000000000000000 0000000000000000 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000000]: {{$}} +# GNU64-NEXT: [ 1] allflags +# GNU64-NEXT: PROGBITS 1111111111111111 2222222222222222 3 +# GNU64-NEXT: 4444444444444444 0000000000000005 6 7 +# GNU64-NEXT: [0000000080000ff7]: WRITE, ALLOC, EXEC, MERGE, STRINGS, INFO LINK, LINK ORDER, OS NONCONF, GROUP, TLS, COMPRESSED, EXCLUDE +# GNU64-NEXT: [ 2] noflags +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000000]: {{$}} +# GNU64-NEXT: [ 3] write +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000001]: WRITE +# GNU64-NEXT: [ 4] alloc +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000002]: ALLOC +# GNU64-NEXT: [ 5] exec +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000004]: EXEC +# GNU64-NEXT: [ 6] merge +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000010]: MERGE +# GNU64-NEXT: [ 7] strings +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000020]: STRINGS +# GNU64-NEXT: [ 8] infolink +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000040]: INFO LINK +# GNU64-NEXT: [ 9] linkorder +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000080]: LINK ORDER +# GNU64-NEXT: [10] nonconforming +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000100]: OS NONCONF +# GNU64-NEXT: [11] group +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000200]: GROUP +# GNU64-NEXT: [12] tls +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000400]: TLS +# GNU64-NEXT: [13] compressed +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000000000800]: COMPRESSED +# GNU64-NEXT: [14] exclude +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [0000000080000000]: EXCLUDE +# GNU64-NEXT: [15] known_and_unknown +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [00000000000f0003]: WRITE, ALLOC, UNKNOWN (00000000000f0000) +# GNU64-NEXT: [16] only_unknown +# GNU64-NEXT: PROGBITS 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000000 0000000000000000 0 0 +# GNU64-NEXT: [00000000000f0000]: UNKNOWN (00000000000f0000) +# GNU64-NEXT: [17] .strtab +# GNU64-NEXT: STRTAB 0000000000000000 0000000000000046 0 +# GNU64-NEXT: 0000000000000001 0000000000000000 0 1 +# GNU64-NEXT: [0000000000000000]: {{$}} +# GNU64-NEXT: [18] .shstrtab +# GNU64-NEXT: STRTAB 0000000000000000 0000000000000047 0 +# GNU64-NEXT: 00000000000000a0 0000000000000000 0 1 +# GNU64-NEXT: [0000000000000000]: {{$}} +# GNU64-NOT:{{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS[[BITS]] + Data: ELFDATA2LSB + Type: ET_REL + Flags: [] +Sections: + - Name: allflags + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_EXECINSTR, SHF_MERGE, + SHF_STRINGS, SHF_INFO_LINK, SHF_LINK_ORDER, SHF_OS_NONCONFORMING, + SHF_GROUP, SHF_TLS, SHF_COMPRESSED, SHF_EXCLUDE ] + Address: 0x1111111111111111 + ShOffset: 0x2222222222222222 + Link: 0x3 + ShSize: 0x4444444444444444 + EntSize: 0x5 + Info: 0x6 + AddressAlign: 0x7 + - Name: noflags + Type: SHT_PROGBITS + Flags: [ ] + - Name: write + Type: SHT_PROGBITS + Flags: [ SHF_WRITE ] + - Name: alloc + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + - Name: exec + Type: SHT_PROGBITS + Flags: [ SHF_EXECINSTR ] + - Name: merge + Type: SHT_PROGBITS + Flags: [ SHF_MERGE ] + - Name: strings + Type: SHT_PROGBITS + Flags: [ SHF_STRINGS ] + - Name: infolink + Type: SHT_PROGBITS + Flags: [ SHF_INFO_LINK ] + - Name: linkorder + Type: SHT_PROGBITS + Flags: [ SHF_LINK_ORDER ] + - Name: nonconforming + Type: SHT_PROGBITS + Flags: [ SHF_OS_NONCONFORMING ] + - Name: group + Type: SHT_PROGBITS + Flags: [ SHF_GROUP ] + - Name: tls + Type: SHT_PROGBITS + Flags: [ SHF_TLS ] + - Name: compressed + Type: SHT_PROGBITS + Flags: [ SHF_COMPRESSED ] + - Name: exclude + Type: SHT_PROGBITS + Flags: [ SHF_EXCLUDE ] + - Name: known_and_unknown + Type: SHT_PROGBITS + ShFlags: 0x000f0003 + - Name: only_unknown + Type: SHT_PROGBITS + ShFlags: 0x000f0000 + +## When --section-details and --sections are both specified, the latter one is ignored. +# RUN: llvm-readelf %t64.o --section-details --sections | FileCheck %s --check-prefix=GNU64 +# RUN: llvm-readelf %t64.o --sections --section-details | FileCheck %s --check-prefix=GNU64 + +## Check that we produce the same output with -t (alias). +# RUN: llvm-readelf --section-details %t64.o > %t.readelf.full +# RUN: llvm-readelf -t %t64.o > %t.readelf.alias +# RUN: cmp %t.readelf.full %t.readelf.alias + +## Check the output for 32-bit case. +# RUN: yaml2obj %s -DBITS=32 -o %t32.o +# RUN: llvm-readelf %t32.o --section-details | \ +# RUN: FileCheck %s --strict-whitespace --match-full-lines --check-prefix=GNU32 + +# GNU32:There are 19 section headers, starting at offset 0xdc: +# GNU32-EMPTY: +# GNU32-NEXT:Section Headers: +# GNU32-NEXT: [Nr] Name +# GNU32-NEXT: Type Addr Off Size ES Lk Inf Al +# GNU32-NEXT: [ 0] {{$}} +# GNU32-NEXT: NULL 00000000 00000000 00000000 00 0 0 0 +# GNU32-NEXT: [00000000]: {{$}} +# GNU32-NEXT: [ 1] allflags +# GNU32-NEXT: PROGBITS 11111111 22222222 44444444 05 3 6 7 +# GNU32-NEXT: [80000ff7]: WRITE, ALLOC, EXEC, MERGE, STRINGS, INFO LINK, LINK ORDER, OS NONCONF, GROUP, TLS, COMPRESSED, EXCLUDE +# GNU32-NEXT: [ 2] noflags +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000000]: {{$}} +# GNU32-NEXT: [ 3] write +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000001]: WRITE +# GNU32-NEXT: [ 4] alloc +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000002]: ALLOC +# GNU32-NEXT: [ 5] exec +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000004]: EXEC +# GNU32-NEXT: [ 6] merge +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000010]: MERGE +# GNU32-NEXT: [ 7] strings +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000020]: STRINGS +# GNU32-NEXT: [ 8] infolink +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000040]: INFO LINK +# GNU32-NEXT: [ 9] linkorder +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000080]: LINK ORDER +# GNU32-NEXT: [10] nonconforming +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000100]: OS NONCONF +# GNU32-NEXT: [11] group +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000200]: GROUP +# GNU32-NEXT: [12] tls +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000400]: TLS +# GNU32-NEXT: [13] compressed +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [00000800]: COMPRESSED +# GNU32-NEXT: [14] exclude +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [80000000]: EXCLUDE +# GNU32-NEXT: [15] known_and_unknown +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [000f0003]: WRITE, ALLOC, UNKNOWN (000f0000) +# GNU32-NEXT: [16] only_unknown +# GNU32-NEXT: PROGBITS 00000000 00000038 00000000 00 0 0 0 +# GNU32-NEXT: [000f0000]: UNKNOWN (000f0000) +# GNU32-NEXT: [17] .strtab +# GNU32-NEXT: STRTAB 00000000 00000038 00000001 00 0 0 1 +# GNU32-NEXT: [00000000]: {{$}} +# GNU32-NEXT: [18] .shstrtab +# GNU32-NEXT: STRTAB 00000000 00000039 000000a0 00 0 0 1 +# GNU32-NEXT: [00000000]: {{$}} +# GNU32-NOT:{{.}} Index: llvm/test/tools/llvm-readobj/ELF/symbols.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/symbols.test +++ llvm/test/tools/llvm-readobj/ELF/symbols.test @@ -78,9 +78,7 @@ # RUN: llvm-readelf --symbols %t64 > %t.symbols.gnu # RUN: llvm-readelf --syms %t64 > %t.syms.gnu # RUN: cmp %t.symbols.gnu %t.syms.gnu -## There is no -t option in llvm-readelf. -# RUN: not llvm-readelf -t %t64 2>&1 | FileCheck %s --check-prefix=NO-T-ERR -# NO-T-ERR: Unknown command line argument '-t' + ## -s is an llvm-readobj option to dump sections. # RUN: llvm-readobj -s --elf-output-style=GNU %t64 | FileCheck %s --implicit-check-not="Symbol table" Index: llvm/test/tools/llvm-readobj/basic.test =================================================================== --- llvm/test/tools/llvm-readobj/basic.test +++ llvm/test/tools/llvm-readobj/basic.test @@ -41,5 +41,5 @@ OBJ: -s - Alias for --section-headers OBJ: -t - Alias for --symbols ELF: -s - Alias for --symbols -ELF-NOT: {{ }}-t{{ }} +ELF: -t - Alias for --section-details HELP: @FILE Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -239,6 +239,7 @@ void printDynamicRelocations() override; void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override; void printHashSymbols() override; + void printSectionDetails() override; void printUnwindInfo() override; void printDynamicTable() override; @@ -736,6 +737,7 @@ virtual void printSectionHeaders() = 0; virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) = 0; virtual void printHashSymbols() {} + virtual void printSectionDetails() {} virtual void printDependentLibs() = 0; virtual void printDynamic() {} virtual void printDynamicRelocations() = 0; @@ -814,6 +816,7 @@ void printSectionHeaders() override; void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override; void printHashSymbols() override; + void printSectionDetails() override; void printDependentLibs() override; void printDynamic() override; void printDynamicRelocations() override; @@ -2307,6 +2310,10 @@ ELFDumperStyle->printHashSymbols(); } +template void ELFDumper::printSectionDetails() { + ELFDumperStyle->printSectionDetails(); +} + template void ELFDumper::printHashHistograms() { ELFDumperStyle->printHashHistograms(); } @@ -4158,6 +4165,122 @@ } } +template void GNUStyle::printSectionDetails() { + ArrayRef Sections = cantFail(this->Obj.sections()); + OS << "There are " << to_string(Sections.size()) + << " section headers, starting at offset " + << "0x" << to_hexString(this->Obj.getHeader().e_shoff, false) << ":\n\n"; + + OS << "Section Headers:\n"; + + auto PrintFields = [&](ArrayRef V) { + for (const Field &F : V) + printField(F); + OS << "\n"; + }; + + PrintFields({{"[Nr]", 2}, {"Name", 7}}); + + if (ELFT::Is64Bits) { + PrintFields({{"Type", 7}, {"Address", 25}, {"Offset", 42}, {"Link", 60}}); + PrintFields({{"Size", 7}, {"EntSize", 25}, {"Info", 42}, {"Align", 60}}); + PrintFields({{"Flags", 7}}); + } else { + PrintFields({{"Type", 7}, + {"Addr", 23}, + {"Off", 32}, + {"Size", 41}, + {"ES", 50}, + {"Lk", 55}, + {"Inf", 58}, + {"Al", 61}}); + } + + StringRef SecStrTable; + if (Expected SecStrTableOrErr = this->Obj.getSectionStringTable( + Sections, this->dumper().WarningHandler)) + SecStrTable = *SecStrTableOrErr; + else + this->reportUniqueWarning(SecStrTableOrErr.takeError()); + + size_t SectionIndex = 0; + const unsigned AddrSize = ELFT::Is64Bits ? 16 : 8; + for (const Elf_Shdr &S : Sections) { + OS.PadToColumn(2); + OS << "[" << right_justify(to_string(SectionIndex), 2) << "]"; + + StringRef Name = unwrapOrError( + this->FileName, this->Obj.getSectionName(S, SecStrTable)); + PrintFields({{Name, 7}}); + + if (ELFT::Is64Bits) { + PrintFields( + {{getSectionTypeString(this->Obj.getHeader().e_machine, S.sh_type), + 7}, + {to_string(format_hex_no_prefix(S.sh_addr, AddrSize)), 25}, + {to_string(format_hex_no_prefix(S.sh_offset, AddrSize)), 42}, + {to_string(format_hex_no_prefix(S.sh_link, 1)), 60}}); + + PrintFields( + {{to_string(format_hex_no_prefix(S.sh_size, AddrSize)), 7}, + {to_string(format_hex_no_prefix(S.sh_entsize, AddrSize)), 25}, + {to_string(format_hex_no_prefix(S.sh_info, 1)), 42}, + {to_string(format_hex_no_prefix(S.sh_addralign, 1)), 60}}); + } else { + PrintFields( + {{getSectionTypeString(this->Obj.getHeader().e_machine, S.sh_type), + 7}, + {to_string(format_hex_no_prefix(S.sh_addr, AddrSize)), 23}, + {to_string(format_hex_no_prefix(S.sh_offset, AddrSize)), 32}, + {to_string(format_hex_no_prefix(S.sh_size, AddrSize)), 39}, + {to_string(format_hex_no_prefix(S.sh_entsize, 2)), 50}, + {to_string(format_hex_no_prefix(S.sh_link, 1)), 55}, + {to_string(format_hex_no_prefix(S.sh_info, 1)), 59}, + {to_string(format_hex_no_prefix(S.sh_addralign, 1)), 62}}); + } + + OS.PadToColumn(7); + OS << "[" << to_string(format_hex_no_prefix(S.sh_flags, AddrSize)) << "]: "; + + DenseMap FlagToName = { + {SHF_WRITE, "WRITE"}, {SHF_ALLOC, "ALLOC"}, + {SHF_EXECINSTR, "EXEC"}, {SHF_MERGE, "MERGE"}, + {SHF_STRINGS, "STRINGS"}, {SHF_INFO_LINK, "INFO LINK"}, + {SHF_LINK_ORDER, "LINK ORDER"}, {SHF_OS_NONCONFORMING, "OS NONCONF"}, + {SHF_GROUP, "GROUP"}, {SHF_TLS, "TLS"}, + {SHF_COMPRESSED, "COMPRESSED"}, {SHF_EXCLUDE, "EXCLUDE"}}; + + uint64_t Flags = S.sh_flags; + uint64_t UnknownFlags = 0; + bool First = true; + while (Flags) { + // Take the least significant bit as a flag. + uint64_t Flag = Flags & -Flags; + Flags -= Flag; + + auto It = FlagToName.find(Flag); + if (It != FlagToName.end()) { + if (!First) + OS << ", "; + First = false; + OS << (*It).second; + } else { + UnknownFlags |= Flag; + } + } + + if (UnknownFlags != 0) { + if (!First) + OS << ", "; + OS << "UNKNOWN (" + << to_string(format_hex_no_prefix(UnknownFlags, AddrSize)) << ")"; + } + + OS << "\n"; + ++SectionIndex; + } +} + static inline std::string printPhdrFlags(unsigned Flag) { std::string Str; Str = (Flag & PF_R) ? "R" : " "; Index: llvm/tools/llvm-readobj/ObjDumper.h =================================================================== --- llvm/tools/llvm-readobj/ObjDumper.h +++ llvm/tools/llvm-readobj/ObjDumper.h @@ -72,7 +72,8 @@ virtual void printNotes() {} virtual void printELFLinkerOptions() {} virtual void printStackSizes() {} - virtual void printArchSpecificInfo() { } + virtual void printSectionDetails() {} + virtual void printArchSpecificInfo() {} // Only implemented for PE/COFF. virtual void printCOFFImports() { } Index: llvm/tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- llvm/tools/llvm-readobj/llvm-readobj.cpp +++ llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -137,6 +137,11 @@ cl::opt DynRelocs("dyn-relocations", cl::desc("Display the dynamic relocation entries in the file")); + // --section-details + // Also -t in llvm-readelf mode. + cl::opt SectionDetails("section-details", + cl::desc("Display the section details")); + // --symbols // Also -s in llvm-readelf mode, or -t in llvm-readobj mode. cl::opt @@ -480,8 +485,14 @@ if (opts::FileHeaders) Dumper->printFileHeaders(); - if (opts::SectionHeaders) - Dumper->printSectionHeaders(); + + if (opts::SectionDetails || opts::SectionHeaders) { + if (opts::Output == opts::GNU && opts::SectionDetails) + Dumper->printSectionDetails(); + else + Dumper->printSectionHeaders(); + } + if (opts::HashSymbols) Dumper->printHashSymbols(); if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE) @@ -656,8 +667,7 @@ cl::aliasopt(opts::SectionHeaders), cl::NotHidden); - // Only register -t in llvm-readobj, as readelf reserves it for - // --section-details (not implemented yet). + // llvm-readelf reserves it for --section-details. static cl::alias SymbolsShort("t", cl::desc("Alias for --symbols"), cl::aliasopt(opts::Symbols), cl::NotHidden); @@ -683,6 +693,11 @@ cl::aliasopt(opts::Symbols), cl::NotHidden, cl::Grouping); + // -t is here because for readobj it is an alias for --symbols. + static cl::alias SectionDetailsShort( + "t", cl::desc("Alias for --section-details"), + cl::aliasopt(opts::SectionDetails), cl::NotHidden); + // Allow all single letter flags to be grouped together. for (auto &OptEntry : cl::getRegisteredOptions()) { StringRef ArgName = OptEntry.getKey();