[llvm] [Object,ELF] Implement PN_XNUM extension for program headers (PR #162288)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Oct 20 09:49:36 PDT 2025
https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/162288
>From d2740bcf5c84d4f5d71e4058b25169034ec8f5e5 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 7 Oct 2025 21:46:52 +0800
Subject: [PATCH 1/9] [Object, ELF] Implement PN_XNUM extension for program
headers
In ELF file, there is a possible extended header for those phnum, shnum,
and shstrndx larger than the maximum of 16 bits. This extended header
use section 0 to record these fields in 32 bits. We implment this
feature so that programs rely on ELFFile::program_headers() can get the
correct number of segments. Also, the consumers don't have to check the
section 0 themselve, insteead, they can use the getPhNum() as an
alternative.
---
llvm/include/llvm/BinaryFormat/ELF.h | 2 +
llvm/include/llvm/Object/ELF.h | 60 +++++++++++++++++++---------
llvm/include/llvm/Object/ELFTypes.h | 5 +++
3 files changed, 48 insertions(+), 19 deletions(-)
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index e619b186dfe3d..136f8cfbde818 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1123,6 +1123,8 @@ struct Elf64_Shdr {
Elf64_Xword sh_entsize;
};
+enum { PN_XNUM = 0xffff };
+
// Special section indices.
enum {
SHN_UNDEF = 0, // Undefined, missing, irrelevant, or meaningless
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 59f63eb6b5bb6..3b96c0e2b9d1f 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -278,9 +278,16 @@ class ELFFile {
std::vector<Elf_Shdr> FakeSections;
SmallString<0> FakeSectionStrings;
+ Elf_Word RealPhNum;
+ Elf_Word RealShNum;
+ Elf_Word RealShStrNdx;
+
ELFFile(StringRef Object);
public:
+ Elf_Word getPhNum() const { return RealPhNum; }
+ Elf_Word getShNum() const { return RealShNum; }
+ Elf_Word getShStrNdx() const { return RealShStrNdx; }
const Elf_Ehdr &getHeader() const {
return *reinterpret_cast<const Elf_Ehdr *>(base());
}
@@ -379,22 +386,21 @@ class ELFFile {
/// Iterate over program header table.
Expected<Elf_Phdr_Range> program_headers() const {
- if (getHeader().e_phnum && getHeader().e_phentsize != sizeof(Elf_Phdr))
+ if (RealPhNum && getHeader().e_phentsize != sizeof(Elf_Phdr))
return createError("invalid e_phentsize: " +
Twine(getHeader().e_phentsize));
- uint64_t HeadersSize =
- (uint64_t)getHeader().e_phnum * getHeader().e_phentsize;
+ uint64_t HeadersSize = (uint64_t)RealPhNum * getHeader().e_phentsize;
uint64_t PhOff = getHeader().e_phoff;
if (PhOff + HeadersSize < PhOff || PhOff + HeadersSize > getBufSize())
return createError("program headers are longer than binary of size " +
Twine(getBufSize()) + ": e_phoff = 0x" +
Twine::utohexstr(getHeader().e_phoff) +
- ", e_phnum = " + Twine(getHeader().e_phnum) +
+ ", e_phnum = " + Twine(RealPhNum) +
", e_phentsize = " + Twine(getHeader().e_phentsize));
auto *Begin = reinterpret_cast<const Elf_Phdr *>(base() + PhOff);
- return ArrayRef(Begin, Begin + getHeader().e_phnum);
+ return ArrayRef(Begin, Begin + RealPhNum);
}
/// Get an iterator over notes in a program header.
@@ -772,18 +778,10 @@ template <class ELFT>
Expected<StringRef>
ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections,
WarningHandler WarnHandler) const {
- uint32_t Index = getHeader().e_shstrndx;
- if (Index == ELF::SHN_XINDEX) {
- // If the section name string table section index is greater than
- // or equal to SHN_LORESERVE, then the actual index of the section name
- // string table section is contained in the sh_link field of the section
- // header at index 0.
- if (Sections.empty())
- return createError(
- "e_shstrndx == SHN_XINDEX, but the section header table is empty");
-
- Index = Sections[0].sh_link;
- }
+ uint32_t Index = RealShStrNdx;
+ if (Index == ELF::SHN_XINDEX)
+ return createError(
+ "e_shstrndx == SHN_XINDEX, but the section header table is empty");
// There is no section name string table. Return FakeSectionStrings which
// is non-empty if we have created fake sections.
@@ -889,7 +887,31 @@ Expected<uint64_t> ELFFile<ELFT>::getDynSymtabSize() const {
return 0;
}
-template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {}
+template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {
+ const Elf_Ehdr &Header = getHeader();
+ RealPhNum = Header.e_phnum;
+ RealShNum = Header.e_shnum;
+ RealShStrNdx = Header.e_shstrndx;
+ if (!Header.hasPhdrNumExtension())
+ return;
+
+ // An ELF binary may report `hasExtendedHeader` as true but not actually
+ // include an extended header. For example, a core dump can contain 65,535
+ // segments but no sections at all. We defer reporting an error until section
+ // 0 is accessed. Consumers should handle and emit the error themselves when
+ // they attempt to access it.
+ auto SecOrErr = getSection(0);
+ if (!SecOrErr) {
+ consumeError(SecOrErr.takeError());
+ return;
+ }
+ if (RealPhNum == 0xFFFF)
+ RealPhNum = (*SecOrErr)->sh_info;
+ if (RealShNum == ELF::SHN_UNDEF)
+ RealShNum = (*SecOrErr)->sh_size;
+ if (RealShStrNdx == ELF::SHN_XINDEX)
+ RealShStrNdx = (*SecOrErr)->sh_link;
+}
template <class ELFT>
Expected<ELFFile<ELFT>> ELFFile<ELFT>::create(StringRef Object) {
@@ -956,7 +978,7 @@ Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::sections() const {
const Elf_Shdr *First =
reinterpret_cast<const Elf_Shdr *>(base() + SectionTableOffset);
- uintX_t NumSections = getHeader().e_shnum;
+ uintX_t NumSections = RealShNum;
if (NumSections == 0)
NumSections = First->sh_size;
diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index 5a26e2fc31458..b791f7486fe97 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -529,6 +529,11 @@ struct Elf_Ehdr_Impl {
unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; }
unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; }
+ bool hasPhdrNumExtension() const {
+ return (e_phnum == ELF::PN_XNUM || e_shnum == ELF::SHN_UNDEF ||
+ e_shstrndx == ELF::SHN_XINDEX) &&
+ e_shoff != 0;
+ }
};
template <endianness Endianness>
>From 6c38a39a7dc9bdf803fb301749fea5066c1c30a3 Mon Sep 17 00:00:00 2001
From: SHENG-YI HONG <aokblast at FreeBSD.org>
Date: Fri, 17 Oct 2025 17:45:39 +0800
Subject: [PATCH 2/9] fixup! [Object, ELF] Implement PN_XNUM extension for
program headers
---
llvm/include/llvm/Object/ELF.h | 49 +++++++++++++++++++---------------
1 file changed, 28 insertions(+), 21 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 3b96c0e2b9d1f..47bcc5dbe9899 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -278,16 +278,26 @@ class ELFFile {
std::vector<Elf_Shdr> FakeSections;
SmallString<0> FakeSectionStrings;
- Elf_Word RealPhNum;
- Elf_Word RealShNum;
- Elf_Word RealShStrNdx;
+ //
+ // According to the ELF gABI, these three fields can be recorded in section 0
+ // when possible. Therefore, we store this information when it is available.
+ //
+ std::optional<uint32_t> RealPhNum;
+ std::optional<uint32_t> RealShNum;
+ std::optional<uint32_t> RealShStrNdx;
ELFFile(StringRef Object);
public:
- Elf_Word getPhNum() const { return RealPhNum; }
- Elf_Word getShNum() const { return RealShNum; }
- Elf_Word getShStrNdx() const { return RealShStrNdx; }
+ uint32_t getPhNum() const {
+ return RealPhNum ? *RealPhNum : getHeader().e_phnum;
+ }
+ uint32_t getShNum() const {
+ return RealShNum ? *RealShNum : getHeader().e_shnum;
+ }
+ uint32_t getShStrNdx() const {
+ return RealShStrNdx ? *RealShStrNdx : getHeader().e_shstrndx;
+ }
const Elf_Ehdr &getHeader() const {
return *reinterpret_cast<const Elf_Ehdr *>(base());
}
@@ -386,21 +396,21 @@ class ELFFile {
/// Iterate over program header table.
Expected<Elf_Phdr_Range> program_headers() const {
- if (RealPhNum && getHeader().e_phentsize != sizeof(Elf_Phdr))
+ if (getPhNum() && getHeader().e_phentsize != sizeof(Elf_Phdr))
return createError("invalid e_phentsize: " +
Twine(getHeader().e_phentsize));
- uint64_t HeadersSize = (uint64_t)RealPhNum * getHeader().e_phentsize;
+ uint64_t HeadersSize = (uint64_t)getPhNum() * getHeader().e_phentsize;
uint64_t PhOff = getHeader().e_phoff;
if (PhOff + HeadersSize < PhOff || PhOff + HeadersSize > getBufSize())
return createError("program headers are longer than binary of size " +
Twine(getBufSize()) + ": e_phoff = 0x" +
Twine::utohexstr(getHeader().e_phoff) +
- ", e_phnum = " + Twine(RealPhNum) +
+ ", e_phnum = " + Twine(getPhNum()) +
", e_phentsize = " + Twine(getHeader().e_phentsize));
auto *Begin = reinterpret_cast<const Elf_Phdr *>(base() + PhOff);
- return ArrayRef(Begin, Begin + RealPhNum);
+ return ArrayRef(Begin, Begin + getPhNum());
}
/// Get an iterator over notes in a program header.
@@ -778,11 +788,11 @@ template <class ELFT>
Expected<StringRef>
ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections,
WarningHandler WarnHandler) const {
- uint32_t Index = RealShStrNdx;
- if (Index == ELF::SHN_XINDEX)
+ if (getHeader().e_shstrndx == ELF::SHN_XINDEX && !RealShStrNdx)
return createError(
"e_shstrndx == SHN_XINDEX, but the section header table is empty");
+ uint32_t Index = getShStrNdx();
// There is no section name string table. Return FakeSectionStrings which
// is non-empty if we have created fake sections.
if (!Index)
@@ -889,14 +899,11 @@ Expected<uint64_t> ELFFile<ELFT>::getDynSymtabSize() const {
template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {
const Elf_Ehdr &Header = getHeader();
- RealPhNum = Header.e_phnum;
- RealShNum = Header.e_shnum;
- RealShStrNdx = Header.e_shstrndx;
if (!Header.hasPhdrNumExtension())
return;
- // An ELF binary may report `hasExtendedHeader` as true but not actually
- // include an extended header. For example, a core dump can contain 65,535
+ // An ELF binary may report `hasPhdrNumExtension` as true but not actually
+ // include an section 0. For example, a core dump can contain 65,535
// segments but no sections at all. We defer reporting an error until section
// 0 is accessed. Consumers should handle and emit the error themselves when
// they attempt to access it.
@@ -905,11 +912,11 @@ template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {
consumeError(SecOrErr.takeError());
return;
}
- if (RealPhNum == 0xFFFF)
+ if (Header.e_phnum == 0xFFFF)
RealPhNum = (*SecOrErr)->sh_info;
- if (RealShNum == ELF::SHN_UNDEF)
+ if (Header.e_shnum == ELF::SHN_UNDEF)
RealShNum = (*SecOrErr)->sh_size;
- if (RealShStrNdx == ELF::SHN_XINDEX)
+ if (Header.e_shstrndx == ELF::SHN_XINDEX)
RealShStrNdx = (*SecOrErr)->sh_link;
}
@@ -978,7 +985,7 @@ Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::sections() const {
const Elf_Shdr *First =
reinterpret_cast<const Elf_Shdr *>(base() + SectionTableOffset);
- uintX_t NumSections = RealShNum;
+ uintX_t NumSections = getShNum();
if (NumSections == 0)
NumSections = First->sh_size;
>From eb602eea2ac559441b32bfd0921cd49fce8896f7 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 7 Oct 2025 21:46:52 +0800
Subject: [PATCH 3/9] [Object, ELF] Implement PN_XNUM extension for program
headers
In ELF file, there is a possible extended header for those phnum, shnum,
and shstrndx larger than the maximum of 16 bits. This extended header
use section 0 to record these fields in 32 bits. We implment this
feature so that programs rely on ELFFile::program_headers() can get the
correct number of segments. Also, the consumers don't have to check the
section 0 themselve, insteead, they can use the getPhNum() as an
alternative.
---
llvm/include/llvm/BinaryFormat/ELF.h | 2 +
llvm/include/llvm/Object/ELF.h | 60 +++++++++++++++++++---------
llvm/include/llvm/Object/ELFTypes.h | 5 +++
3 files changed, 48 insertions(+), 19 deletions(-)
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 8d0dc64199ebf..812618be0e918 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1125,6 +1125,8 @@ struct Elf64_Shdr {
Elf64_Xword sh_entsize;
};
+enum { PN_XNUM = 0xffff };
+
// Special section indices.
enum {
SHN_UNDEF = 0, // Undefined, missing, irrelevant, or meaningless
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 59f63eb6b5bb6..3b96c0e2b9d1f 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -278,9 +278,16 @@ class ELFFile {
std::vector<Elf_Shdr> FakeSections;
SmallString<0> FakeSectionStrings;
+ Elf_Word RealPhNum;
+ Elf_Word RealShNum;
+ Elf_Word RealShStrNdx;
+
ELFFile(StringRef Object);
public:
+ Elf_Word getPhNum() const { return RealPhNum; }
+ Elf_Word getShNum() const { return RealShNum; }
+ Elf_Word getShStrNdx() const { return RealShStrNdx; }
const Elf_Ehdr &getHeader() const {
return *reinterpret_cast<const Elf_Ehdr *>(base());
}
@@ -379,22 +386,21 @@ class ELFFile {
/// Iterate over program header table.
Expected<Elf_Phdr_Range> program_headers() const {
- if (getHeader().e_phnum && getHeader().e_phentsize != sizeof(Elf_Phdr))
+ if (RealPhNum && getHeader().e_phentsize != sizeof(Elf_Phdr))
return createError("invalid e_phentsize: " +
Twine(getHeader().e_phentsize));
- uint64_t HeadersSize =
- (uint64_t)getHeader().e_phnum * getHeader().e_phentsize;
+ uint64_t HeadersSize = (uint64_t)RealPhNum * getHeader().e_phentsize;
uint64_t PhOff = getHeader().e_phoff;
if (PhOff + HeadersSize < PhOff || PhOff + HeadersSize > getBufSize())
return createError("program headers are longer than binary of size " +
Twine(getBufSize()) + ": e_phoff = 0x" +
Twine::utohexstr(getHeader().e_phoff) +
- ", e_phnum = " + Twine(getHeader().e_phnum) +
+ ", e_phnum = " + Twine(RealPhNum) +
", e_phentsize = " + Twine(getHeader().e_phentsize));
auto *Begin = reinterpret_cast<const Elf_Phdr *>(base() + PhOff);
- return ArrayRef(Begin, Begin + getHeader().e_phnum);
+ return ArrayRef(Begin, Begin + RealPhNum);
}
/// Get an iterator over notes in a program header.
@@ -772,18 +778,10 @@ template <class ELFT>
Expected<StringRef>
ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections,
WarningHandler WarnHandler) const {
- uint32_t Index = getHeader().e_shstrndx;
- if (Index == ELF::SHN_XINDEX) {
- // If the section name string table section index is greater than
- // or equal to SHN_LORESERVE, then the actual index of the section name
- // string table section is contained in the sh_link field of the section
- // header at index 0.
- if (Sections.empty())
- return createError(
- "e_shstrndx == SHN_XINDEX, but the section header table is empty");
-
- Index = Sections[0].sh_link;
- }
+ uint32_t Index = RealShStrNdx;
+ if (Index == ELF::SHN_XINDEX)
+ return createError(
+ "e_shstrndx == SHN_XINDEX, but the section header table is empty");
// There is no section name string table. Return FakeSectionStrings which
// is non-empty if we have created fake sections.
@@ -889,7 +887,31 @@ Expected<uint64_t> ELFFile<ELFT>::getDynSymtabSize() const {
return 0;
}
-template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {}
+template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {
+ const Elf_Ehdr &Header = getHeader();
+ RealPhNum = Header.e_phnum;
+ RealShNum = Header.e_shnum;
+ RealShStrNdx = Header.e_shstrndx;
+ if (!Header.hasPhdrNumExtension())
+ return;
+
+ // An ELF binary may report `hasExtendedHeader` as true but not actually
+ // include an extended header. For example, a core dump can contain 65,535
+ // segments but no sections at all. We defer reporting an error until section
+ // 0 is accessed. Consumers should handle and emit the error themselves when
+ // they attempt to access it.
+ auto SecOrErr = getSection(0);
+ if (!SecOrErr) {
+ consumeError(SecOrErr.takeError());
+ return;
+ }
+ if (RealPhNum == 0xFFFF)
+ RealPhNum = (*SecOrErr)->sh_info;
+ if (RealShNum == ELF::SHN_UNDEF)
+ RealShNum = (*SecOrErr)->sh_size;
+ if (RealShStrNdx == ELF::SHN_XINDEX)
+ RealShStrNdx = (*SecOrErr)->sh_link;
+}
template <class ELFT>
Expected<ELFFile<ELFT>> ELFFile<ELFT>::create(StringRef Object) {
@@ -956,7 +978,7 @@ Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::sections() const {
const Elf_Shdr *First =
reinterpret_cast<const Elf_Shdr *>(base() + SectionTableOffset);
- uintX_t NumSections = getHeader().e_shnum;
+ uintX_t NumSections = RealShNum;
if (NumSections == 0)
NumSections = First->sh_size;
diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index e9a417d3d4fb3..77a68f95a46ce 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -529,6 +529,11 @@ struct Elf_Ehdr_Impl {
unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; }
unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; }
+ bool hasPhdrNumExtension() const {
+ return (e_phnum == ELF::PN_XNUM || e_shnum == ELF::SHN_UNDEF ||
+ e_shstrndx == ELF::SHN_XINDEX) &&
+ e_shoff != 0;
+ }
};
template <endianness Endianness>
>From c107bc3d0dc75eea0b9b2ef6a9dd3a41adaec3c2 Mon Sep 17 00:00:00 2001
From: SHENG-YI HONG <aokblast at FreeBSD.org>
Date: Fri, 17 Oct 2025 17:45:39 +0800
Subject: [PATCH 4/9] fixup! [Object, ELF] Implement PN_XNUM extension for
program headers
---
llvm/include/llvm/Object/ELF.h | 49 +++++++++++++++++++---------------
1 file changed, 28 insertions(+), 21 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 3b96c0e2b9d1f..47bcc5dbe9899 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -278,16 +278,26 @@ class ELFFile {
std::vector<Elf_Shdr> FakeSections;
SmallString<0> FakeSectionStrings;
- Elf_Word RealPhNum;
- Elf_Word RealShNum;
- Elf_Word RealShStrNdx;
+ //
+ // According to the ELF gABI, these three fields can be recorded in section 0
+ // when possible. Therefore, we store this information when it is available.
+ //
+ std::optional<uint32_t> RealPhNum;
+ std::optional<uint32_t> RealShNum;
+ std::optional<uint32_t> RealShStrNdx;
ELFFile(StringRef Object);
public:
- Elf_Word getPhNum() const { return RealPhNum; }
- Elf_Word getShNum() const { return RealShNum; }
- Elf_Word getShStrNdx() const { return RealShStrNdx; }
+ uint32_t getPhNum() const {
+ return RealPhNum ? *RealPhNum : getHeader().e_phnum;
+ }
+ uint32_t getShNum() const {
+ return RealShNum ? *RealShNum : getHeader().e_shnum;
+ }
+ uint32_t getShStrNdx() const {
+ return RealShStrNdx ? *RealShStrNdx : getHeader().e_shstrndx;
+ }
const Elf_Ehdr &getHeader() const {
return *reinterpret_cast<const Elf_Ehdr *>(base());
}
@@ -386,21 +396,21 @@ class ELFFile {
/// Iterate over program header table.
Expected<Elf_Phdr_Range> program_headers() const {
- if (RealPhNum && getHeader().e_phentsize != sizeof(Elf_Phdr))
+ if (getPhNum() && getHeader().e_phentsize != sizeof(Elf_Phdr))
return createError("invalid e_phentsize: " +
Twine(getHeader().e_phentsize));
- uint64_t HeadersSize = (uint64_t)RealPhNum * getHeader().e_phentsize;
+ uint64_t HeadersSize = (uint64_t)getPhNum() * getHeader().e_phentsize;
uint64_t PhOff = getHeader().e_phoff;
if (PhOff + HeadersSize < PhOff || PhOff + HeadersSize > getBufSize())
return createError("program headers are longer than binary of size " +
Twine(getBufSize()) + ": e_phoff = 0x" +
Twine::utohexstr(getHeader().e_phoff) +
- ", e_phnum = " + Twine(RealPhNum) +
+ ", e_phnum = " + Twine(getPhNum()) +
", e_phentsize = " + Twine(getHeader().e_phentsize));
auto *Begin = reinterpret_cast<const Elf_Phdr *>(base() + PhOff);
- return ArrayRef(Begin, Begin + RealPhNum);
+ return ArrayRef(Begin, Begin + getPhNum());
}
/// Get an iterator over notes in a program header.
@@ -778,11 +788,11 @@ template <class ELFT>
Expected<StringRef>
ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections,
WarningHandler WarnHandler) const {
- uint32_t Index = RealShStrNdx;
- if (Index == ELF::SHN_XINDEX)
+ if (getHeader().e_shstrndx == ELF::SHN_XINDEX && !RealShStrNdx)
return createError(
"e_shstrndx == SHN_XINDEX, but the section header table is empty");
+ uint32_t Index = getShStrNdx();
// There is no section name string table. Return FakeSectionStrings which
// is non-empty if we have created fake sections.
if (!Index)
@@ -889,14 +899,11 @@ Expected<uint64_t> ELFFile<ELFT>::getDynSymtabSize() const {
template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {
const Elf_Ehdr &Header = getHeader();
- RealPhNum = Header.e_phnum;
- RealShNum = Header.e_shnum;
- RealShStrNdx = Header.e_shstrndx;
if (!Header.hasPhdrNumExtension())
return;
- // An ELF binary may report `hasExtendedHeader` as true but not actually
- // include an extended header. For example, a core dump can contain 65,535
+ // An ELF binary may report `hasPhdrNumExtension` as true but not actually
+ // include an section 0. For example, a core dump can contain 65,535
// segments but no sections at all. We defer reporting an error until section
// 0 is accessed. Consumers should handle and emit the error themselves when
// they attempt to access it.
@@ -905,11 +912,11 @@ template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {
consumeError(SecOrErr.takeError());
return;
}
- if (RealPhNum == 0xFFFF)
+ if (Header.e_phnum == 0xFFFF)
RealPhNum = (*SecOrErr)->sh_info;
- if (RealShNum == ELF::SHN_UNDEF)
+ if (Header.e_shnum == ELF::SHN_UNDEF)
RealShNum = (*SecOrErr)->sh_size;
- if (RealShStrNdx == ELF::SHN_XINDEX)
+ if (Header.e_shstrndx == ELF::SHN_XINDEX)
RealShStrNdx = (*SecOrErr)->sh_link;
}
@@ -978,7 +985,7 @@ Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::sections() const {
const Elf_Shdr *First =
reinterpret_cast<const Elf_Shdr *>(base() + SectionTableOffset);
- uintX_t NumSections = RealShNum;
+ uintX_t NumSections = getShNum();
if (NumSections == 0)
NumSections = First->sh_size;
>From b13b26d83141a0c2a4393c77982d5a03aa0853cb Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sat, 18 Oct 2025 03:39:24 +0800
Subject: [PATCH 5/9] fixup! [Object, ELF] Implement PN_XNUM extension for
program headers
---
llvm/include/llvm/Object/ELF.h | 96 ++++++++++++++++++++---------
llvm/include/llvm/Object/ELFTypes.h | 5 --
2 files changed, 66 insertions(+), 35 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 47bcc5dbe9899..08b294a48d7ab 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -288,16 +288,31 @@ class ELFFile {
ELFFile(StringRef Object);
+ Error readShdrZero();
+
public:
- uint32_t getPhNum() const {
- return RealPhNum ? *RealPhNum : getHeader().e_phnum;
+ Expected<uint32_t> getPhNum() const {
+ if (!RealPhNum) {
+ if (Error E = readShdrZero())
+ return std::move(E);
+ }
+ return *RealPhNum;
}
- uint32_t getShNum() const {
- return RealShNum ? *RealShNum : getHeader().e_shnum;
+ Expected<uint32_t> getShNum() const {
+ if (!RealShNum) {
+ if (Error E = readShdrZero())
+ return std::move(E);
+ }
+ return *RealShNum;
}
- uint32_t getShStrNdx() const {
- return RealShStrNdx ? *RealShStrNdx : getHeader().e_shstrndx;
+ Expected<uint32_t> getShStrNdx() const {
+ if (!RealShStrNdx) {
+ if (Error E = readShdrZero())
+ return std::move(E);
+ }
+ return *RealShStrNdx;
}
+
const Elf_Ehdr &getHeader() const {
return *reinterpret_cast<const Elf_Ehdr *>(base());
}
@@ -396,21 +411,26 @@ class ELFFile {
/// Iterate over program header table.
Expected<Elf_Phdr_Range> program_headers() const {
- if (getPhNum() && getHeader().e_phentsize != sizeof(Elf_Phdr))
+ uint32_t NumPh;
+ if (Expected<uint32_t> PhNumOrErr = getPhNum())
+ NumPh = *PhNumOrErr;
+ else
+ return PhNumOrErr.takeError();
+ if (NumPh && getHeader().e_phentsize != sizeof(Elf_Phdr))
return createError("invalid e_phentsize: " +
Twine(getHeader().e_phentsize));
- uint64_t HeadersSize = (uint64_t)getPhNum() * getHeader().e_phentsize;
+ uint64_t HeadersSize = (uint64_t)NumPh * getHeader().e_phentsize;
uint64_t PhOff = getHeader().e_phoff;
if (PhOff + HeadersSize < PhOff || PhOff + HeadersSize > getBufSize())
return createError("program headers are longer than binary of size " +
Twine(getBufSize()) + ": e_phoff = 0x" +
Twine::utohexstr(getHeader().e_phoff) +
- ", e_phnum = " + Twine(getPhNum()) +
+ ", e_phnum = " + Twine(NumPh) +
", e_phentsize = " + Twine(getHeader().e_phentsize));
auto *Begin = reinterpret_cast<const Elf_Phdr *>(base() + PhOff);
- return ArrayRef(Begin, Begin + getPhNum());
+ return ArrayRef(Begin, Begin + NumPh);
}
/// Get an iterator over notes in a program header.
@@ -792,7 +812,12 @@ ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections,
return createError(
"e_shstrndx == SHN_XINDEX, but the section header table is empty");
- uint32_t Index = getShStrNdx();
+ uint32_t Index;
+ if (Expected<uint32_t> IndexOrErr = getShStrNdx())
+ Index = *IndexOrErr;
+ else
+ return IndexOrErr.takeError();
+
// There is no section name string table. Return FakeSectionStrings which
// is non-empty if we have created fake sections.
if (!Index)
@@ -897,27 +922,34 @@ Expected<uint64_t> ELFFile<ELFT>::getDynSymtabSize() const {
return 0;
}
-template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {
+template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {}
+
+template <class ELFT> Error ELFFile<ELFT>::readShdrZero() {
const Elf_Ehdr &Header = getHeader();
- if (!Header.hasPhdrNumExtension())
- return;
- // An ELF binary may report `hasPhdrNumExtension` as true but not actually
- // include an section 0. For example, a core dump can contain 65,535
- // segments but no sections at all. We defer reporting an error until section
- // 0 is accessed. Consumers should handle and emit the error themselves when
- // they attempt to access it.
- auto SecOrErr = getSection(0);
- if (!SecOrErr) {
- consumeError(SecOrErr.takeError());
- return;
+ if ((Header.e_phnum == ELF::PN_XNUM || Header.e_shnum == 0 ||
+ Header.e_shstrndx == ELF::SHN_XINDEX) &&
+ Header.e_shoff != 0) {
+ //
+ // Pretend we have section 0 or sections() would call getShNum and thus
+ // become an infinite recursion
+ //
+ RealShNum = 0;
+ auto SecOrErr = getSection(0);
+ if (!SecOrErr) {
+ RealShNum = std::nullopt;
+ return SecOrErr.takeError();
+ }
+ RealPhNum =
+ Header.e_phnum == ELF::PN_XNUM ? (*SecOrErr)->sh_info : Header.e_phnum;
+ RealShNum = Header.e_shnum == 0 ? (*SecOrErr)->sh_size : Header.e_shnum;
+ RealShStrNdx = Header.e_shstrndx == ELF::SHN_XINDEX ? (*SecOrErr)->sh_link
+ : Header.e_shstrndx;
+ } else {
+ RealPhNum = Header.e_phnum;
+ RealShNum = Header.e_shnum;
+ RealShStrNdx = Header.e_shstrndx;
}
- if (Header.e_phnum == 0xFFFF)
- RealPhNum = (*SecOrErr)->sh_info;
- if (Header.e_shnum == ELF::SHN_UNDEF)
- RealShNum = (*SecOrErr)->sh_size;
- if (Header.e_shstrndx == ELF::SHN_XINDEX)
- RealShStrNdx = (*SecOrErr)->sh_link;
}
template <class ELFT>
@@ -985,7 +1017,11 @@ Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::sections() const {
const Elf_Shdr *First =
reinterpret_cast<const Elf_Shdr *>(base() + SectionTableOffset);
- uintX_t NumSections = getShNum();
+ uintX_t NumSections = 0;
+ if (Expected<uint32_t> ShNumOrErr = getShNum())
+ NumSections = *ShNumOrErr;
+ else
+ return ShNumOrErr.takeError();
if (NumSections == 0)
NumSections = First->sh_size;
diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index 77a68f95a46ce..e9a417d3d4fb3 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -529,11 +529,6 @@ struct Elf_Ehdr_Impl {
unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; }
unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; }
- bool hasPhdrNumExtension() const {
- return (e_phnum == ELF::PN_XNUM || e_shnum == ELF::SHN_UNDEF ||
- e_shstrndx == ELF::SHN_XINDEX) &&
- e_shoff != 0;
- }
};
template <endianness Endianness>
>From ab6377043e8a05cd15bf5b1bc7caac9f6baf44b3 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Mon, 20 Oct 2025 23:11:25 +0800
Subject: [PATCH 6/9] fixup! [Object, ELF] Implement PN_XNUM extension for
program headers
---
llvm/include/llvm/Object/ELF.h | 5 +----
llvm/include/llvm/Object/ELFTypes.h | 5 +++++
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 08b294a48d7ab..5ac30e0f8e19e 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -278,10 +278,8 @@ class ELFFile {
std::vector<Elf_Shdr> FakeSections;
SmallString<0> FakeSectionStrings;
- //
// According to the ELF gABI, these three fields can be recorded in section 0
// when possible. Therefore, we store this information when it is available.
- //
std::optional<uint32_t> RealPhNum;
std::optional<uint32_t> RealShNum;
std::optional<uint32_t> RealShStrNdx;
@@ -930,10 +928,9 @@ template <class ELFT> Error ELFFile<ELFT>::readShdrZero() {
if ((Header.e_phnum == ELF::PN_XNUM || Header.e_shnum == 0 ||
Header.e_shstrndx == ELF::SHN_XINDEX) &&
Header.e_shoff != 0) {
- //
+
// Pretend we have section 0 or sections() would call getShNum and thus
// become an infinite recursion
- //
RealShNum = 0;
auto SecOrErr = getSection(0);
if (!SecOrErr) {
diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index e9a417d3d4fb3..77a68f95a46ce 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -529,6 +529,11 @@ struct Elf_Ehdr_Impl {
unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; }
unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; }
+ bool hasPhdrNumExtension() const {
+ return (e_phnum == ELF::PN_XNUM || e_shnum == ELF::SHN_UNDEF ||
+ e_shstrndx == ELF::SHN_XINDEX) &&
+ e_shoff != 0;
+ }
};
template <endianness Endianness>
>From 8522aa99f51f6880b0a8727037095061e8f7636a Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Mon, 20 Oct 2025 23:18:41 +0800
Subject: [PATCH 7/9] fixup! [Object, ELF] Implement PN_XNUM extension for
program headers
---
llvm/include/llvm/Object/ELFTypes.h | 5 -----
1 file changed, 5 deletions(-)
diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index 77a68f95a46ce..e9a417d3d4fb3 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -529,11 +529,6 @@ struct Elf_Ehdr_Impl {
unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; }
unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; }
- bool hasPhdrNumExtension() const {
- return (e_phnum == ELF::PN_XNUM || e_shnum == ELF::SHN_UNDEF ||
- e_shstrndx == ELF::SHN_XINDEX) &&
- e_shoff != 0;
- }
};
template <endianness Endianness>
>From 0c7c1d42ffea8b3d75c3aacbb3eba5be94f65a40 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 21 Oct 2025 00:06:08 +0800
Subject: [PATCH 8/9] fixup! [Object, ELF] Implement PN_XNUM extension for
program headers
---
llvm/include/llvm/Object/ELF.h | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 5ac30e0f8e19e..fc6cfcfe36a07 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -291,21 +291,21 @@ class ELFFile {
public:
Expected<uint32_t> getPhNum() const {
if (!RealPhNum) {
- if (Error E = readShdrZero())
+ if (Error E = const_cast<ELFFile<ELFT> *>(this)->readShdrZero())
return std::move(E);
}
return *RealPhNum;
}
Expected<uint32_t> getShNum() const {
if (!RealShNum) {
- if (Error E = readShdrZero())
+ if (Error E = const_cast<ELFFile<ELFT> *>(this)->readShdrZero())
return std::move(E);
}
return *RealShNum;
}
Expected<uint32_t> getShStrNdx() const {
if (!RealShStrNdx) {
- if (Error E = readShdrZero())
+ if (Error E = const_cast<ELFFile<ELFT> *>(this)->readShdrZero())
return std::move(E);
}
return *RealShStrNdx;
@@ -947,6 +947,8 @@ template <class ELFT> Error ELFFile<ELFT>::readShdrZero() {
RealShNum = Header.e_shnum;
RealShStrNdx = Header.e_shstrndx;
}
+
+ return Error::success();
}
template <class ELFT>
>From 389b52c070569f9b6c142a95d2320a0ac7eb6c3e Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Tue, 21 Oct 2025 00:49:06 +0800
Subject: [PATCH 9/9] fixup! [Object, ELF] Implement PN_XNUM extension for
program headers
---
llvm/include/llvm/Object/ELF.h | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index fc6cfcfe36a07..772a20bb52f5d 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -806,9 +806,12 @@ template <class ELFT>
Expected<StringRef>
ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections,
WarningHandler WarnHandler) const {
- if (getHeader().e_shstrndx == ELF::SHN_XINDEX && !RealShStrNdx)
+ Expected<uint32_t> ShStrNdxOrErr = getShStrNdx();
+ if (!ShStrNdxOrErr || (*ShStrNdxOrErr == ELF::SHN_XINDEX && RealShNum == 0)) {
+ consumeError(ShStrNdxOrErr.takeError());
return createError(
"e_shstrndx == SHN_XINDEX, but the section header table is empty");
+ }
uint32_t Index;
if (Expected<uint32_t> IndexOrErr = getShStrNdx())
@@ -932,15 +935,29 @@ template <class ELFT> Error ELFFile<ELFT>::readShdrZero() {
// Pretend we have section 0 or sections() would call getShNum and thus
// become an infinite recursion
RealShNum = 0;
- auto SecOrErr = getSection(0);
- if (!SecOrErr) {
+ auto SecsOrErr = sections();
+ if (!SecsOrErr) {
RealShNum = std::nullopt;
- return SecOrErr.takeError();
+ return SecsOrErr.takeError();
}
+
+ // We can really have 0 number of seciton
+ if ((*SecsOrErr).size() == 0) {
+ if (Header.e_phnum == ELF::PN_XNUM ||
+ Header.e_shstrndx == ELF::SHN_XINDEX) {
+ return createError("Unable to find Section 0");
+ }
+ RealShNum = 0;
+ RealPhNum = Header.e_phnum;
+ RealShStrNdx = Header.e_shstrndx;
+ return Error::success();
+ }
+
+ auto &Section = (*SecsOrErr)[0];
RealPhNum =
- Header.e_phnum == ELF::PN_XNUM ? (*SecOrErr)->sh_info : Header.e_phnum;
- RealShNum = Header.e_shnum == 0 ? (*SecOrErr)->sh_size : Header.e_shnum;
- RealShStrNdx = Header.e_shstrndx == ELF::SHN_XINDEX ? (*SecOrErr)->sh_link
+ Header.e_phnum == ELF::PN_XNUM ? Section.sh_info : Header.e_phnum;
+ RealShNum = Header.e_shnum == 0 ? Section.sh_size : Header.e_shnum;
+ RealShStrNdx = Header.e_shstrndx == ELF::SHN_XINDEX ? Section.sh_link
: Header.e_shstrndx;
} else {
RealPhNum = Header.e_phnum;
More information about the llvm-commits
mailing list