Index: test/tools/llvm-readobj/gnu-phdrs.test =================================================================== --- /dev/null +++ test/tools/llvm-readobj/gnu-phdrs.test @@ -0,0 +1,77 @@ +#Source : +#__thread int a = 1; +#__thread int b; +# +#int main () { +# b = 2; +# throw (a + b) ; +# return 0; +#} +# compiled as clang++ source.cpp +# and clang++ -m32 source.cpp + +RUN: llvm-readobj -program-headers %p/Inputs/phdrs-elf.exe-i386 --elf-output-style=GNU \ +RUN: | FileCheck %s -check-prefix ELF32 +RUN: llvm-readobj -program-headers %p/Inputs/phdrs-elf.exe-x86_64 \ +RUN: --elf-output-style=GNU | FileCheck %s -check-prefix ELF64 + +ELF32: Elf file type is EXEC (Executable file) +ELF32-NEXT: Entry point 0x8048460 +ELF32-NEXT: There are 10 program headers, starting at Offsetset 52 +ELF32: Program Headers: +ELF32-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +ELF32-NEXT: PHDR 0x000034 0x8048034 0x8048034 0x000140 0x000140 R E 0x4 +ELF32-NEXT: INTERP 0x000174 0x8048174 0x8048174 0x000013 0x000013 R 0x1 +ELF32-NEXT: [Requesting program interpreter: /lib/ld-linux.so.2] +ELF32-NEXT: LOAD 0x000000 0x8048000 0x8048000 0x0006d0 0x0006d0 R E 0x1000 +ELF32-NEXT: LOAD 0x000ef0 0x8049ef0 0x8049ef0 0x000128 0x000140 RW 0x1000 +ELF32-NEXT: DYNAMIC 0x000f08 0x8049f08 0x8049f08 0x0000e8 0x0000e8 RW 0x4 +ELF32-NEXT: NOTE 0x000188 0x8048188 0x8048188 0x000044 0x000044 R 0x4 +ELF32-NEXT: TLS 0x000ef0 0x8049ef0 0x8049ef0 0x000004 0x000008 R 0x4 +ELF32-NEXT: GNU_EH_FRAME 0x000640 0x8048640 0x8048640 0x00001c 0x00001c R 0x4 +ELF32-NEXT: GNU_STACK 0x000000 0x000000 0x000000 0x000000 0x000000 RW 0x4 +ELF32-NEXT: GNU_RELRO 0x000ef0 0x8049ef0 0x8049ef0 0x000110 0x000110 R 0x1 + +ELF32: Section to Segment mapping: +ELF32-NEXT: Segment Sections... +ELF32-NEXT: 0 +ELF32-NEXT: 1 .interp +ELF32-NEXT: 2 .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame +ELF32-NEXT: 3 .tdata .ctors .dtors .jcr .dynamic .got .got.plt .data .bss +ELF32-NEXT: 4 .dynamic +ELF32-NEXT: 5 .note.ABI-tag .note.gnu.build-id +ELF32-NEXT: 6 .tdata .tbss +ELF32-NEXT: 7 .eh_frame_hdr +ELF32-NEXT: 8 +ELF32-NEXT: 9 .tdata .ctors .dtors .jcr .dynamic .got + +ELF64: Elf file type is EXEC (Executable file) +ELF64-NEXT: Entry point 0x400610 +ELF64-NEXT: There are 10 program headers, starting at Offsetset 64 + +ELF64: Program Headers: +ELF64-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +ELF64-NEXT: PHDR 0x000040 0x00000000400040 0x00000000400040 0x000230 0x000230 R E 0x8 +ELF64-NEXT: INTERP 0x000270 0x00000000400270 0x00000000400270 0x00001c 0x00001c R 0x1 +ELF64-NEXT: [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] +ELF64-NEXT: LOAD 0x000000 0x00000000400000 0x00000000400000 0x000924 0x000924 R E 0x200000 +ELF64-NEXT: LOAD 0x000db4 0x00000000600db4 0x00000000600db4 0x000274 0x0002a4 RW 0x200000 +ELF64-NEXT: DYNAMIC 0x000dd0 0x00000000600dd0 0x00000000600dd0 0x000210 0x000210 RW 0x8 +ELF64-NEXT: NOTE 0x00028c 0x0000000040028c 0x0000000040028c 0x000044 0x000044 R 0x4 +ELF64-NEXT: TLS 0x000db4 0x00000000600db4 0x00000000600db4 0x000004 0x000008 R 0x4 +ELF64-NEXT: GNU_EH_FRAME 0x00083c 0x0000000040083c 0x0000000040083c 0x00002c 0x00002c R 0x4 +ELF64-NEXT: GNU_STACK 0x000000 0x00000000000000 0x00000000000000 0x000000 0x000000 RW 0x8 +ELF64-NEXT: GNU_RELRO 0x000db4 0x00000000600db4 0x00000000600db4 0x00024c 0x00024c R 0x1 + +ELF64: Section to Segment mapping: +ELF64-NEXT: Segment Sections... +ELF64-NEXT: 0 +ELF64-NEXT: 1 .interp +ELF64-NEXT: 2 .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame +ELF64-NEXT: 3 .tdata .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss +ELF64-NEXT: 4 .dynamic +ELF64-NEXT: 5 .note.ABI-tag .note.gnu.build-id +ELF64-NEXT: 6 .tdata .tbss +ELF64-NEXT: 7 .eh_frame_hdr +ELF64-NEXT: 8 +ELF64-NEXT: 9 .tdata .init_array .fini_array .jcr .dynamic .got Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -279,6 +279,7 @@ virtual void printSymbol(const ELFFile *Obj, const Elf_Sym *Symbol, const Elf_Sym *FirstSym, StringRef StrTable, bool IsDynamic) = 0; + virtual void printProgramHeaders(const ELFFile *Obj) = 0; const ELFDumper *dumper() const { return Dumper; } private: const ELFDumper *Dumper; @@ -299,6 +300,7 @@ void printDynamicRelocations(const ELFO *Obj) override; virtual void printSymtabMessage(const ELFO *Obj, StringRef Name, size_t Offset) override; + void printProgramHeaders(const ELFO *Obj) override; private: struct Field { @@ -329,6 +331,10 @@ StringRef StrTable, bool IsDynamic) override; std::string getSymbolSectionNdx(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *FirstSym); + bool checkTLSSections(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); + bool checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); + bool checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); + bool checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); }; template class LLVMStyle : public DumpStyle { @@ -345,6 +351,7 @@ void printSymbols(const ELFO *Obj) override; void printDynamicSymbols(const ELFO *Obj) override; void printDynamicRelocations(const ELFO *Obj) override; + void printProgramHeaders(const ELFO *Obj) override; private: void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab); @@ -1076,6 +1083,65 @@ } } +static std::string getElfPtType(unsigned Arch, unsigned Type) { + switch (Type) { + case ELF::PT_NULL: + return "NULL"; + case ELF::PT_LOAD: + return "LOAD"; + case ELF::PT_DYNAMIC: + return "DYNAMIC"; + case ELF::PT_INTERP: + return "INTERP"; + case ELF::PT_NOTE: + return "NOTE"; + case ELF::PT_SHLIB: + return "SHLIB"; + case ELF::PT_PHDR: + return "PHDR"; + case ELF::PT_TLS: + return "TLS"; + case ELF::PT_GNU_EH_FRAME: + return "GNU_EH_FRAME"; + case ELF::PT_SUNW_UNWIND: + return "SUNW_UNWIND"; + case ELF::PT_GNU_STACK: + return "GNU_STACK"; + case ELF::PT_GNU_RELRO: + return "GNU_RELRO"; + default: + // All machine specific PT_* types + switch (Arch) { + case ELF::EM_AMDGPU: + switch (Type) { + LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_GLOBAL_PROGRAM); + LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_GLOBAL_AGENT); + LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_READONLY_AGENT); + LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_CODE_AGENT); + } + return ""; + case ELF::EM_ARM: + if (Type == ELF::PT_ARM_EXIDX) + return "EXIDX"; + return ""; + case ELF::EM_MIPS: + case ELF::EM_MIPS_RS3_LE: + switch (Type) { + case PT_MIPS_REGINFO: + return "REGINFO"; + case PT_MIPS_RTPROC: + return "RTPROC"; + case PT_MIPS_OPTIONS: + return "OPTIONS"; + case PT_MIPS_ABIFLAGS: + return "ABIFLAGS"; + } + return ""; + } + } + return std::string(": ") + to_string(format_hex(Type, 1)); +} + static const EnumEntry ElfSegmentFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, PF_X), LLVM_READOBJ_ENUM_ENT(ELF, PF_W), @@ -1294,11 +1360,14 @@ ELFDumperStyle->printRelocations(Obj); } +template void ELFDumper::printProgramHeaders() { + ELFDumperStyle->printProgramHeaders(Obj); +} + template void ELFDumper::printDynamicRelocations() { ELFDumperStyle->printDynamicRelocations(Obj); } - template void ELFDumper::printSymbols() { ELFDumperStyle->printSymbols(Obj); @@ -1309,7 +1378,6 @@ ELFDumperStyle->printDynamicSymbols(Obj); } - #define LLVM_READOBJ_TYPE_CASE(name) \ case DT_##name: return #name @@ -1623,24 +1691,6 @@ } } -template -void ELFDumper::printProgramHeaders() { - ListScope L(W, "ProgramHeaders"); - - for (const Elf_Phdr &Phdr : Obj->program_headers()) { - DictScope P(W, "ProgramHeader"); - W.printHex("Type", - getElfSegmentType(Obj->getHeader()->e_machine, Phdr.p_type), - Phdr.p_type); - W.printHex("Offset", Phdr.p_offset); - W.printHex("VirtualAddress", Phdr.p_vaddr); - W.printHex("PhysicalAddress", Phdr.p_paddr); - W.printNumber("FileSize", Phdr.p_filesz); - W.printNumber("MemSize", Phdr.p_memsz); - W.printFlags("Flags", Phdr.p_flags, makeArrayRef(ElfSegmentFlags)); - W.printNumber("Alignment", Phdr.p_align); - } -} template void ELFDumper::printHashTable() { @@ -2629,6 +2679,143 @@ this->dumper()->printSymbolsHelper(true); } +static inline std::string printPhdrFlags(unsigned Flag) { + std::string Str; + Str = (Flag & PF_R) ? "R" : " "; + Str += (Flag & PF_W) ? "W" : " "; + Str += (Flag & PF_X) ? "E" : " "; + return Str; +} + +// SHF_TLS sections are only in PT_TLS, PT_LOAD or PT_GNU_RELRO +// PT_TLS must only have SHF_TLS sections +template +bool GNUStyle::checkTLSSections(const Elf_Phdr &Phdr, + const Elf_Shdr &Sec) { + return (((Sec.sh_flags & ELF::SHF_TLS) && + ((Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) || + (Phdr.p_type == ELF::PT_GNU_RELRO))) || + (!(Sec.sh_flags & ELF::SHF_TLS) && Phdr.p_type != ELF::PT_TLS)); +} + +// Non-SHT_NOBITS must have its offset inside the segment +// Only non-zero section can be at end of segment +template +bool GNUStyle::checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { + if (Sec.sh_type == ELF::SHT_NOBITS) + return true; + bool IsSpecial = + (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0); + // .tbss is special, it only has memory in PT_TLS and has NOBITS properties + auto SectionSize = + (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size; + if (Sec.sh_offset >= Phdr.p_offset) + return ((Sec.sh_offset + SectionSize <= Phdr.p_filesz + Phdr.p_offset) + /*only non-zero sized sections at end*/ && + (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz)); + return false; +} + +// SHF_ALLOC must have VMA inside segment +// Only non-zero section can be at end of segment +template +bool GNUStyle::checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { + if (!(Sec.sh_flags & ELF::SHF_ALLOC)) + return true; + bool IsSpecial = + (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0); + // .tbss is special, it only has memory in PT_TLS and has NOBITS properties + auto SectionSize = + (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size; + if (Sec.sh_addr >= Phdr.p_vaddr) + return ((Sec.sh_addr + SectionSize <= Phdr.p_vaddr + Phdr.p_memsz) && + (Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz)); + return false; +} + +// No section with zero size must be at start or end of PT_DYNAMIC +template +bool GNUStyle::checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { + if (Phdr.p_type != ELF::PT_DYNAMIC || Sec.sh_size != 0 || Phdr.p_memsz == 0) + return true; + // Is section within the phdr both based on offset and VMA ? + return ((Sec.sh_type == ELF::SHT_NOBITS) || + (Sec.sh_offset > Phdr.p_offset && + Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz)) && + (!(Sec.sh_flags & ELF::SHF_ALLOC) || + (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz)); +} + +template +void GNUStyle::printProgramHeaders(const ELFO *Obj) { + int Bias = (ELFT::Is64Bits) ? 8 : 0; + unsigned Width = (ELFT::Is64Bits) ? 18 : 10; + unsigned SizeWidth = (ELFT::Is64Bits) ? 8 : 7; + std::string Type, Offset, VMA, LMA, FileSz, MemSz, Flag, Align; + + const Elf_Ehdr *Header = Obj->getHeader(); + Field Fields[8] = {2, 17, 26, 37 + Bias, + 48 + Bias, 56 + Bias, 64 + Bias, 68 + Bias}; + OS << "\nElf file type is " + << printEnum(Header->e_type, makeArrayRef(ElfObjectFileType)) << "\n" + << "Entry point " << format_hex(Header->e_entry, 1) << "\n" + << "There are " << Header->e_phnum << " program headers," + << " starting at offset " << Header->e_phoff << "\n\n" + << "Program Headers:\n"; + if (ELFT::Is64Bits) + OS << " Type Offset VirtAddr PhysAddr " + << " FileSiz MemSiz Flg Align\n"; + else + OS << " Type Offset VirtAddr PhysAddr FileSiz " + << "MemSiz Flg Align\n"; + for (const auto &Phdr : Obj->program_headers()) { + Type = getElfPtType(Header->e_machine, Phdr.p_type); + Offset = to_string(format_hex(Phdr.p_offset, 8)); + VMA = to_string(format_hex(Phdr.p_vaddr, Width)); + LMA = to_string(format_hex(Phdr.p_paddr, Width)); + FileSz = to_string(format_hex(Phdr.p_filesz, SizeWidth)); + MemSz = to_string(format_hex(Phdr.p_memsz, SizeWidth)); + Flag = printPhdrFlags(Phdr.p_flags); + Align = to_string(format_hex(Phdr.p_align, 1)); + Fields[0].Str = Type; + Fields[1].Str = Offset; + Fields[2].Str = VMA; + Fields[3].Str = LMA; + Fields[4].Str = FileSz; + Fields[5].Str = MemSz; + Fields[6].Str = Flag; + Fields[7].Str = Align; + for (auto Field : Fields) + printField(Field); + if (Phdr.p_type == ELF::PT_INTERP) { + OS << "\n [Requesting program interpreter: "; + OS << reinterpret_cast(Obj->base()) + Phdr.p_offset << "]"; + } + OS << "\n"; + } + OS << "\n Section to Segment mapping:\n Segment Sections...\n"; + int Phnum = 0; + for (const Elf_Phdr &Phdr : Obj->program_headers()) { + std::string Sections; + OS << format(" %2.2d ", Phnum++); + for (const Elf_Shdr &Sec : Obj->sections()) { + // Check if each section is in a segment and then print mapping. + // readelf additionally makes sure it does not print zero sized sections + // at end of segments and for PT_DYNAMIC both start and end of section + // .tbss must only be shown in PT_TLS section. + bool TbssInNonTLS = (Sec.sh_type == ELF::SHT_NOBITS) && + ((Sec.sh_flags & ELF::SHF_TLS) != 0) && + Phdr.p_type != ELF::PT_TLS; + if (!TbssInNonTLS && checkTLSSections(Phdr, Sec) && + checkoffsets(Phdr, Sec) && checkVMA(Phdr, Sec) && + checkPTDynamic(Phdr, Sec) && (Sec.sh_type != ELF::SHT_NULL)) + Sections += unwrapOrError(Obj->getSectionName(&Sec)).str() + " "; + } + OS << Sections << "\n"; + OS.flush(); + } +} + template void GNUStyle::printDynamicRelocations(const ELFO *Obj) { OS << "GNU style dynamic relocations not implemented!\n"; @@ -2955,3 +3142,22 @@ << W.hex(Rel.r_addend) << "\n"; } } + +template +void LLVMStyle::printProgramHeaders(const ELFO *Obj) { + ListScope L(W, "ProgramHeaders"); + + for (const Elf_Phdr &Phdr : Obj->program_headers()) { + DictScope P(W, "ProgramHeader"); + W.printHex("Type", + getElfSegmentType(Obj->getHeader()->e_machine, Phdr.p_type), + Phdr.p_type); + W.printHex("Offset", Phdr.p_offset); + W.printHex("VirtualAddress", Phdr.p_vaddr); + W.printHex("PhysicalAddress", Phdr.p_paddr); + W.printNumber("FileSize", Phdr.p_filesz); + W.printNumber("MemSize", Phdr.p_memsz); + W.printFlags("Flags", Phdr.p_flags, makeArrayRef(ElfSegmentFlags)); + W.printNumber("Alignment", Phdr.p_align); + } +}