Skip to content

Commit

Permalink
llvm-readobj: add support for printing GNU Notes
Browse files Browse the repository at this point in the history
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
  • Loading branch information
compnerd committed Aug 30, 2016
1 parent a2a31ca commit b899d19
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 0 deletions.
76 changes: 76 additions & 0 deletions test/tools/llvm-readobj/gnu-notes.test
Original file line number Diff line number Diff line change
@@ -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
...
138 changes: 138 additions & 0 deletions tools/llvm-readobj/ELFDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class ELFDumper : public ObjDumper {

void printHashHistogram() override;

void printNotes() override;

private:
std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle;
typedef ELFFile<ELFT> ELFO;
Expand Down Expand Up @@ -292,6 +294,7 @@ template <typename ELFT> class DumpStyle {
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;
Expand All @@ -314,6 +317,7 @@ template <typename ELFT> class GNUStyle : public DumpStyle<ELFT> {
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 {
Expand Down Expand Up @@ -367,6 +371,7 @@ template <typename ELFT> class LLVMStyle : public DumpStyle<ELFT> {
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);
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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();
{
Expand Down Expand Up @@ -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";
}

1 change: 1 addition & 0 deletions tools/llvm-readobj/ObjDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class ObjDumper {
virtual void printVersionInfo() {}
virtual void printGroupSections() {}
virtual void printHashHistogram() {}
virtual void printNotes() {}

// Only implemented for ARM ELF at this time.
virtual void printAttributes() { }
Expand Down
6 changes: 6 additions & 0 deletions tools/llvm-readobj/llvm-readobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit b899d19

Please sign in to comment.