diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 6ee6b666c1735..39e9611c7190e 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..03d5ee21a71b4 100644 --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -278,9 +278,46 @@ class ELFFile { std::vector 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 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 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 RealShStrNdx; + ELFFile(StringRef Object); + Error readShdrZero(); + public: + Expected getPhNum() const { + if (!RealPhNum) { + if (Error E = const_cast *>(this)->readShdrZero()) + return std::move(E); + } + return *RealPhNum; + } + + Expected getShNum() const { + if (!RealShNum) { + if (Error E = const_cast *>(this)->readShdrZero()) + return std::move(E); + } + return *RealShNum; + } + + Expected getShStrNdx() const { + if (!RealShStrNdx) { + if (Error E = const_cast *>(this)->readShdrZero()) + return std::move(E); + } + return *RealShStrNdx; + } + const Elf_Ehdr &getHeader() const { return *reinterpret_cast(base()); } @@ -379,22 +416,26 @@ class ELFFile { /// Iterate over program header table. Expected program_headers() const { - if (getHeader().e_phnum && getHeader().e_phentsize != sizeof(Elf_Phdr)) + uint32_t NumPh; + if (Expected 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(base() + PhOff); - return ArrayRef(Begin, Begin + getHeader().e_phnum); + return ArrayRef(Begin, Begin + NumPh); } /// Get an iterator over notes in a program header. @@ -772,19 +813,15 @@ template Expected ELFFile::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 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) @@ -891,6 +928,35 @@ Expected ELFFile::getDynSymtabSize() const { template ELFFile::ELFFile(StringRef Object) : Buf(Object) {} +template Error ELFFile::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 Expected> ELFFile::create(StringRef Object) { if (sizeof(Elf_Ehdr) > Object.size()) @@ -956,9 +1022,11 @@ Expected ELFFile::sections() const { const Elf_Shdr *First = reinterpret_cast(base() + SectionTableOffset); - uintX_t NumSections = getHeader().e_shnum; - if (NumSections == 0) - NumSections = First->sh_size; + uintX_t NumSections = 0; + if (Expected 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 " 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 423a11fd5b72a..accb9984caa15 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 +static std::string getProgramHeadersNumString(const ELFFile &Obj, + StringRef FileName) { + + if (Obj.getHeader().e_phnum != ELF::PN_XNUM) + return to_string(Obj.getHeader().e_phnum); + + Expected 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 static std::string getSectionHeadersNumString(const ELFFile &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> ArrOrErr = Obj.sections(); - if (!ArrOrErr) { + Expected 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 static std::string getSectionHeaderTableIndexString(const ELFFile &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> ArrOrErr = Obj.sections(); - if (!ArrOrErr) { + Expected 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 *getObjectFileEnumEntry(unsigned Type) { @@ -3765,7 +3782,7 @@ template void GNUELFDumper::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::printProgramHeaders( return; if (PrintProgramHeaders) { - const Elf_Ehdr &Header = this->Obj.getHeader(); - if (Header.e_phnum == 0) { + Expected 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 void GNUELFDumper::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 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 void LLVMELFDumper::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));