Skip to content

Commit 2ccfc51

Browse files
authored
[Object,ELF] Implement PN_XNUM extension for program headers (#162288)
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.
1 parent 215aca4 commit 2ccfc51

File tree

2 files changed

+89
-19
lines changed

2 files changed

+89
-19
lines changed

llvm/include/llvm/BinaryFormat/ELF.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,8 @@ struct Elf64_Shdr {
11251125
Elf64_Xword sh_entsize;
11261126
};
11271127

1128+
enum { PN_XNUM = 0xffff };
1129+
11281130
// Special section indices.
11291131
enum {
11301132
SHN_UNDEF = 0, // Undefined, missing, irrelevant, or meaningless

llvm/include/llvm/Object/ELF.h

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,46 @@ class ELFFile {
278278
std::vector<Elf_Shdr> FakeSections;
279279
SmallString<0> FakeSectionStrings;
280280

281+
// When the number of program headers is >= PN_XNUM, the actual number is
282+
// contained in the sh_info field of the section header at index 0.
283+
std::optional<uint32_t> RealPhNum;
284+
// When the number of section headers is >= SHN_LORESERVE, the actual number
285+
// is contained in the sh_size field of the section header at index 0.
286+
std::optional<uint64_t> RealShNum;
287+
// When the section index of the section name table is >= SHN_LORESERVE, the
288+
// actual number is contained in the sh_link field of the section header at
289+
// index 0.
290+
std::optional<uint32_t> RealShStrNdx;
291+
281292
ELFFile(StringRef Object);
282293

294+
Error readShdrZero();
295+
283296
public:
297+
Expected<uint32_t> getPhNum() const {
298+
if (!RealPhNum) {
299+
if (Error E = const_cast<ELFFile<ELFT> *>(this)->readShdrZero())
300+
return std::move(E);
301+
}
302+
return *RealPhNum;
303+
}
304+
305+
Expected<uint64_t> getShNum() const {
306+
if (!RealShNum) {
307+
if (Error E = const_cast<ELFFile<ELFT> *>(this)->readShdrZero())
308+
return std::move(E);
309+
}
310+
return *RealShNum;
311+
}
312+
313+
Expected<uint32_t> getShStrNdx() const {
314+
if (!RealShStrNdx) {
315+
if (Error E = const_cast<ELFFile<ELFT> *>(this)->readShdrZero())
316+
return std::move(E);
317+
}
318+
return *RealShStrNdx;
319+
}
320+
284321
const Elf_Ehdr &getHeader() const {
285322
return *reinterpret_cast<const Elf_Ehdr *>(base());
286323
}
@@ -379,22 +416,26 @@ class ELFFile {
379416

380417
/// Iterate over program header table.
381418
Expected<Elf_Phdr_Range> program_headers() const {
382-
if (getHeader().e_phnum && getHeader().e_phentsize != sizeof(Elf_Phdr))
419+
uint32_t NumPh;
420+
if (Expected<uint32_t> PhNumOrErr = getPhNum())
421+
NumPh = *PhNumOrErr;
422+
else
423+
return PhNumOrErr.takeError();
424+
if (NumPh && getHeader().e_phentsize != sizeof(Elf_Phdr))
383425
return createError("invalid e_phentsize: " +
384426
Twine(getHeader().e_phentsize));
385427

386-
uint64_t HeadersSize =
387-
(uint64_t)getHeader().e_phnum * getHeader().e_phentsize;
428+
uint64_t HeadersSize = (uint64_t)NumPh * getHeader().e_phentsize;
388429
uint64_t PhOff = getHeader().e_phoff;
389430
if (PhOff + HeadersSize < PhOff || PhOff + HeadersSize > getBufSize())
390431
return createError("program headers are longer than binary of size " +
391432
Twine(getBufSize()) + ": e_phoff = 0x" +
392433
Twine::utohexstr(getHeader().e_phoff) +
393-
", e_phnum = " + Twine(getHeader().e_phnum) +
434+
", e_phnum = " + Twine(NumPh) +
394435
", e_phentsize = " + Twine(getHeader().e_phentsize));
395436

396437
auto *Begin = reinterpret_cast<const Elf_Phdr *>(base() + PhOff);
397-
return ArrayRef(Begin, Begin + getHeader().e_phnum);
438+
return ArrayRef(Begin, Begin + NumPh);
398439
}
399440

400441
/// Get an iterator over notes in a program header.
@@ -772,19 +813,15 @@ template <class ELFT>
772813
Expected<StringRef>
773814
ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections,
774815
WarningHandler WarnHandler) const {
775-
uint32_t Index = getHeader().e_shstrndx;
776-
if (Index == ELF::SHN_XINDEX) {
777-
// If the section name string table section index is greater than
778-
// or equal to SHN_LORESERVE, then the actual index of the section name
779-
// string table section is contained in the sh_link field of the section
780-
// header at index 0.
781-
if (Sections.empty())
782-
return createError(
783-
"e_shstrndx == SHN_XINDEX, but the section header table is empty");
816+
Expected<uint32_t> ShStrNdxOrErr = getShStrNdx();
817+
if (!ShStrNdxOrErr)
818+
return ShStrNdxOrErr.takeError();
784819

785-
Index = Sections[0].sh_link;
786-
}
820+
if (*ShStrNdxOrErr == ELF::SHN_XINDEX && Sections.empty())
821+
return createError(
822+
"e_shstrndx == SHN_XINDEX, but the section header table is empty");
787823

824+
uint32_t Index = *ShStrNdxOrErr;
788825
// There is no section name string table. Return FakeSectionStrings which
789826
// is non-empty if we have created fake sections.
790827
if (!Index)
@@ -891,6 +928,35 @@ Expected<uint64_t> ELFFile<ELFT>::getDynSymtabSize() const {
891928

892929
template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {}
893930

931+
template <class ELFT> Error ELFFile<ELFT>::readShdrZero() {
932+
const Elf_Ehdr &Header = getHeader();
933+
934+
if ((Header.e_phnum == ELF::PN_XNUM || Header.e_shnum == 0 ||
935+
Header.e_shstrndx == ELF::SHN_XINDEX) &&
936+
Header.e_shoff != 0) {
937+
// Pretend we have section 0 or sections() would call getShNum and thus
938+
// become an infinite recursion.
939+
RealShNum = 1;
940+
auto SecOrErr = getSection(0);
941+
if (!SecOrErr) {
942+
RealShNum = std::nullopt;
943+
return SecOrErr.takeError();
944+
}
945+
946+
RealPhNum =
947+
Header.e_phnum == ELF::PN_XNUM ? (*SecOrErr)->sh_info : Header.e_phnum;
948+
RealShNum = Header.e_shnum == 0 ? (*SecOrErr)->sh_size : Header.e_shnum;
949+
RealShStrNdx = Header.e_shstrndx == ELF::SHN_XINDEX ? (*SecOrErr)->sh_link
950+
: Header.e_shstrndx;
951+
} else {
952+
RealPhNum = Header.e_phnum;
953+
RealShNum = Header.e_shnum;
954+
RealShStrNdx = Header.e_shstrndx;
955+
}
956+
957+
return Error::success();
958+
}
959+
894960
template <class ELFT>
895961
Expected<ELFFile<ELFT>> ELFFile<ELFT>::create(StringRef Object) {
896962
if (sizeof(Elf_Ehdr) > Object.size())
@@ -956,9 +1022,11 @@ Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::sections() const {
9561022
const Elf_Shdr *First =
9571023
reinterpret_cast<const Elf_Shdr *>(base() + SectionTableOffset);
9581024

959-
uintX_t NumSections = getHeader().e_shnum;
960-
if (NumSections == 0)
961-
NumSections = First->sh_size;
1025+
uintX_t NumSections = 0;
1026+
if (Expected<uint64_t> ShNumOrErr = getShNum())
1027+
NumSections = *ShNumOrErr;
1028+
else
1029+
return ShNumOrErr.takeError();
9621030

9631031
if (NumSections > UINT64_MAX / sizeof(Elf_Shdr))
9641032
return createError("invalid number of sections specified in the NULL "

0 commit comments

Comments
 (0)