diff options
-rw-r--r-- | llvm/test/tools/llvm-readobj/gnu-notes.test | 76 | ||||
-rw-r--r-- | llvm/tools/llvm-readobj/ELFDumper.cpp | 138 | ||||
-rw-r--r-- | llvm/tools/llvm-readobj/ObjDumper.h | 1 | ||||
-rw-r--r-- | llvm/tools/llvm-readobj/llvm-readobj.cpp | 6 |
4 files changed, 221 insertions, 0 deletions
diff --git a/llvm/test/tools/llvm-readobj/gnu-notes.test b/llvm/test/tools/llvm-readobj/gnu-notes.test new file mode 100644 index 00000000000..1a9c7e304b1 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/gnu-notes.test @@ -0,0 +1,76 @@ +# RUN: yaml2obj %s > %t.so +# RUN: llvm-readobj -elf-output-style GNU --notes %t.so | FileCheck %s + +# CHECK: Displaying notes found at file offset 0x00000300 with length 0x00000020: +# CHECK: Owner Data size Description +# CHECK: GNU 0x00000010 NT_GNU_BUILD_ID (unique build ID bitstring) +# CHECK: Build ID: 4fcb712aa6387724a9f465a32cd8c14b + +# CHECK: Displaying notes found at file offset 0x0000036c with length 0x0000001c: +# CHECK: Owner Data size Description +# CHECK: GNU 0x00000009 NT_GNU_GOLD_VERSION (gold version) +# CHECK: Version: gold 1.11 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .note.gnu.build-id + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x0000000000400120 + AddressAlign: 0x0000000000000004 + Content: 040000001000000003000000474E55004FCB712AA6387724A9F465A32CD8C14B + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0000000000400140 + AddressAlign: 0x0000000000000001 + Content: 31C0C3 + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0000000000400148 + AddressAlign: 0x0000000000000008 + Content: 1400000000000000017A5200017810011B0C070890010000140000001C000000D8FFFFFF030000000000000000000000 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x0000000000401000 + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x0000000000401000 + AddressAlign: 0x0000000000000001 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 004743433A2028474E552920352E342E3000 + - Name: .note.gnu.gold-version + Type: SHT_NOTE + AddressAlign: 0x0000000000000004 + Content: 040000000900000004000000474E5500676F6C6420312E3131000000 +Symbols: + Local: + - Name: reduced.c + Type: STT_FILE + - Type: STT_FILE + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000400140 + Size: 0x0000000000000003 + - Name: _edata + Value: 0x0000000000401000 + - Name: __bss_start + Value: 0x0000000000401000 + - Name: _end + Value: 0x0000000000401000 +... diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 1ed10932e69..7e3394ba2fd 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -126,6 +126,8 @@ public: void printHashHistogram() override; + void printNotes() override; + private: std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle; typedef ELFFile<ELFT> ELFO; @@ -292,6 +294,7 @@ public: bool IsDynamic) = 0; virtual void printProgramHeaders(const ELFFile<ELFT> *Obj) = 0; virtual void printHashHistogram(const ELFFile<ELFT> *Obj) = 0; + virtual void printNotes(const ELFFile<ELFT> *Obj) = 0; const ELFDumper<ELFT> *dumper() const { return Dumper; } private: const ELFDumper<ELFT> *Dumper; @@ -314,6 +317,7 @@ public: size_t Offset) override; void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile<ELFT> *Obj) override; + void printNotes(const ELFFile<ELFT> *Obj) override; private: struct Field { @@ -367,6 +371,7 @@ public: void printDynamicRelocations(const ELFO *Obj) override; void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile<ELFT> *Obj) override; + void printNotes(const ELFFile<ELFT> *Obj) override; private: void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab); @@ -1491,6 +1496,11 @@ void ELFDumper<ELFT>::printDynamicSymbols() { template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() { ELFDumperStyle->printHashHistogram(Obj); } + +template <class ELFT> void ELFDumper<ELFT>::printNotes() { + ELFDumperStyle->printNotes(Obj); +} + #define LLVM_READOBJ_TYPE_CASE(name) \ case DT_##name: return #name @@ -3161,6 +3171,127 @@ void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { } } +static std::string getGNUNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = { + {ELF::NT_GNU_ABI_TAG, "NT_GNU_ABI_TAG (ABI version tag)"}, + {ELF::NT_GNU_HWCAP, "NT_GNU_HWCAP (DSO-supplied software HWCAP info)"}, + {ELF::NT_GNU_BUILD_ID, "NT_GNU_BUILD_ID (unique build ID bitstring)"}, + {ELF::NT_GNU_GOLD_VERSION, "NT_GNU_GOLD_VERSION (gold version)"}, + }; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return std::string(Note.Name); + + std::string string; + raw_string_ostream OS(string); + OS << format("Unknown note type (0x%08x)", NT); + return string; +} + +template <typename ELFT> +static void printGNUNote(raw_ostream &OS, uint32_t NoteType, + ArrayRef<typename ELFFile<ELFT>::Elf_Word> Words) { + switch (NoteType) { + default: + return; + case ELF::NT_GNU_ABI_TAG: { + static const char *OSNames[] = { + "Linux", "Hurd", "Solaris", "FreeBSD", "NetBSD", "Syllable", "NaCl", + }; + + StringRef OSName = "Unknown"; + if (Words[0] < array_lengthof(OSNames)) + OSName = OSNames[Words[0]]; + uint32_t Major = Words[1], Minor = Words[2], Patch = Words[3]; + + if (Words.size() < 4) + OS << " <corrupt GNU_ABI_TAG>"; + else + OS << " OS: " << OSName << ", ABI: " << Major << "." << Minor << "." + << Patch; + break; + } + case ELF::NT_GNU_BUILD_ID: { + OS << " Build ID: "; + ArrayRef<uint8_t> ID(reinterpret_cast<const uint8_t *>(Words.data()), + Words.size() * 4); + for (const auto &B : ID) + OS << format_hex_no_prefix(B, 2); + break; + } + case ELF::NT_GNU_GOLD_VERSION: + OS << " Version: " + << StringRef(reinterpret_cast<const char *>(Words.data()), + Words.size() * 4); + break; + } + + OS << '\n'; +} + +template <class ELFT> +void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { + const Elf_Ehdr *e = Obj->getHeader(); + bool IsCore = e->e_type == ELF::ET_CORE; + + auto process = [&](const typename ELFFile<ELFT>::Elf_Off Offset, + const typename ELFFile<ELFT>::Elf_Addr Size) { + using Word = typename ELFFile<ELFT>::Elf_Word; + + if (Size <= 0) + return; + + const auto *P = static_cast<const uint8_t *>(Obj->base() + Offset); + const auto *E = P + Size; + + OS << "Displaying notes found at file offset " << format_hex(Offset, 10) + << " with length " << format_hex(Size, 10) << ":\n" + << " Owner Data size\tDescription\n"; + + while (P < E) { + const Word *Words = reinterpret_cast<const Word *>(&P[0]); + + uint32_t NameSize = Words[0]; + uint32_t DescriptorSize = Words[1]; + uint32_t Type = Words[2]; + + ArrayRef<Word> Descriptor(&Words[3 + (alignTo<4>(NameSize) / 4)], + alignTo<4>(DescriptorSize) / 4); + + StringRef Name; + if (NameSize) + Name = + StringRef(reinterpret_cast<const char *>(&Words[3]), NameSize - 1); + + OS << " " << Name << std::string(22 - NameSize, ' ') + << format_hex(DescriptorSize, 10) << '\t'; + + if (Name == "GNU") { + OS << getGNUNoteTypeName(Type) << '\n'; + printGNUNote<ELFT>(OS, Type, Descriptor); + } + OS << '\n'; + + P = P + 3 * sizeof(Word) * alignTo<4>(NameSize) + + alignTo<4>(DescriptorSize); + } + }; + + if (IsCore) { + for (const auto &P : Obj->program_headers()) + if (P.p_type == PT_NOTE) + process(P.p_offset, P.p_filesz); + } else { + for (const auto &S : Obj->sections()) + if (S.sh_type == SHT_NOTE) + process(S.sh_offset, S.sh_size); + } +} + template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) { const Elf_Ehdr *e = Obj->getHeader(); { @@ -3526,7 +3657,14 @@ void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { W.printNumber("Alignment", Phdr.p_align); } } + template <class ELFT> void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { W.startLine() << "Hash Histogram not implemented!\n"; } + +template <class ELFT> +void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { + W.startLine() << "printNotes not implemented!\n"; +} + diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index a39fc260b54..9368cc562fe 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -47,6 +47,7 @@ public: virtual void printVersionInfo() {} virtual void printGroupSections() {} virtual void printHashHistogram() {} + virtual void printNotes() {} // Only implemented for ARM ELF at this time. virtual void printAttributes() { } diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index d811ea995ba..53d6782a671 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -92,6 +92,10 @@ namespace opts { cl::desc("Alias for --relocations"), cl::aliasopt(Relocations)); + // -notes, -n + cl::opt<bool> Notes("notes", cl::desc("Display the ELF notes in the file")); + cl::alias NotesShort("n", cl::desc("Alias for --notes"), cl::aliasopt(Notes)); + // -dyn-relocations cl::opt<bool> DynRelocs("dyn-relocations", cl::desc("Display the dynamic relocation entries in the file")); @@ -408,6 +412,8 @@ static void dumpObject(const ObjectFile *Obj) { Dumper->printGroupSections(); if (opts::HashHistogram) Dumper->printHashHistogram(); + if (opts::Notes) + Dumper->printNotes(); } if (Obj->isCOFF()) { if (opts::COFFImports) |