[llvm-branch-commits] [llvm] [llvm-readobj, ELF] Support reading binary with more than PN_XNUM segments. (PR #165278)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Oct 27 09:40:29 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-binary-utilities
Author: None (aokblast)
<details>
<summary>Changes</summary>
FreeBSD coredump uses program headers to store mmap information. It is possible for program to use more than PN_XNUM mmaps. Therefore, we implement the support of PN_XNUM in readelf.
---
Full diff: https://github.com/llvm/llvm-project/pull/165278.diff
4 Files Affected:
- (added) llvm/test/tools/llvm-readobj/ELF/Inputs/many-segments.o.gz ()
- (added) llvm/test/tools/llvm-readobj/ELF/invalid-e_phnum.test (+39)
- (added) llvm/test/tools/llvm-readobj/ELF/many-segments.test (+79)
- (modified) llvm/tools/llvm-readobj/ELFDumper.cpp (+50-22)
``````````diff
diff --git a/llvm/test/tools/llvm-readobj/ELF/Inputs/many-segments.o.gz b/llvm/test/tools/llvm-readobj/ELF/Inputs/many-segments.o.gz
new file mode 100644
index 0000000000000..0709ed1d6389e
Binary files /dev/null and b/llvm/test/tools/llvm-readobj/ELF/Inputs/many-segments.o.gz differ
diff --git a/llvm/test/tools/llvm-readobj/ELF/invalid-e_phnum.test b/llvm/test/tools/llvm-readobj/ELF/invalid-e_phnum.test
new file mode 100644
index 0000000000000..a174742af7192
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/invalid-e_phnum.test
@@ -0,0 +1,39 @@
+# RUN: yaml2obj --docnum=1 %s -o %t.o
+
+# RUN: llvm-readobj --headers %t.o 2>&1 | FileCheck %s --check-prefix=CASE-INVALID
+
+# CASE-INVALID: SectionHeaderOffset: 0
+# CASE-INVALID: ProgramHeaderCount: 65535 (corrupt)
+# CASE-INVALID: unable to dump program headers: program headers are longer than binary of size 336: e_phoff = 0x40, e_phnum = 65535, e_phentsize = 56
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+ EPhNum: 65535
+ EShOff: 0
+ProgramHeaders:
+ - Type: PT_LOAD
+
+# RUN: yaml2obj --docnum=2 %s -o %t2.o
+
+# RUN: llvm-readobj --headers %t2.o 2>&1 | FileCheck %s --check-prefix=CASE-VALID
+
+# CASE-VALID: SectionHeaderOffset: 0
+# CASE-VALID: ProgramHeaderCount: 65535 (65536)
+# CASE-VALID: unable to dump program headers: program headers are longer than binary of size 336: e_phoff = 0x40, e_phnum = 65536, e_phentsize = 56
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+ EPhNum: 65535
+Sections:
+ - Type: SHT_NULL
+ Info: 65536
+ProgramHeaders:
+ - Type: PT_LOAD
diff --git a/llvm/test/tools/llvm-readobj/ELF/many-segments.test b/llvm/test/tools/llvm-readobj/ELF/many-segments.test
new file mode 100644
index 0000000000000..2f154ddf53899
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/many-segments.test
@@ -0,0 +1,79 @@
+## Show that llvm-readelf can handle an input file with many segments.
+
+RUN: %python %p/../../llvm-objcopy/Inputs/ungzip.py %p/Inputs/many-segments.o.gz > %t
+RUN: llvm-readobj --file-headers --sections --segments %t | FileCheck %s
+RUN: llvm-readelf --segments %t | FileCheck --check-prefix=SYMS %s
+
+## The ELF header should have e_phnum == PN_XNUM
+# CHECK: ProgramHeaderCount: 65535 (66549)
+## The first section header should store the real program header count in its fields.
+# CHECK: Section {
+# CHECK-NEXT: Index: 0
+# CHECK-NEXT: Name:
+# CHECK-NEXT: Type: SHT_NULL
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info: 66549
+
+## Show that the symbols with segments indexes around the reserved range still
+## have the right segment indexes afterwards.
+# 65535th segment
+# CHECK: Offset: 0x1183B000
+# CHECK-NEXT: VirtualAddress: 0x349139F3000
+# CHECK: }
+# CHECK-NEXT ProgramHeader {
+# CHECK-NEXT Type: PT_LOAD (0x1)
+# CHECK-NEXT Offset: 0x1183C000
+# CHECK-NEXT VirtualAddress: 0x349139F4000
+# CHECK-NEXT PhysicalAddress: 0x0
+# CHECK-NEXT FileSize: 4096
+# CHECK-NEXT MemSize: 4096
+# CHECK-NEXT Flags [ (0x4)
+# CHECK-NEXT PF_R (0x4)
+# CHECK-NEXT ]
+# CHECK-NEXT Alignment: 4096
+# CHECK-NEXT }
+# CHECK-NEXT ProgramHeader {
+# CHECK-NEXT Type: PT_LOAD (0x1)
+# CHECK-NEXT Offset: 0x1183D000
+# CHECK-NEXT VirtualAddress: 0x349139F5000
+# CHECK-NEXT PhysicalAddress: 0x0
+# CHECK-NEXT FileSize: 4096
+# CHECK-NEXT MemSize: 4096
+# CHECK-NEXT Flags [ (0x6)
+# CHECK-NEXT PF_R (0x4)
+# CHECK-NEXT PF_W (0x2)
+# CHECK-NEXT ]
+# CHECK-NEXT Alignment: 4096
+# CHECK-NEXT }
+# CHECK-NEXT ProgramHeader {
+# CHECK-NEXT Type: PT_LOAD (0x1)
+# CHECK-NEXT Offset: 0x1183E000
+# CHECK-NEXT VirtualAddress: 0x349139F6000
+# CHECK-NEXT PhysicalAddress: 0x0
+# CHECK-NEXT FileSize: 4096
+# CHECK-NEXT MemSize: 4096
+# CHECK-NEXT Flags [ (0x4)
+# CHECK-NEXT PF_R (0x4)
+# CHECK-NEXT ]
+# CHECK-NEXT Alignment: 4096
+# CHECK-NEXT }
+# CHECK ProgramHeader {
+# CHECK-NEXT Type: PT_LOAD (0x1)
+# CHECK-NEXT Offset: 0x11C31000
+# CHECK-NEXT VirtualAddress: 0x30D8E7868000
+# CHECK-NEXT PhysicalAddress: 0x0
+# CHECK-NEXT FileSize: 8192
+# CHECK-NEXT MemSize: 8192
+# CHECK-NEXT Flags [ (0x6)
+# CHECK-NEXT PF_R (0x4)
+# CHECK-NEXT PF_W (0x2)
+# CHECK-NEXT ]
+# CHECK-NEXT Alignment: 4096
+# CHECK-NEXT }
+
+# SYMS: There are 66549 program headers, starting at offset 64
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 9c9b2dd79e686..22431c4396ca5 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -3572,45 +3572,62 @@ static inline void printFields(formatted_raw_ostream &OS, StringRef Str1,
OS.flush();
}
+template <class ELFT>
+static std::string getProgramHeadersNumString(const ELFFile<ELFT> &Obj,
+ StringRef FileName) {
+
+ if (Obj.getHeader().e_phnum != ELF::PN_XNUM)
+ return to_string(Obj.getHeader().e_phnum);
+
+ Expected<uint32_t> PhNumOrErr = Obj.getPhNum();
+ if (!PhNumOrErr) {
+ // In this case we can ignore an error, because we have already reported a
+ // warning about the broken section header table earlier.
+ consumeError(PhNumOrErr.takeError());
+ return "<?>";
+ }
+
+ if (*PhNumOrErr == ELF::PN_XNUM)
+ return "65535 (corrupt)";
+ return "65535 (" + to_string(*PhNumOrErr) + ")";
+}
+
template <class ELFT>
static std::string getSectionHeadersNumString(const ELFFile<ELFT> &Obj,
StringRef FileName) {
- const typename ELFT::Ehdr &ElfHeader = Obj.getHeader();
- if (ElfHeader.e_shnum != 0)
- return to_string(ElfHeader.e_shnum);
+ if (Obj.getHeader().e_shnum != 0)
+ return to_string(Obj.getHeader().e_shnum);
- Expected<ArrayRef<typename ELFT::Shdr>> ArrOrErr = Obj.sections();
- if (!ArrOrErr) {
+ Expected<uint64_t> ShNumOrErr = Obj.getShNum();
+ if (!ShNumOrErr) {
// In this case we can ignore an error, because we have already reported a
// warning about the broken section header table earlier.
- consumeError(ArrOrErr.takeError());
+ consumeError(ShNumOrErr.takeError());
return "<?>";
}
- if (ArrOrErr->empty())
+ if (*ShNumOrErr == 0)
return "0";
- return "0 (" + to_string((*ArrOrErr)[0].sh_size) + ")";
+ return "0 (" + to_string(*ShNumOrErr) + ")";
}
template <class ELFT>
static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> &Obj,
StringRef FileName) {
- const typename ELFT::Ehdr &ElfHeader = Obj.getHeader();
- if (ElfHeader.e_shstrndx != SHN_XINDEX)
- return to_string(ElfHeader.e_shstrndx);
+ if (Obj.getHeader().e_shstrndx != ELF::SHN_XINDEX)
+ return to_string(Obj.getHeader().e_shstrndx);
- Expected<ArrayRef<typename ELFT::Shdr>> ArrOrErr = Obj.sections();
- if (!ArrOrErr) {
+ Expected<uint32_t> ShStrNdxOrErr = Obj.getShStrNdx();
+ if (!ShStrNdxOrErr) {
// In this case we can ignore an error, because we have already reported a
// warning about the broken section header table earlier.
- consumeError(ArrOrErr.takeError());
+ consumeError(ShStrNdxOrErr.takeError());
return "<?>";
}
- if (ArrOrErr->empty())
+ if (*ShStrNdxOrErr == ELF::SHN_XINDEX)
return "65535 (corrupt: out of range)";
- return to_string(ElfHeader.e_shstrndx) + " (" +
- to_string((*ArrOrErr)[0].sh_link) + ")";
+ return "65535 (" + to_string(*ShStrNdxOrErr) + ")";
}
static const EnumEntry<unsigned> *getObjectFileEnumEntry(unsigned Type) {
@@ -3765,7 +3782,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printFileHeaders() {
printFields(OS, "Size of this header:", Str);
Str = to_string(e.e_phentsize) + " (bytes)";
printFields(OS, "Size of program headers:", Str);
- Str = to_string(e.e_phnum);
+ Str = getProgramHeadersNumString(this->Obj, this->FileName);
printFields(OS, "Number of program headers:", Str);
Str = to_string(e.e_shentsize) + " (bytes)";
printFields(OS, "Size of section headers:", Str);
@@ -4778,8 +4795,10 @@ void GNUELFDumper<ELFT>::printProgramHeaders(
return;
if (PrintProgramHeaders) {
- const Elf_Ehdr &Header = this->Obj.getHeader();
- if (Header.e_phnum == 0) {
+ Expected<uint32_t> PhNumOrErr = this->Obj.getPhNum();
+ if (!PhNumOrErr) {
+ OS << '\n' << errorToErrorCode(PhNumOrErr.takeError()).message() << '\n';
+ } else if (*PhNumOrErr == 0) {
OS << "\nThere are no program headers in this file.\n";
} else {
printProgramHeaders();
@@ -4795,10 +4814,18 @@ template <class ELFT> void GNUELFDumper<ELFT>::printProgramHeaders() {
const Elf_Ehdr &Header = this->Obj.getHeader();
Field Fields[8] = {2, 17, 26, 37 + Bias,
48 + Bias, 56 + Bias, 64 + Bias, 68 + Bias};
+ uint32_t PhNum;
+ if (Expected<uint32_t> PhNumOrErr = this->Obj.getPhNum())
+ PhNum = *PhNumOrErr;
+ else {
+ OS << '\n' << errorToErrorCode(PhNumOrErr.takeError()).message() << '\n';
+ return;
+ }
+
OS << "\nElf file type is "
<< enumToString(Header.e_type, ArrayRef(ElfObjectFileType)) << "\n"
<< "Entry point " << format_hex(Header.e_entry, 3) << "\n"
- << "There are " << Header.e_phnum << " program headers,"
+ << "There are " << PhNum << " program headers,"
<< " starting at offset " << Header.e_phoff << "\n\n"
<< "Program Headers:\n";
if (ELFT::Is64Bits)
@@ -7470,7 +7497,8 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printFileHeaders() {
W.printFlags("Flags", E.e_flags);
W.printNumber("HeaderSize", E.e_ehsize);
W.printNumber("ProgramHeaderEntrySize", E.e_phentsize);
- W.printNumber("ProgramHeaderCount", E.e_phnum);
+ W.printString("ProgramHeaderCount",
+ getProgramHeadersNumString(this->Obj, this->FileName));
W.printNumber("SectionHeaderEntrySize", E.e_shentsize);
W.printString("SectionHeaderCount",
getSectionHeadersNumString(this->Obj, this->FileName));
``````````
</details>
https://github.com/llvm/llvm-project/pull/165278
More information about the llvm-branch-commits
mailing list