Index: llvm/include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/ELFYAML.h +++ llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -176,6 +176,10 @@ // When they are, this flag is used to signal about that. bool IsImplicit; + // This holds the original section size and used for + // dumping program headers. + uint64_t OriginalSize = 0; + Section(ChunkKind Kind, bool IsImplicit = false) : Chunk(Kind), IsImplicit(IsImplicit) {} Index: llvm/test/Object/obj2yaml.test =================================================================== --- llvm/test/Object/obj2yaml.test +++ llvm/test/Object/obj2yaml.test @@ -664,6 +664,19 @@ # 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: - Type: PT_LOAD +# ELF-AVR-NEXT: Flags: [ PF_W, PF_R ] +# ELF-AVR-NEXT: Sections: +# ELF-AVR-NEXT: - Section: .data +# ELF-AVR-NEXT: VAddr: 0x0000000000800060 +# ELF-AVR-NEXT: PAddr: 0x0000000000000004 +# ELF-AVR-NEXT: Align: 0x0000000000000001 # ELF-AVR-NEXT: Sections: # ELF-AVR-NEXT: - Name: .text # ELF-AVR-NEXT: Type: SHT_PROGBITS Index: llvm/test/tools/obj2yaml/program-headers.yaml =================================================================== --- /dev/null +++ llvm/test/tools/obj2yaml/program-headers.yaml @@ -0,0 +1,180 @@ +## Show that obj2yaml is able to dump program headers. + +# RUN: yaml2obj %s -o %t1 +# RUN: llvm-readelf -a %t1 | FileCheck %s --check-prefix=SEGMENT-MAPPING +# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=YAML + +## Show the layout of the object before we dumped it using obj2yaml. + +# SEGMENT-MAPPING: Program Headers: +# SEGMENT-MAPPING-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# SEGMENT-MAPPING-NEXT: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0001d9 0x0001d9 R 0x1000 +# SEGMENT-MAPPING-NEXT: LOAD 0x0001d9 0x0000000000001000 0x0000000000001000 0x000010 0x000010 R E 0x1000 +# SEGMENT-MAPPING-NEXT: LOAD 0x0001e9 0x0000000000002000 0x0000000000002000 0x000009 0x000009 R 0x1000 +# SEGMENT-MAPPING-NEXT: LOAD 0x0001f2 0x0000000000003ef0 0x0000000000003ef0 0x000010 0x000010 RW 0x1000 +# SEGMENT-MAPPING-NEXT: DYNAMIC 0x0001f2 0x0000000000003ef0 0x0000000000003ef0 0x000010 0x000010 RW 0x8 +# SEGMENT-MAPPING-NEXT: GNU_RELRO 0x0001f2 0x0000000000003ef0 0x0000000000003ef0 0x000010 0x000010 R 0x1 +# SEGMENT-MAPPING: Section to Segment mapping: +# SEGMENT-MAPPING-NEXT: Segment Sections... +# SEGMENT-MAPPING-NEXT: 00 .hash .gnu.hash .dynsym .dynstr +# SEGMENT-MAPPING-NEXT: 01 .foo .zed +# SEGMENT-MAPPING-NEXT: 02 .foo .baz +# SEGMENT-MAPPING-NEXT: 03 .dynamic +# SEGMENT-MAPPING-NEXT: 04 .dynamic +# SEGMENT-MAPPING-NEXT: 05 .dynamic +# SEGMENT-MAPPING-NEXT: None .symtab .strtab .shstrtab + +## Check that obj2yaml produced a correct program headers description. + +# YAML: ProgramHeaders: +# YAML-NEXT: - Type: PT_LOAD +# YAML-NEXT: Flags: [ PF_R ] +# YAML-NEXT: Sections: +# YAML-NEXT: - Section: .hash +# YAML-NEXT: - Section: .gnu.hash +# YAML-NEXT: - Section: .dynsym +# YAML-NEXT: - Section: .dynstr +# YAML-NEXT: Align: 0x0000000000001000 +# YAML-NEXT: - Type: PT_LOAD +# YAML-NEXT: Flags: [ PF_X, PF_R ] +# YAML-NEXT: Sections: +# YAML-NEXT: - Section: .foo +# YAML-NEXT: - Section: .zed +# YAML-NEXT: VAddr: 0x0000000000001000 +# YAML-NEXT: PAddr: 0x0000000000001000 +# YAML-NEXT: Align: 0x0000000000001000 +# YAML-NEXT: - Type: PT_LOAD +# YAML-NEXT: Flags: [ PF_R ] +# YAML-NEXT: Sections: +# YAML-NEXT: - Section: '.foo [1]' +# YAML-NEXT: - Section: .baz +# YAML-NEXT: VAddr: 0x0000000000002000 +# YAML-NEXT: PAddr: 0x0000000000002000 +# YAML-NEXT: Align: 0x0000000000001000 +# YAML-NEXT: - Type: PT_LOAD +# YAML-NEXT: Flags: [ PF_W, PF_R ] +# YAML-NEXT: Sections: +# YAML-NEXT: - Section: .dynamic +# YAML-NEXT: VAddr: 0x0000000000003EF0 +# YAML-NEXT: PAddr: 0x0000000000003EF0 +# YAML-NEXT: Align: 0x0000000000001000 +# YAML-NEXT: - Type: PT_DYNAMIC +# YAML-NEXT: Flags: [ PF_W, PF_R ] +# YAML-NEXT: Sections: +# YAML-NEXT: - Section: .dynamic +# YAML-NEXT: VAddr: 0x0000000000003EF0 +# YAML-NEXT: PAddr: 0x0000000000003EF0 +# YAML-NEXT: Align: 0x0000000000000008 +# YAML-NEXT: - Type: PT_GNU_RELRO +# YAML-NEXT: Flags: [ PF_R ] +# YAML-NEXT: Sections: +# YAML-NEXT: - Section: .dynamic +# YAML-NEXT: VAddr: 0x0000000000003EF0 +# YAML-NEXT: PAddr: 0x0000000000003EF0 +# YAML-NEXT: Align: 0x0000000000000001 +# YAML-NEXT: Sections: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + Sections: + - Section: .hash + - Section: .gnu.hash + - Section: .dynsym + - Section: .dynstr + Align: 0x1000 + Offset: 0x0 + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + Sections: + - Section: .foo + - Section: .zed + VAddr: 0x1000 + PAddr: 0x1000 + Align: 0x1000 + - Type: PT_LOAD + Flags: [ PF_R ] + Sections: + - Section: '.foo [1]' + - Section: .baz + VAddr: 0x2000 + PAddr: 0x2000 + Align: 0x1000 + - Type: PT_LOAD + Flags: [ PF_W, PF_R ] + Sections: + - Section: .dynamic + VAddr: 0x3EF0 + PAddr: 0x3EF0 + Align: 0x1000 + - Type: PT_DYNAMIC + Flags: [ PF_W, PF_R ] + Sections: + - Section: .dynamic + VAddr: 0x3EF0 + PAddr: 0x3EF0 + Align: 0x8 + - Type: PT_GNU_RELRO + Flags: [ PF_R ] + Sections: + - Section: .dynamic + VAddr: 0x3EF0 + PAddr: 0x3EF0 + Align: 0x1 +Sections: + - Name: .hash + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x190 + Size: 0x10 + - Name: .gnu.hash + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1A0 + Size: 0x20 + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Address: 0x1C0 + Link: .dynstr + EntSize: 0x18 + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Address: 0x1D8 + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + Size: 0x8 + - Name: .zed + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1008 + Size: 0x8 + - Name: '.foo [1]' + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x2000 + Size: 0x8 + - Name: .baz + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x2008 + Size: 0x1 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x0000000000003EF0 + Link: .dynstr + Entries: + - Tag: DT_NULL + Value: 0x0 +Symbols: [] +DynamicSymbols: [] Index: llvm/tools/obj2yaml/elf2yaml.cpp =================================================================== --- llvm/tools/obj2yaml/elf2yaml.cpp +++ llvm/tools/obj2yaml/elf2yaml.cpp @@ -51,6 +51,9 @@ const object::ELFFile &Obj; ArrayRef ShndxTable; + Error dumpProgramHeaders(std::vector &Phdrs, + ArrayRef> Sections); + Error dumpSymbols(const Elf_Shdr *Symtab, std::vector &Symbols); Error dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab, @@ -159,6 +162,61 @@ return Name; } +template +Error ELFDumper::dumpProgramHeaders( + std::vector &Phdrs, + ArrayRef> Chunks) { + auto PhdrsOrErr = Obj.program_headers(); + if (!PhdrsOrErr) + return PhdrsOrErr.takeError(); + if (PhdrsOrErr->empty()) + return Error::success(); + + // Prepare a list of allocatable sections that we are going to assign to + // segments. + std::vector WorkList; + for (const std::unique_ptr &C : Chunks) { + ELFYAML::Section *S = cast(C.get()); + if (S->Flags && ((*S->Flags) & ELF::SHF_ALLOC)) + WorkList.push_back(S); + } + + // Sort the list of sections by virtual address. + auto SortPred = [](const ELFYAML::Section *A, const ELFYAML::Section *B) { + uint64_t AddressA = A->Address ? (uint64_t)*A->Address : 0; + uint64_t AddressB = B->Address ? (uint64_t)*B->Address : 0; + return AddressA < AddressB; + }; + llvm::sort(WorkList, SortPred); + + for (const typename ELFT::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; + PH.Align = static_cast(Phdr.p_align); + + // Here we match sections with segments. We find the start of a sorted + // range of sections, where the first section has a virtual address that is + // not less than VA of the current program header. Then we assign each + // section that is inside a segment to it. + ELFYAML::Section FindSec(ELFYAML::Chunk::ChunkKind::RawContent); + FindSec.Address = static_cast(Phdr.p_vaddr); + auto It = llvm::lower_bound(WorkList, &FindSec, SortPred); + while (It != WorkList.end()) { + ELFYAML::Section* Sec = *It++; + uint64_t VA = Sec->Address.getValueOr(llvm::yaml::Hex64(0)); + if (VA < PH.VAddr || (VA + Sec->OriginalSize > PH.VAddr + Phdr.p_memsz)) + break; + PH.Sections.push_back({Sec->Name}); + } + Phdrs.push_back(PH); + } + + return Error::success(); +} + template Expected ELFDumper::dump() { auto Y = std::make_unique(); @@ -229,11 +287,17 @@ return std::move(E); } - if (auto ChunksOrErr = dumpSections()) - Y->Chunks = std::move(*ChunksOrErr); - else + // We dump chunks for each allocatable section and do not create chunks for + // some non-allocatable sections, e.g. for .symtab. + Expected>> ChunksOrErr = + dumpSections(); + if (!ChunksOrErr) return ChunksOrErr.takeError(); + Y->Chunks = std::move(*ChunksOrErr); + // Dump program headers. + if (Error E = dumpProgramHeaders(Y->ProgramHeaders, Y->Chunks)) + return std::move(E); return Y.release(); } @@ -254,7 +318,12 @@ case ELF::SHT_STRTAB: case ELF::SHT_SYMTAB: case ELF::SHT_DYNSYM: - // Do not dump these sections. + if (Sec.sh_flags & ELF::SHF_ALLOC) { + auto S = std::make_unique(); + if (Error E = dumpCommonSection(&Sec, *S.get())) + return std::move(E); + Ret.emplace_back(S.release()); + } break; case ELF::SHT_SYMTAB_SHNDX: { Expected SecOrErr = @@ -521,6 +590,8 @@ if (Shdr->sh_entsize) S.EntSize = static_cast(Shdr->sh_entsize); + S.OriginalSize = static_cast(Shdr->sh_size); + auto NameOrErr = getUniquedSectionName(Shdr); if (!NameOrErr) return NameOrErr.takeError();