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.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280131 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Saleem Abdulrasool 2016-08-30 18:52:02 +00:00
parent a2a31ca57f
commit b899d1987d
4 changed files with 221 additions and 0 deletions

View File

@ -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
...

View File

@ -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";
}

View File

@ -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() { }

View File

@ -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)