From 6a405448f396afda39e2ed9054c492f5943acd8f Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 30 Aug 2016 18:52:02 +0000 Subject: [PATCH] llvm-readobj: add support for printing GNU Notes Add support for printing the GNU Notes. This allows an easy way to view the build id for a binary built with the build id. Currently, this only handles the GNU notes, though it would be easy to extend for other note types (default, FreeBSD, NetBSD, etc). Only the GNU style is supported currently. llvm-svn: 280131 --- llvm/test/tools/llvm-readobj/gnu-notes.test | 76 +++++++++++++++ llvm/tools/llvm-readobj/ELFDumper.cpp | 138 ++++++++++++++++++++++++++++ llvm/tools/llvm-readobj/ObjDumper.h | 1 + llvm/tools/llvm-readobj/llvm-readobj.cpp | 6 ++ 4 files changed, 221 insertions(+) create mode 100644 llvm/test/tools/llvm-readobj/gnu-notes.test 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 0000000..1a9c7e3 --- /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 1ed1093..7e3394b 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> ELFDumperStyle; typedef ELFFile ELFO; @@ -292,6 +294,7 @@ public: bool IsDynamic) = 0; virtual void printProgramHeaders(const ELFFile *Obj) = 0; virtual void printHashHistogram(const ELFFile *Obj) = 0; + virtual void printNotes(const ELFFile *Obj) = 0; const ELFDumper *dumper() const { return Dumper; } private: const ELFDumper *Dumper; @@ -314,6 +317,7 @@ public: size_t Offset) override; void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile *Obj) override; + void printNotes(const ELFFile *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 *Obj) override; + void printNotes(const ELFFile *Obj) override; private: void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab); @@ -1491,6 +1496,11 @@ void ELFDumper::printDynamicSymbols() { template void ELFDumper::printHashHistogram() { ELFDumperStyle->printHashHistogram(Obj); } + +template void ELFDumper::printNotes() { + ELFDumperStyle->printNotes(Obj); +} + #define LLVM_READOBJ_TYPE_CASE(name) \ case DT_##name: return #name @@ -3161,6 +3171,127 @@ void GNUStyle::printHashHistogram(const ELFFile *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 +static void printGNUNote(raw_ostream &OS, uint32_t NoteType, + ArrayRef::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 << " "; + else + OS << " OS: " << OSName << ", ABI: " << Major << "." << Minor << "." + << Patch; + break; + } + case ELF::NT_GNU_BUILD_ID: { + OS << " Build ID: "; + ArrayRef ID(reinterpret_cast(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(Words.data()), + Words.size() * 4); + break; + } + + OS << '\n'; +} + +template +void GNUStyle::printNotes(const ELFFile *Obj) { + const Elf_Ehdr *e = Obj->getHeader(); + bool IsCore = e->e_type == ELF::ET_CORE; + + auto process = [&](const typename ELFFile::Elf_Off Offset, + const typename ELFFile::Elf_Addr Size) { + using Word = typename ELFFile::Elf_Word; + + if (Size <= 0) + return; + + const auto *P = static_cast(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(&P[0]); + + uint32_t NameSize = Words[0]; + uint32_t DescriptorSize = Words[1]; + uint32_t Type = Words[2]; + + ArrayRef Descriptor(&Words[3 + (alignTo<4>(NameSize) / 4)], + alignTo<4>(DescriptorSize) / 4); + + StringRef Name; + if (NameSize) + Name = + StringRef(reinterpret_cast(&Words[3]), NameSize - 1); + + OS << " " << Name << std::string(22 - NameSize, ' ') + << format_hex(DescriptorSize, 10) << '\t'; + + if (Name == "GNU") { + OS << getGNUNoteTypeName(Type) << '\n'; + printGNUNote(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 void LLVMStyle::printFileHeaders(const ELFO *Obj) { const Elf_Ehdr *e = Obj->getHeader(); { @@ -3526,7 +3657,14 @@ void LLVMStyle::printProgramHeaders(const ELFO *Obj) { W.printNumber("Alignment", Phdr.p_align); } } + template void LLVMStyle::printHashHistogram(const ELFFile *Obj) { W.startLine() << "Hash Histogram not implemented!\n"; } + +template +void LLVMStyle::printNotes(const ELFFile *Obj) { + W.startLine() << "printNotes not implemented!\n"; +} + diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index a39fc26..9368cc5 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 d811ea9..53d6782 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 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 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) -- 2.7.4