Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions llvm/include/llvm/BinaryFormat/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
106 changes: 87 additions & 19 deletions llvm/include/llvm/Object/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,46 @@ class ELFFile {
std::vector<Elf_Shdr> FakeSections;
SmallString<0> FakeSectionStrings;

// When the number of program headers is >= PN_XNUM, the actual number is
// contained in the sh_info field of the section header at index 0.
std::optional<uint32_t> RealPhNum;
// When the number of section headers is >= SHN_LORESERVE, the actual number
// is contained in the sh_size field of the section header at index 0.
std::optional<uint64_t> RealShNum;
// When the section index of the section name table is >= SHN_LORESERVE, the
// actual number is contained in the sh_link field of the section header at
// index 0.
std::optional<uint32_t> RealShStrNdx;

ELFFile(StringRef Object);

Error readShdrZero();

public:
Expected<uint32_t> getPhNum() const {
if (!RealPhNum) {
if (Error E = const_cast<ELFFile<ELFT> *>(this)->readShdrZero())
return std::move(E);
}
return *RealPhNum;
}

Expected<uint64_t> getShNum() const {
if (!RealShNum) {
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 = const_cast<ELFFile<ELFT> *>(this)->readShdrZero())
return std::move(E);
}
return *RealShStrNdx;
}

const Elf_Ehdr &getHeader() const {
return *reinterpret_cast<const Elf_Ehdr *>(base());
}
Expand Down Expand Up @@ -379,22 +416,26 @@ class ELFFile {

/// Iterate over program header table.
Expected<Elf_Phdr_Range> program_headers() const {
if (getHeader().e_phnum && 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)getHeader().e_phnum * 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(getHeader().e_phnum) +
", e_phnum = " + Twine(NumPh) +
", 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 + NumPh);
}

/// Get an iterator over notes in a program header.
Expand Down Expand Up @@ -772,19 +813,15 @@ 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");
Expected<uint32_t> ShStrNdxOrErr = getShStrNdx();
if (!ShStrNdxOrErr)
return ShStrNdxOrErr.takeError();

Index = Sections[0].sh_link;
}
if (*ShStrNdxOrErr == ELF::SHN_XINDEX && Sections.empty())
return createError(
"e_shstrndx == SHN_XINDEX, but the section header table is empty");

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

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

template <class ELFT> Error ELFFile<ELFT>::readShdrZero() {
const Elf_Ehdr &Header = getHeader();

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 = 1;
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;
}

return Error::success();
}

template <class ELFT>
Expected<ELFFile<ELFT>> ELFFile<ELFT>::create(StringRef Object) {
if (sizeof(Elf_Ehdr) > Object.size())
Expand Down Expand Up @@ -956,9 +1022,11 @@ 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;
if (NumSections == 0)
NumSections = First->sh_size;
uintX_t NumSections = 0;
if (Expected<uint64_t> ShNumOrErr = getShNum())
NumSections = *ShNumOrErr;
else
return ShNumOrErr.takeError();

if (NumSections > UINT64_MAX / sizeof(Elf_Shdr))
return createError("invalid number of sections specified in the NULL "
Expand Down
Binary file not shown.
39 changes: 39 additions & 0 deletions llvm/test/tools/llvm-readobj/ELF/invalid-e_phnum.test
Original file line number Diff line number Diff line change
@@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't feel like the right warning to be printing. We should just be printing something about the section header table missing or similar. We should probably not continue to try to print the program header table, if the program header count cannot be determined due to an invalid section header table.


--- !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
79 changes: 79 additions & 0 deletions llvm/test/tools/llvm-readobj/ELF/many-segments.test
Original file line number Diff line number Diff line change
@@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not rely on a large file to test this behaviour when we don't have to.

As noted in a previous comment somewhere, you can use yaml2obj to craft an object with e_phnum set to PN_XNUM and then a low number in the SHT_NULL section header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question is that it seems not a valid ELF under your suggestions since gABI would require you have more than or equal to PN_XNUM sections when using sectionc 0.

It causes the following problem.
For example, if we have 65535 in phnum in ELF header and 4 in section 0. getPhNum returns 4.

That means we are not able to ouput 65535(4) in readelf without checking the getHeader().e_phnum == PN_XNUM since getPhNum always returns the true number.

As a result, we might not able to test 65535 (N) case without actually creating more than 65535 segments.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question is that it seems not a valid ELF under your suggestions since gABI would require you have more than or equal to PN_XNUM sections when using sectionc 0.

As I believed both @MaskRay and I have explained in different places, although the gABI requires an object to have >= PN_XNUM program headers for this functionality to be active, there is no reason to enforce that in our code, so we can use a smaller number without issue.

That means we are not able to ouput 65535(4) in readelf without checking the getHeader().e_phnum == PN_XNUM since getPhNum always returns the true number.

I may not have been clear in my other comments, but it is acceptable to check whether getHeader().e_phnum == PN_XNUM, to allow it to distinguish between the different cases, which means it should be possible to check the 65535 (N) case without lots of segments.

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
Loading