diff --git a/llvm/test/tools/llvm-readobj/note-core-ntfile-bad.test b/llvm/test/tools/llvm-readobj/note-core-ntfile-bad.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/note-core-ntfile-bad.test @@ -0,0 +1,145 @@ +## Test that malformed NT_FILE sections in core files are gracefully ignored. + +## llvm-mc doesn't support generating ET_CORE files; the following test cases +## were generated with the following steps: +# $ llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu tmp.s -o tmp.o +# $ llvm-readobj --section-data --sections tmp.o +# using the assembly shown with each test case. + +# RUN: yaml2obj --docnum=1 %s -o %t1.o +# RUN: llvm-readelf -n %t1.o 2>&1 | FileCheck %s --check-prefix=ERR-HEADER-SHORT +# ERR-HEADER-SHORT: warning: malformed note: header too short + +# .section ".note.foo", "a" +# .align 4 +# .long 5 /* namesz */ +# .long end - begin /* descsz */ +# .long 0x46494c45 /* type = NT_FILE */ +# .asciz "CORE" +# .align 4 +# begin: +# .quad 0 /* no file mappings */ +# end: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_CORE + Machine: EM_X86_64 +Sections: + - Name: .note.foo + Type: SHT_NOTE + Content: 0500000008000000454C4946434F5245000000000000000000000000 +ProgramHeaders: + - Type: PT_NOTE + Sections: + - Section: .note.foo + +# RUN: yaml2obj --docnum=2 %s -o %t2.o +# RUN: llvm-readelf -n %t2.o 2>&1 | FileCheck %s --check-prefix=ERR-NULL-TERM +# ERR-NULL-TERM: warning: malformed note: not NUL terminated + +# .section ".note.foo", "a" +# .align 4 +# .long 5 /* namesz */ +# .long end - begin /* descsz */ +# .long 0x46494c45 /* type = NT_FILE */ +# .asciz "CORE" +# .align 4 +# begin: +# .quad 1 /* 1 file mapping */ +# .quad 4096 /* page size */ +# .quad 0x1000 /* start #1 */ +# .quad 0x2000 /* end #1 */ +# .quad 0x3000 /* offset #1 */ +# .ascii "xxxx" +# end: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_CORE + Machine: EM_X86_64 +Sections: + - Name: .note.foo + Type: SHT_NOTE + Content: 050000002C000000454C4946434F5245000000000100000000000000001000000000000000100000000000000020000000000000003000000000000078787878 +ProgramHeaders: + - Type: PT_NOTE + Sections: + - Section: .note.foo + +# RUN: yaml2obj --docnum=3 %s -o %t3.o +# RUN: llvm-readelf -n %t3.o 2>&1 | FileCheck %s --check-prefix=ERR-FILE-COUNT +# ERR-FILE-COUNT: warning: malformed note: too short for number of files + +# .section ".note.foo", "a" +# .align 4 +# .long 5 /* namesz */ +# .long end - begin /* descsz */ +# .long 0x46494c45 /* type = NT_FILE */ +# .asciz "CORE" +# .align 4 +# begin: +# .quad 2 /* 2 file mappings */ +# .quad 4096 /* page size */ +# .quad 0x1000 /* start #1 */ +# .quad 0x2000 /* end #1 */ +# .quad 0x3000 /* offset #1 */ +# .asciz "xyz" +# end: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_CORE + Machine: EM_X86_64 +Sections: + - Name: .note.foo + Type: SHT_NOTE + Content: 050000002C000000454C4946434F5245000000000200000000000000001000000000000000100000000000000020000000000000003000000000000078797A00 +ProgramHeaders: + - Type: PT_NOTE + Sections: + - Section: .note.foo + +# RUN: yaml2obj --docnum=4 %s -o %t4.o +# RUN: llvm-readelf -n %t4.o 2>&1 | FileCheck %s --check-prefix=ERR-FILE-END-EARLY +# ERR-FILE-END-EARLY: warning: malformed note: too few filenames + +# .section ".note.foo", "a" +# .align 4 +# .long 5 /* namesz */ +# .long end - begin /* descsz */ +# .long 0x46494c45 /* type = NT_FILE */ +# .asciz "CORE" +# .align 4 +# begin: +# .quad 2 /* 2 file mappings */ +# .quad 4096 /* page size */ +# .quad 0x1000 /* start #1 */ +# .quad 0x2000 /* end #1 */ +# .quad 0x3000 /* offset #1 */ +# .quad 0x4000 /* start #2 */ +# .quad 0x5000 /* end #2 */ +# .quad 0x6000 /* offset #2 */ +# .asciz "xyz" +# end: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_CORE + Machine: EM_X86_64 +Sections: + - Name: .note.foo + Type: SHT_NOTE + Content: 0500000044000000454C4946434F5245000000000200000000000000001000000000000000100000000000000020000000000000003000000000000000400000000000000050000000000000006000000000000078797A00 +ProgramHeaders: + - Type: PT_NOTE + Sections: + - Section: .note.foo diff --git a/llvm/test/tools/llvm-readobj/note-core-ntfile.test b/llvm/test/tools/llvm-readobj/note-core-ntfile.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/note-core-ntfile.test @@ -0,0 +1,94 @@ +## Test that NT_FILE sections in core files are interpreted correctly. + +# RUN: yaml2obj %s > %t.o +# RUN: llvm-readelf --notes %t.o | FileCheck %s --check-prefix=GNU +# RUN: llvm-readobj --notes %t.o | FileCheck %s --check-prefix=LLVM + +## llvm-mc doesn't support generating ET_CORE files; the following section data +## was generated with the following steps: +# $ llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu tmp.s -o tmp.o +# $ llvm-readobj --section-data --sections tmp.o +## Using the following input: +# .section ".note.foo", "a" +# .align 4 +# .long 5 /* namesz */ +# .long end - begin /* descsz */ +# .long 0x46494c45 /* type = NT_FILE */ +# .asciz "CORE" +# .align 4 +# begin: +# .quad 3 /* 3 file mappings */ +# .quad 4096 /* page size */ +# .quad 0x1000 /* start #1 */ +# .quad 0x2000 /* end #1 */ +# .quad 0x3000 /* offset #1 */ +# .quad 0x4000 /* start #2 */ +# .quad 0x5000 /* end #2 */ +# .quad 0x6000 /* offset #2 */ +# .quad 0x7000 /* start #3 */ +# .quad 0x8000 /* end #3 */ +# .quad 0x9000 /* offset #3 */ +# .asciz "/path/to/a.out" +# .asciz "/path/to/libc.so" +# .asciz "[stack]" +# .align 4 +# end: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_CORE + Machine: EM_X86_64 +Sections: + - Name: .note.foo + Type: SHT_NOTE + Content: 0500000080000000454C4946434F524500000000030000000000000000100000000000000010000000000000002000000000000000300000000000000040000000000000005000000000000000600000000000000070000000000000008000000000000000900000000000002F706174682F746F2F612E6F7574002F706174682F746F2F6C6962632E736F005B737461636B5D00 +ProgramHeaders: + - Type: PT_NOTE + Sections: + - Section: .note.foo + +# GNU: Displaying notes found +# GNU-NEXT: Owner Data size Description +# GNU-NEXT: CORE 0x00000080 NT_FILE (mapped files) +# GNU-NEXT: Page size: 4096 +# GNU-NEXT: Start End Page Offset +# GNU-NEXT: 0x0000000000001000 0x0000000000002000 0x0000000000003000 +# GNU-NEXT: /path/to/a.out +# GNU-NEXT: 0x0000000000004000 0x0000000000005000 0x0000000000006000 +# GNU-NEXT: /path/to/libc.so +# GNU-NEXT: 0x0000000000007000 0x0000000000008000 0x0000000000009000 +# GNU-NEXT: [stack] +# GNU-NOT: {{.}} + +# LLVM: Notes [ +# LLVM-NEXT: NoteSection { +# LLVM-NEXT: Offset: +# LLVM-NEXT: Size: +# LLVM-NEXT: Note { +# LLVM-NEXT: Owner: CORE +# LLVM-NEXT: Data size: 0x80 +# LLVM-NEXT: Type: NT_FILE (mapped files) +# LLVM-NEXT: Page Size: 4096 +# LLVM-NEXT: Mapping [ +# LLVM-NEXT: Start: 0x1000 +# LLVM-NEXT: End: 0x2000 +# LLVM-NEXT: Offset: 0x3000 +# LLVM-NEXT: Filename: /path/to/a.out +# LLVM-NEXT: ] +# LLVM-NEXT: Mapping [ +# LLVM-NEXT: Start: 0x4000 +# LLVM-NEXT: End: 0x5000 +# LLVM-NEXT: Offset: 0x6000 +# LLVM-NEXT: Filename: /path/to/libc.so +# LLVM-NEXT: ] +# LLVM-NEXT: Mapping [ +# LLVM-NEXT: Start: 0x7000 +# LLVM-NEXT: End: 0x8000 +# LLVM-NEXT: Offset: 0x9000 +# LLVM-NEXT: Filename: [stack] +# LLVM-NEXT: ] +# LLVM-NEXT: } +# LLVM-NEXT: } +# LLVM-NEXT: ] diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -4309,6 +4309,92 @@ } } +template struct CoreFileMapping { + using Elf_Addr = typename ELFT::Addr; + Elf_Addr Start, End, Offset; + StringRef Filename; +}; + +template struct CoreNote { + using Elf_Addr = typename ELFT::Addr; + Elf_Addr PageSize; + std::vector> Mappings; +}; + +template +static Expected> readCoreNote(ArrayRef Desc) { + using Elf_Addr = typename ELFT::Addr; + // Expected format of the NT_FILE note description: + // 1. # of file mappings (call it N) + // 2. Page size + // 3. N (start, end, offset) triples + // 4. N packed filenames (null delimited) + // Each field is an Elf_Addr, except for filenames which are char* strings. + + CoreNote Ret; + const int Bytes = ELFT::Is64Bits ? 8 : 4; + + if (Desc.size() < 2 * Bytes) + return createStringError(object_error::parse_failed, + "malformed note: header too short"); + + if (Desc.back() != 0) + return createStringError(object_error::parse_failed, + "malformed note: not NUL terminated"); + + auto ReadField = [&Desc]() -> Elf_Addr { + auto Field = reinterpret_cast(Desc.data()); + Desc = Desc.drop_front(Bytes); + return *Field; + }; + Elf_Addr FileCount = ReadField(); + Ret.PageSize = ReadField(); + + if (Desc.size() < 3 * FileCount * Bytes) + return createStringError(object_error::parse_failed, + "malformed note: too short for number of files"); + + ArrayRef Filenames = + makeArrayRef(Desc.data() + 3 * FileCount * Bytes, Desc.end()); + auto ReadFilename = [&Filenames]() -> StringRef { + StringRef Filename(reinterpret_cast(Filenames.data())); + // Advance by size() + 1 due to NUL delimiter. + Filenames = Filenames.drop_front(Filename.size() + 1); + return Filename; + }; + + Ret.Mappings.resize(FileCount); + for (CoreFileMapping &Mapping : Ret.Mappings) { + if (Filenames.empty()) + return createStringError(object_error::parse_failed, + "malformed note: too few filenames"); + + Mapping.Start = ReadField(); + Mapping.End = ReadField(); + Mapping.Offset = ReadField(); + Mapping.Filename = ReadFilename(); + } + + return Ret; +} + +template +static void printCoreNote(raw_ostream &OS, CoreNote &Note) { + // Length of "0x
" string. + const int FieldWidth = ELFT::Is64Bits ? 18 : 10; + + OS << " Page size: " << format_decimal(Note.PageSize, 0) << '\n'; + OS << " " << right_justify("Start", FieldWidth) << " " + << right_justify("End", FieldWidth) << " " + << right_justify("Page Offset", FieldWidth) << '\n'; + for (const CoreFileMapping &Mapping : Note.Mappings) { + OS << " " << format_hex(Mapping.Start, FieldWidth) << " " + << format_hex(Mapping.End, FieldWidth) << " " + << format_hex(Mapping.Offset, FieldWidth) << "\n " + << Mapping.Filename << '\n'; + } +} + template void GNUStyle::printNotes(const ELFFile *Obj) { auto PrintHeader = [&](const typename ELFT::Off Offset, @@ -4341,16 +4427,22 @@ const AMDGPUNote N = getAMDGPUNote(Type, Descriptor); if (!N.Type.empty()) OS << " " << N.Type << ":\n " << N.Value << '\n'; + } else if (Name == "CORE") { + OS << getCoreNoteTypeName(Type) << '\n'; + if (Type == ELF::NT_FILE) { + Expected> Note = readCoreNote(Descriptor); + if (Note) + printCoreNote(OS, *Note); + else + warn(Note.takeError()); + } } else { - StringRef NoteType = Obj->getHeader()->e_type == ELF::ET_CORE - ? getCoreNoteTypeName(Type) - : getGenericNoteTypeName(Type); + StringRef NoteType = getGenericNoteTypeName(Type); if (!NoteType.empty()) - OS << NoteType; + OS << NoteType << '\n'; else - OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; + OS << "Unknown note type: (" << format_hex(Type, 10) << ")\n"; } - OS << '\n'; }; if (Obj->getHeader()->e_type == ELF::ET_CORE) { @@ -5436,6 +5528,18 @@ } } +template +static void printCoreNoteLLVMStyle(CoreNote &Note, ScopedPrinter &W) { + W.printNumber("Page Size", Note.PageSize); + for (const CoreFileMapping &Mapping : Note.Mappings) { + ListScope D(W, "Mapping"); + W.printHex("Start", Mapping.Start); + W.printHex("End", Mapping.End); + W.printHex("Offset", Mapping.Offset); + W.printString("Filename", Mapping.Filename); + } +} + template void LLVMStyle::printNotes(const ELFFile *Obj) { ListScope L(W, "Notes"); @@ -5469,10 +5573,17 @@ const AMDGPUNote N = getAMDGPUNote(Type, Descriptor); if (!N.Type.empty()) W.printString(N.Type, N.Value); + } else if (Name == "CORE") { + W.printString("Type", getCoreNoteTypeName(Type)); + if (Type == ELF::NT_FILE) { + Expected> Note = readCoreNote(Descriptor); + if (Note) + printCoreNoteLLVMStyle(*Note, W); + else + warn(Note.takeError()); + } } else { - StringRef NoteType = Obj->getHeader()->e_type == ELF::ET_CORE - ? getCoreNoteTypeName(Type) - : getGenericNoteTypeName(Type); + StringRef NoteType = getGenericNoteTypeName(Type); if (!NoteType.empty()) W.printString("Type", NoteType); else