Skip to content

Commit d2740bc

Browse files
committed
[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.
1 parent 69761e7 commit d2740bc

File tree

3 files changed

+48
-19
lines changed

3 files changed

+48
-19
lines changed

llvm/include/llvm/BinaryFormat/ELF.h

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

1126+
enum { PN_XNUM = 0xffff };
1127+
11261128
// Special section indices.
11271129
enum {
11281130
SHN_UNDEF = 0, // Undefined, missing, irrelevant, or meaningless

llvm/include/llvm/Object/ELF.h

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

281+
Elf_Word RealPhNum;
282+
Elf_Word RealShNum;
283+
Elf_Word RealShStrNdx;
284+
281285
ELFFile(StringRef Object);
282286

283287
public:
288+
Elf_Word getPhNum() const { return RealPhNum; }
289+
Elf_Word getShNum() const { return RealShNum; }
290+
Elf_Word getShStrNdx() const { return RealShStrNdx; }
284291
const Elf_Ehdr &getHeader() const {
285292
return *reinterpret_cast<const Elf_Ehdr *>(base());
286293
}
@@ -379,22 +386,21 @@ class ELFFile {
379386

380387
/// Iterate over program header table.
381388
Expected<Elf_Phdr_Range> program_headers() const {
382-
if (getHeader().e_phnum && getHeader().e_phentsize != sizeof(Elf_Phdr))
389+
if (RealPhNum && getHeader().e_phentsize != sizeof(Elf_Phdr))
383390
return createError("invalid e_phentsize: " +
384391
Twine(getHeader().e_phentsize));
385392

386-
uint64_t HeadersSize =
387-
(uint64_t)getHeader().e_phnum * getHeader().e_phentsize;
393+
uint64_t HeadersSize = (uint64_t)RealPhNum * getHeader().e_phentsize;
388394
uint64_t PhOff = getHeader().e_phoff;
389395
if (PhOff + HeadersSize < PhOff || PhOff + HeadersSize > getBufSize())
390396
return createError("program headers are longer than binary of size " +
391397
Twine(getBufSize()) + ": e_phoff = 0x" +
392398
Twine::utohexstr(getHeader().e_phoff) +
393-
", e_phnum = " + Twine(getHeader().e_phnum) +
399+
", e_phnum = " + Twine(RealPhNum) +
394400
", e_phentsize = " + Twine(getHeader().e_phentsize));
395401

396402
auto *Begin = reinterpret_cast<const Elf_Phdr *>(base() + PhOff);
397-
return ArrayRef(Begin, Begin + getHeader().e_phnum);
403+
return ArrayRef(Begin, Begin + RealPhNum);
398404
}
399405

400406
/// Get an iterator over notes in a program header.
@@ -772,18 +778,10 @@ template <class ELFT>
772778
Expected<StringRef>
773779
ELFFile<ELFT>::getSectionStringTable(Elf_Shdr_Range Sections,
774780
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");
784-
785-
Index = Sections[0].sh_link;
786-
}
781+
uint32_t Index = RealShStrNdx;
782+
if (Index == ELF::SHN_XINDEX)
783+
return createError(
784+
"e_shstrndx == SHN_XINDEX, but the section header table is empty");
787785

788786
// There is no section name string table. Return FakeSectionStrings which
789787
// is non-empty if we have created fake sections.
@@ -889,7 +887,31 @@ Expected<uint64_t> ELFFile<ELFT>::getDynSymtabSize() const {
889887
return 0;
890888
}
891889

892-
template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {}
890+
template <class ELFT> ELFFile<ELFT>::ELFFile(StringRef Object) : Buf(Object) {
891+
const Elf_Ehdr &Header = getHeader();
892+
RealPhNum = Header.e_phnum;
893+
RealShNum = Header.e_shnum;
894+
RealShStrNdx = Header.e_shstrndx;
895+
if (!Header.hasPhdrNumExtension())
896+
return;
897+
898+
// An ELF binary may report `hasExtendedHeader` as true but not actually
899+
// include an extended header. For example, a core dump can contain 65,535
900+
// segments but no sections at all. We defer reporting an error until section
901+
// 0 is accessed. Consumers should handle and emit the error themselves when
902+
// they attempt to access it.
903+
auto SecOrErr = getSection(0);
904+
if (!SecOrErr) {
905+
consumeError(SecOrErr.takeError());
906+
return;
907+
}
908+
if (RealPhNum == 0xFFFF)
909+
RealPhNum = (*SecOrErr)->sh_info;
910+
if (RealShNum == ELF::SHN_UNDEF)
911+
RealShNum = (*SecOrErr)->sh_size;
912+
if (RealShStrNdx == ELF::SHN_XINDEX)
913+
RealShStrNdx = (*SecOrErr)->sh_link;
914+
}
893915

894916
template <class ELFT>
895917
Expected<ELFFile<ELFT>> ELFFile<ELFT>::create(StringRef Object) {
@@ -956,7 +978,7 @@ Expected<typename ELFT::ShdrRange> ELFFile<ELFT>::sections() const {
956978
const Elf_Shdr *First =
957979
reinterpret_cast<const Elf_Shdr *>(base() + SectionTableOffset);
958980

959-
uintX_t NumSections = getHeader().e_shnum;
981+
uintX_t NumSections = RealShNum;
960982
if (NumSections == 0)
961983
NumSections = First->sh_size;
962984

llvm/include/llvm/Object/ELFTypes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,11 @@ struct Elf_Ehdr_Impl {
529529

530530
unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; }
531531
unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; }
532+
bool hasPhdrNumExtension() const {
533+
return (e_phnum == ELF::PN_XNUM || e_shnum == ELF::SHN_UNDEF ||
534+
e_shstrndx == ELF::SHN_XINDEX) &&
535+
e_shoff != 0;
536+
}
532537
};
533538

534539
template <endianness Endianness>

0 commit comments

Comments
 (0)