Index: test/tools/llvm-readobj/gnu-notes.test =================================================================== --- test/tools/llvm-readobj/gnu-notes.test +++ test/tools/llvm-readobj/gnu-notes.test @@ -1,15 +1,55 @@ # RUN: yaml2obj %s > %t.so -# RUN: llvm-readobj -elf-output-style GNU --notes %t.so | FileCheck %s +# RUN: llvm-readobj -elf-output-style GNU --notes %t.so | FileCheck %s --check-prefix=GNU +# RUN: llvm-readobj -elf-output-style LLVM --notes %t.so | FileCheck %s --check-prefix=LLVM -# 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 +# GNU: Displaying notes found at file offset 0x00000340 with length 0x00000020: +# GNU-NEXT: Owner Data size Description +# GNU-NEXT: GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) +# GNU-NEXT: OS: Linux, ABI: 2.6.32 -# 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 +# GNU: Displaying notes found at file offset 0x00000360 with length 0x00000020: +# GNU-NEXT: Owner Data size Description +# GNU-NEXT: GNU 0x00000010 NT_GNU_BUILD_ID (unique build ID bitstring) +# GNU-NEXT: Build ID: 4fcb712aa6387724a9f465a32cd8c14b + +# GNU: Displaying notes found at file offset 0x000003cc with length 0x0000001c: +# GNU-NEXT: Owner Data size Description +# GNU-NEXT: GNU 0x00000009 NT_GNU_GOLD_VERSION (gold version) +# GNU-NEXT: Version: gold 1.11 + +# LLVM: Notes [ +# LLVM-NEXT: NoteSection { +# LLVM-NEXT: Offset: 0x340 +# LLVM-NEXT: Size: 0x20 +# LLVM-NEXT: Note { +# LLVM-NEXT: Owner: GNU +# LLVM-NEXT: Data size: 0x10 +# LLVM-NEXT: Type: NT_GNU_ABI_TAG (ABI version tag) +# LLVM-NEXT: OS: Linux +# LLVM-NEXT: ABI: 2.6.32 +# LLVM-NEXT: } +# LLVM-NEXT: } +# LLVM-NEXT: NoteSection { +# LLVM-NEXT: Offset: 0x360 +# LLVM-NEXT: Size: 0x20 +# LLVM-NEXT: Note { +# LLVM-NEXT: Owner: GNU +# LLVM-NEXT: Data size: 0x10 +# LLVM-NEXT: Type: NT_GNU_BUILD_ID (unique build ID bitstring) +# LLVM-NEXT: Build ID: 4fcb712aa6387724a9f465a32cd8c14b +# LLVM-NEXT: } +# LLVM-NEXT: } +# LLVM-NEXT: NoteSection { +# LLVM-NEXT: Offset: 0x3CC +# LLVM-NEXT: Size: 0x1C +# LLVM-NEXT: Note { +# LLVM-NEXT: Owner: GNU +# LLVM-NEXT: Data size: 0x9 +# LLVM-NEXT: Type: NT_GNU_GOLD_VERSION (gold version) +# LLVM-NEXT: Version: gold 1.11 +# LLVM-NEXT: } +# LLVM-NEXT: } +# LLVM-NEXT: ] --- !ELF FileHeader: @@ -18,6 +58,10 @@ Type: ET_EXEC Machine: EM_X86_64 Sections: + - Name: .note.ABI-tag + Type: SHT_NOTE + AddressAlign: 0x0000000000000004 + Content: 040000001000000001000000474E550000000000020000000600000020000000 - Name: .note.gnu.build-id Type: SHT_NOTE Flags: [ SHF_ALLOC ] Index: test/tools/llvm-readobj/note-gnu-property.s =================================================================== --- test/tools/llvm-readobj/note-gnu-property.s +++ test/tools/llvm-readobj/note-gnu-property.s @@ -1,23 +1,51 @@ // REQUIRES: x86-registered-target // RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o %t -// RUN: llvm-readobj -elf-output-style GNU --notes %t | FileCheck %s +// RUN: llvm-readobj -elf-output-style GNU --notes %t | FileCheck %s --check-prefix=GNU +// RUN: llvm-readobj -elf-output-style LLVM --notes %t | FileCheck %s --check-prefix=LLVM -// CHECK: Displaying notes found at file offset 0x00000040 with length 0x000000b8: -// CHECK-NEXT: Owner Data size Description -// CHECK-NEXT: GNU 0x000000a8 NT_GNU_PROPERTY_TYPE_0 (property note) -// CHECK-NEXT: Properties: stack size: 0x100 -// CHECK-NEXT: stack size: 0x100 -// CHECK-NEXT: no copy on protected -// CHECK-NEXT: X86 features: SHSTK -// CHECK-NEXT: X86 features: IBT, SHSTK -// CHECK-NEXT: X86 features: none -// CHECK-NEXT: -// CHECK-NEXT: stack size: -// CHECK-NEXT: stack size: -// CHECK-NEXT: no copy on protected -// CHECK-NEXT: X86 features: -// CHECK-NEXT: X86 features: IBT, -// CHECK-NEXT: +// GNU: Displaying notes found at file offset 0x00000040 with length 0x000000b8: +// GNU-NEXT: Owner Data size Description +// GNU-NEXT: GNU 0x000000a8 NT_GNU_PROPERTY_TYPE_0 (property note) +// GNU-NEXT: Properties: stack size: 0x100 +// GNU-NEXT: stack size: 0x100 +// GNU-NEXT: no copy on protected +// GNU-NEXT: X86 features: SHSTK +// GNU-NEXT: X86 features: IBT, SHSTK +// GNU-NEXT: X86 features: none +// GNU-NEXT: +// GNU-NEXT: stack size: +// GNU-NEXT: stack size: +// GNU-NEXT: no copy on protected +// GNU-NEXT: X86 features: +// GNU-NEXT: X86 features: IBT, +// GNU-NEXT: + +// LLVM: Notes [ +// LLVM-NEXT: NoteSection { +// LLVM-NEXT: Offset: 0x40 +// LLVM-NEXT: Size: 0xB8 +// LLVM-NEXT: Note { +// LLVM-NEXT: Owner: GNU +// LLVM-NEXT: Data size: 0xA8 +// LLVM-NEXT: Type: NT_GNU_PROPERTY_TYPE_0 (property note) +// LLVM-NEXT: Property [ +// LLVM-NEXT: stack size: 0x100 +// LLVM-NEXT: stack size: 0x100 +// LLVM-NEXT: no copy on protected +// LLVM-NEXT: X86 features: SHSTK +// LLVM-NEXT: X86 features: IBT, SHSTK +// LLVM-NEXT: X86 features: none +// LLVM-NEXT: +// LLVM-NEXT: stack size: +// LLVM-NEXT: stack size: +// LLVM-NEXT: no copy on protected +// LLVM-NEXT: X86 features: +// LLVM-NEXT: X86 features: IBT, +// LLVM-NEXT: +// LLVM-NEXT: ] +// LLVM-NEXT: } +// LLVM-NEXT: } +// LLVM-NEXT: ] .section ".note.gnu.property", "a" .align 4 Index: test/tools/llvm-readobj/note-gnu-property2.s =================================================================== --- test/tools/llvm-readobj/note-gnu-property2.s +++ test/tools/llvm-readobj/note-gnu-property2.s @@ -1,11 +1,27 @@ // REQUIRES: x86-registered-target // RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o %t -// RUN: llvm-readobj -elf-output-style GNU --notes %t | FileCheck %s +// RUN: llvm-readobj -elf-output-style GNU --notes %t | FileCheck %s --check-prefix=GNU +// RUN: llvm-readobj -elf-output-style LLVM --notes %t | FileCheck %s --check-prefix=LLVM -// CHECK: Displaying notes found at file offset 0x00000040 with length 0x00000014: -// CHECK-NEXT: Owner Data size Description -// CHECK-NEXT: GNU 0x00000004 NT_GNU_PROPERTY_TYPE_0 (property note) -// CHECK-NEXT: Properties: +// GNU: Displaying notes found at file offset 0x00000040 with length 0x00000014: +// GNU-NEXT: Owner Data size Description +// GNU-NEXT: GNU 0x00000004 NT_GNU_PROPERTY_TYPE_0 (property note) +// GNU-NEXT: Properties: + +// LLVM: Notes [ +// LLVM-NEXT: NoteSection { +// LLVM-NEXT: Offset: 0x40 +// LLVM-NEXT: Size: 0x14 +// LLVM-NEXT: Note { +// LLVM-NEXT: Owner: GNU +// LLVM-NEXT: Data size: 0x4 +// LLVM-NEXT: Type: NT_GNU_PROPERTY_TYPE_0 (property note) +// LLVM-NEXT: Property [ +// LLVM-NEXT: +// LLVM-NEXT: ] +// LLVM-NEXT: } +// LLVM-NEXT: } +// LLVM-NEXT: ] // Section below is broken, check we report that. Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -3654,40 +3654,41 @@ } template -static void printGNUProperty(raw_ostream &OS, uint32_t Type, uint32_t DataSize, - ArrayRef Data) { +static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, + ArrayRef Data) { + std::string str; + raw_string_ostream OS(str); switch (Type) { default: - OS << format(" \n", Type); - return; + OS << format("", Type); + return OS.str(); case GNU_PROPERTY_STACK_SIZE: { - OS << " stack size: "; + OS << "stack size: "; if (DataSize == sizeof(typename ELFT::uint)) - OS << format("0x%llx\n", + OS << format("0x%llx", (uint64_t)(*(const typename ELFT::Addr *)Data.data())); else - OS << format("\n", DataSize); - break; + OS << format("", DataSize); + return OS.str(); } case GNU_PROPERTY_NO_COPY_ON_PROTECTED: - OS << " no copy on protected"; + OS << "no copy on protected"; if (DataSize) OS << format(" ", DataSize); - OS << "\n"; - break; + return OS.str(); case GNU_PROPERTY_X86_FEATURE_1_AND: - OS << " X86 features: "; + OS << "X86 features: "; if (DataSize != 4 && DataSize != 8) { - OS << format("\n", DataSize); - break; + OS << format("", DataSize); + return OS.str(); } uint64_t CFProtection = (DataSize == 4) ? support::endian::read32(Data.data()) : support::endian::read64(Data.data()); if (CFProtection == 0) { - OS << "none\n"; - break; + OS << "none"; + return OS.str(); } if (CFProtection & GNU_PROPERTY_X86_FEATURE_1_IBT) { OS << "IBT"; @@ -3703,105 +3704,143 @@ } if (CFProtection) OS << format("", CFProtection); - OS << "\n"; - break; + return OS.str(); } } template -static void printGNUNote(raw_ostream &OS, uint32_t NoteType, - ArrayRef Words, size_t Size) { +static SmallVector +getGNUPropertyList(ArrayRef Words, size_t Size) { using Elf_Word = typename ELFT::Word; + SmallVector Properties; + ArrayRef Arr(reinterpret_cast(Words.data()), Size); + while (Arr.size() >= 8) { + uint32_t Type = *reinterpret_cast(Arr.data()); + uint32_t DataSize = *reinterpret_cast(Arr.data() + 4); + Arr = Arr.drop_front(8); + + // Take padding size into account if present. + uint64_t PaddedSize = alignTo(DataSize, sizeof(typename ELFT::uint)); + std::string str; + raw_string_ostream OS(str); + if (Arr.size() < PaddedSize) { + OS << format("", Type, DataSize); + Properties.push_back(OS.str()); + break; + } + Properties.push_back( + getGNUProperty(Type, DataSize, Arr.take_front(PaddedSize))); + Arr = Arr.drop_front(PaddedSize); + } + + if (!Arr.empty()) + Properties.push_back(""); + + return Properties; +} + +struct GNUAbiTag { + std::string OSName; + std::string ABI; + bool IsValid; +}; + +template +static GNUAbiTag getGNUAbiTag(ArrayRef Words) { + if (Words.size() < 4) + return {"", "", /*IsValid=*/false}; + + 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]; + std::string str; + raw_string_ostream ABI(str); + ABI << Major << "." << Minor << "." << Patch; + return {OSName, ABI.str(), /*IsValid=*/true}; +} + +template +static std::string getGNUBuildId(ArrayRef Words, + size_t Size) { + std::string str; + raw_string_ostream OS(str); + ArrayRef ID(reinterpret_cast(Words.data()), Size); + for (const auto &B : ID) + OS << format_hex_no_prefix(B, 2); + return OS.str(); +} + +template +static StringRef getGNUGoldVersion(ArrayRef Words, + size_t Size) { + return StringRef(reinterpret_cast(Words.data()), Size); +} + +template +static void printGNUNote(raw_ostream &OS, uint32_t NoteType, + ArrayRef Words, size_t Size) { 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) + const GNUAbiTag &AbiTag = getGNUAbiTag(Words); + if (!AbiTag.IsValid) OS << " "; else - OS << " OS: " << OSName << ", ABI: " << Major << "." << Minor << "." - << Patch; + OS << " OS: " << AbiTag.OSName << ", ABI: " << AbiTag.ABI; break; } case ELF::NT_GNU_BUILD_ID: { - OS << " Build ID: "; - ArrayRef ID(reinterpret_cast(Words.data()), Size); - for (const auto &B : ID) - OS << format_hex_no_prefix(B, 2); + OS << " Build ID: " << getGNUBuildId(Words, Size); break; } case ELF::NT_GNU_GOLD_VERSION: - OS << " Version: " - << StringRef(reinterpret_cast(Words.data()), Size); + OS << " Version: " << getGNUGoldVersion(Words, Size); break; case ELF::NT_GNU_PROPERTY_TYPE_0: OS << " Properties:"; - - ArrayRef Arr(reinterpret_cast(Words.data()), - Size); - while (Arr.size() >= 8) { - uint32_t Type = *reinterpret_cast(Arr.data()); - uint32_t DataSize = *reinterpret_cast(Arr.data() + 4); - Arr = Arr.drop_front(8); - - // Take padding size into account if present. - uint64_t PaddedSize = alignTo(DataSize, sizeof(typename ELFT::uint)); - if (Arr.size() < PaddedSize) { - OS << format(" \n", Type, - DataSize); - break; - } - printGNUProperty(OS, Type, DataSize, Arr.take_front(PaddedSize)); - Arr = Arr.drop_front(PaddedSize); - } - - if (!Arr.empty()) - OS << " "; + for (const auto &Property : getGNUPropertyList(Words, Size)) + OS << " " << Property << "\n"; break; } OS << '\n'; } +struct AMDGPUNote { + std::string type; + std::string value; +}; + template -static void printAMDGPUNote(raw_ostream &OS, uint32_t NoteType, - ArrayRef Words, size_t Size) { +static AMDGPUNote getAMDGPUNote(uint32_t NoteType, + ArrayRef Words, + size_t Size) { switch (NoteType) { default: - return; - case ELF::NT_AMD_AMDGPU_HSA_METADATA: - OS << " HSA Metadata:\n" - << StringRef(reinterpret_cast(Words.data()), Size); - break; - case ELF::NT_AMD_AMDGPU_ISA: - OS << " ISA Version:\n" - << " " - << StringRef(reinterpret_cast(Words.data()), Size); - break; - case ELF::NT_AMD_AMDGPU_PAL_METADATA: - const uint32_t *PALMetadataBegin = reinterpret_cast(Words.data()); - const uint32_t *PALMetadataEnd = PALMetadataBegin + Size; - std::vector PALMetadata(PALMetadataBegin, PALMetadataEnd); - std::string PALMetadataString; - auto Error = AMDGPU::PALMD::toString(PALMetadata, PALMetadataString); - OS << " PAL Metadata:\n"; - if (Error) { - OS << " Invalid"; - return; - } - OS << PALMetadataString; - break; + return {"", ""}; + case ELF::NT_AMD_AMDGPU_HSA_METADATA: + return {"HSA Metadata", + std::string(reinterpret_cast(Words.data()), Size)}; + case ELF::NT_AMD_AMDGPU_ISA: + return {"ISA Version", + std::string(reinterpret_cast(Words.data()), Size)}; + case ELF::NT_AMD_AMDGPU_PAL_METADATA: + const uint32_t *PALMetadataBegin = + reinterpret_cast(Words.data()); + const uint32_t *PALMetadataEnd = PALMetadataBegin + Size; + std::vector PALMetadata(PALMetadataBegin, PALMetadataEnd); + std::string PALMetadataString; + auto Error = AMDGPU::PALMD::toString(PALMetadata, PALMetadataString); + if (Error) { + return {"PAL Metadata", "Invalid"}; + } + return {"PAL Metadata", PALMetadataString}; } - OS.flush(); } template @@ -3831,7 +3870,10 @@ OS << getFreeBSDNoteTypeName(Type) << '\n'; } else if (Name == "AMD") { OS << getAMDGPUNoteTypeName(Type) << '\n'; - printAMDGPUNote(OS, Type, Descriptor, Descriptor.size()); + const AMDGPUNote N = + getAMDGPUNote(Type, Descriptor, Descriptor.size()); + if (!N.type.empty()) + OS << " " << N.type << ":\n " << N.value << '\n'; } else { OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; } @@ -4435,9 +4477,99 @@ } } +template +static void printGNUNoteLLVMStyle(uint32_t NoteType, + ArrayRef Words, + size_t Size, ScopedPrinter &W) { + switch (NoteType) { + default: + return; + case ELF::NT_GNU_ABI_TAG: { + const GNUAbiTag &AbiTag = getGNUAbiTag(Words); + if (!AbiTag.IsValid) { + W.printString("ABI", ""); + } else { + W.printString("OS", AbiTag.OSName); + W.printString("ABI", AbiTag.ABI); + } + break; + } + case ELF::NT_GNU_BUILD_ID: { + W.printString("Build ID", getGNUBuildId(Words, Size)); + break; + } + case ELF::NT_GNU_GOLD_VERSION: + W.printString("Version", getGNUGoldVersion(Words, Size)); + break; + case ELF::NT_GNU_PROPERTY_TYPE_0: + ListScope D(W, "Property"); + for (const auto &Property : getGNUPropertyList(Words, Size)) + W.printString(Property); + break; + } +} + template void LLVMStyle::printNotes(const ELFFile *Obj) { - W.startLine() << "printNotes not implemented!\n"; + ListScope L(W, "Notes"); + const Elf_Ehdr *e = Obj->getHeader(); + bool IsCore = e->e_type == ELF::ET_CORE; + + auto PrintHeader = [&](const typename ELFT::Off Offset, + const typename ELFT::Addr Size) { + W.printHex("Offset", Offset); + W.printHex("Size", Size); + }; + + auto ProcessNote = [&](const Elf_Note &Note) { + DictScope D2(W, "Note"); + StringRef Name = Note.getName(); + ArrayRef Descriptor = Note.getDesc(); + Elf_Word Type = Note.getType(); + + W.printString("Owner", Name); + W.printHex("Data size", Descriptor.size()); + if (Name == "GNU") { + W.printString("Type", getGNUNoteTypeName(Type)); + printGNUNoteLLVMStyle(Type, Descriptor, Descriptor.size(), W); + } else if (Name == "FreeBSD") { + W.printString("Type", getFreeBSDNoteTypeName(Type)); + } else if (Name == "AMD") { + W.printString("Type", getAMDGPUNoteTypeName(Type)); + const AMDGPUNote N = + getAMDGPUNote(Type, Descriptor, Descriptor.size()); + if (!N.type.empty()) + W.printString(N.type, N.value); + } else { + W.getOStream() << "Unknown note type: (" << format_hex(Type, 10) << ')'; + } + }; + + if (IsCore) { + for (const auto &P : unwrapOrError(Obj->program_headers())) { + if (P.p_type != PT_NOTE) + continue; + DictScope D(W, "NoteSection"); + PrintHeader(P.p_offset, P.p_filesz); + Error Err = Error::success(); + for (const auto &Note : Obj->notes(P, Err)) + ProcessNote(Note); + if (Err) + error(std::move(Err)); + } + } else { + for (const auto &S : unwrapOrError(Obj->sections())) { + if (S.sh_type != SHT_NOTE) + continue; + DictScope D(W, "NoteSection"); + PrintHeader(S.sh_offset, S.sh_size); + Error Err = Error::success(); + for (const auto &Note : Obj->notes(S, Err)) + ProcessNote(Note); + if (Err) + error(std::move(Err)); + } + } } template