diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h --- a/llvm/include/llvm/Object/ELF.h +++ b/llvm/include/llvm/Object/ELF.h @@ -299,6 +299,9 @@ template Expected> getSectionContentsAsArray(const Elf_Shdr *Sec) const; Expected> getSectionContents(const Elf_Shdr *Sec) const; + + bool isSectionWithinPhdr(const Elf_Phdr &Phdr, const Elf_Shdr &Sec, + const uint64_t EhdrOffset = 0) const; }; using ELF32LEFile = ELFFile; @@ -717,6 +720,86 @@ return h; } +/// Check if a section is within a program header. +/// +/// \param Phdr the program header. +/// \param Shdr the section header. +/// \param EhdrOffset The offset of the ELF header for loadable partition. +/// Ignored if not used for llvm-objcopy. +template +bool ELFFile::isSectionWithinPhdr(const Elf_Phdr &Phdr, + const Elf_Shdr &Sec, + const uint64_t EhdrOffset) const { + + // Point to the partition section header if specified. + uint64_t POffset = Phdr.p_offset + EhdrOffset; + + // .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; + + // SHF_TLS sections are only in PT_TLS, PT_LOAD or PT_GNU_RELRO + // PT_TLS must only have SHF_TLS sections + bool checkTLSSections = + (((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 + // SHF_ALLOC must have VMA inside segment + // Only non-zero section can be at end of segment + auto checkOffsetsOrVMA = [&POffset](const Elf_Phdr &Phdr, + const Elf_Shdr &Sec) { + if (Sec.sh_type != ELF::SHT_NOBITS) { + if (Sec.sh_offset >= POffset) { + // only non-zero sized sections at end + auto SectionSize = Sec.sh_size ? Sec.sh_size : 1; + return (Sec.sh_offset + SectionSize <= Phdr.p_filesz + POffset); + } + return false; + } + + if (Sec.sh_flags & ELF::SHF_ALLOC) { + 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) + ? 1 + : (Sec.sh_size ? Sec.sh_size : 1); + + if (Sec.sh_addr >= Phdr.p_vaddr) + return (Sec.sh_addr + SectionSize <= Phdr.p_vaddr + Phdr.p_memsz); + return false; + } + + return true; + }; + + // No section with zero size must be at start or end of PT_DYNAMIC + auto checkPTDynamic = [&POffset](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 > POffset && + Sec.sh_offset < POffset + Phdr.p_filesz)) && + (!(Sec.sh_flags & ELF::SHF_ALLOC) || + (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz)); + }; + + if ((Sec.sh_type == ELF::SHT_NULL) || TbssInNonTLS || !checkTLSSections) + return false; + + if (checkOffsetsOrVMA(Phdr, Sec) && checkPTDynamic(Phdr, Sec)) + return true; + + return false; +} + } // end namespace object } // end namespace llvm diff --git a/llvm/test/Object/obj2yaml.test b/llvm/test/Object/obj2yaml.test --- a/llvm/test/Object/obj2yaml.test +++ b/llvm/test/Object/obj2yaml.test @@ -664,6 +664,21 @@ # ELF-AVR-NEXT: Type: ET_EXEC # ELF-AVR-NEXT: Machine: EM_AVR # ELF-AVR-NEXT: Flags: [ EF_AVR_ARCH_AVR2 ] +# ELF-AVR-NEXT: ProgramHeaders: +# ELF-AVR-NEXT: - Type: PT_LOAD +# ELF-AVR-NEXT: Flags: [ PF_X, PF_R ] +# ELF-AVR-NEXT: Sections: +# ELF-AVR-NEXT: - Section: .text +# ELF-AVR-NEXT: Align: 0x0000000000000002 +# ELF-AVR-NEXT: FileSize: 0x0000000000000004 +# ELF-AVR-NEXT: MemSize: 0x0000000000000004 +# ELF-AVR-NEXT: Offset: 0x0000000000000074 +# ELF-AVR-NEXT: - Type: PT_LOAD +# ELF-AVR-NEXT: Flags: [ PF_W, PF_R ] +# ELF-AVR-NEXT: VAddr: 0x0000000000800060 +# ELF-AVR-NEXT: PAddr: 0x0000000000000004 +# ELF-AVR-NEXT: Align: 0x0000000000000001 +# ELF-AVR-NEXT: Offset: 0x0000000000000078 # ELF-AVR-NEXT: Sections: # ELF-AVR-NEXT: - Name: .text # ELF-AVR-NEXT: Type: SHT_PROGBITS diff --git a/llvm/test/tools/obj2yaml/program-headers.test b/llvm/test/tools/obj2yaml/program-headers.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/obj2yaml/program-headers.test @@ -0,0 +1,108 @@ +# RUN: yaml2obj %s > %t.o +# RUN: obj2yaml %t.o | FileCheck %s --check-prefix PHDRS + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0200 + AddressAlign: 0x0008 + Content: 'EEFF112233445566' + - Name: .nested + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0208 + AddressAlign: 0x0004 + Content: 'AABBCCDD' + - Name: .bar + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0210 + AddressAlign: 0x0004 + Content: 'A1B2C3D4' + - Name: .nobit_tbss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + Address: 0x0300 + AddressAlign: 0x0008 + Size: 0x0018 + - Name: .baz + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x0300 + AddressAlign: 0x0001 + Content: '0123456789ABCDEF' + - Name: .nobit_bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x0308 + AddressAlign: 0x0001 + Size: 0x0008 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ ] + VAddr: 0x0208 + PAddr: 0x0208 + Sections: + - Section: .nested + - Type: PT_LOAD + Flags: [ ] + VAddr: 0x0200 + PAddr: 0x0200 + Sections: + - Section: .foo + - Section: .nested + - Section: .bar + - Type: PT_LOAD + Flags: [ ] + VAddr: 0x0300 + PAddr: 0x0300 + Sections: + - Section: .baz + - Section: .nobit_bss + - Type: PT_TLS + Flags: [ ] + VAddr: 0x0300 + PAddr: 0x0300 # no mapping, then no filesz/memsz and offset +... + +# PHDRS: ProgramHeaders: +# PHDRS: - Type: PT_LOAD +# PHDRS: Sections: +# PHDRS: - Section: .nested +# PHDRS: VAddr: 0x0000000000000208 +# PHDRS: PAddr: 0x0000000000000208 +# PHDRS: Align: 0x0000000000000004 +# PHDRS: FileSize: 0x0000000000000004 +# PHDRS: MemSize: 0x0000000000000004 +# PHDRS: Offset: 0x0000000000000128 +# PHDRS: - Type: PT_LOAD +# PHDRS: Sections: +# PHDRS: - Section: .foo +# PHDRS: - Section: .nested +# PHDRS: - Section: .bar +# PHDRS: VAddr: 0x0000000000000200 +# PHDRS: PAddr: 0x0000000000000200 +# PHDRS: Align: 0x0000000000000008 +# PHDRS: FileSize: 0x0000000000000010 +# PHDRS: MemSize: 0x0000000000000010 +# PHDRS: Offset: 0x0000000000000120 +# PHDRS: - Type: PT_LOAD +# PHDRS: Sections: +# PHDRS: - Section: .baz +# PHDRS: VAddr: 0x0000000000000300 +# PHDRS: PAddr: 0x0000000000000300 +# PHDRS: Align: 0x0000000000000001 +# PHDRS: FileSize: 0x0000000000000008 +# PHDRS: MemSize: 0x0000000000000010 +# PHDRS: Offset: 0x0000000000000130 +# PHDRS: - Type: PT_TLS +# PHDRS: VAddr: 0x0000000000000300 +# PHDRS: PAddr: 0x0000000000000300 +# PHDRS: Align: 0x0000000000000008 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -3477,65 +3477,6 @@ 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, bool PrintProgramHeaders, @@ -3600,16 +3541,7 @@ std::string Sections; OS << format(" %2.2d ", Phnum++); for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, 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)) { + if (Obj->isSectionWithinPhdr(Phdr, Sec)) { Sections += unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() + " "; diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -23,6 +23,7 @@ class ELFDumper { typedef object::Elf_Sym_Impl Elf_Sym; typedef typename ELFT::Dyn Elf_Dyn; + typedef typename ELFT::Phdr Elf_Phdr; typedef typename ELFT::Shdr Elf_Shdr; typedef typename ELFT::Word Elf_Word; typedef typename ELFT::Rel Elf_Rel; @@ -348,6 +349,54 @@ } } + // Dump program headers + auto PhdrsOrErr = Obj.program_headers(); + if (!PhdrsOrErr) + return PhdrsOrErr.takeError(); + + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { + ELFYAML::ProgramHeader PH; + PH.Type = Phdr.p_type; + PH.Flags = Phdr.p_flags; + PH.VAddr = Phdr.p_vaddr; + PH.PAddr = Phdr.p_paddr; + + std::vector Shdrs; + // Highly rely on reasonable fields to layout the program header's mapping. + if ((Phdr.p_filesz && Phdr.p_offset) || Phdr.p_memsz) { + PH.FileSize = static_cast(Phdr.p_filesz); + PH.MemSize = static_cast(Phdr.p_memsz); + for (const Elf_Shdr &Sec : Sections) { + if (Obj.isSectionWithinPhdr(Phdr, Sec)) { + auto NameOrErr = getUniquedSectionName(&Sec); + if (!NameOrErr) + return NameOrErr.takeError(); + PH.Sections.push_back(ELFYAML::SectionName{*NameOrErr}); + Shdrs.push_back(&Sec); + } + } + } + + if (Phdr.p_offset) + PH.Offset = static_cast(Phdr.p_offset); + else if (!Shdrs.empty()) { + PH.Offset = UINT32_MAX; + for (const Elf_Shdr *Shdr : Shdrs) + PH.Offset = std::min(*PH.Offset, + static_cast(Shdr->sh_offset)); + } + + PH.Align = static_cast(Phdr.p_align); + if (Phdr.p_align == 0) { + PH.Align = 1; + for (const Elf_Shdr *Shdr : Shdrs) + PH.Align = std::max(*PH.Align, + static_cast(Shdr->sh_addralign)); + } + + Y->ProgramHeaders.push_back(PH); + } + return Y.release(); }