Index: include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- include/llvm/ObjectYAML/ELFYAML.h +++ include/llvm/ObjectYAML/ELFYAML.h @@ -33,12 +33,14 @@ // In the future, these would probably be better suited by C++11 enum // class's with appropriate fixed underlying type. LLVM_YAML_STRONG_TYPEDEF(uint16_t, ELF_ET) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, ELF_PT) LLVM_YAML_STRONG_TYPEDEF(uint32_t, ELF_EM) LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_ELFCLASS) LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_ELFDATA) LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_ELFOSABI) // Just use 64, since it can hold 32-bit values too. LLVM_YAML_STRONG_TYPEDEF(uint64_t, ELF_EF) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, ELF_PF) LLVM_YAML_STRONG_TYPEDEF(uint32_t, ELF_SHT) LLVM_YAML_STRONG_TYPEDEF(uint32_t, ELF_REL) LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_RSS) @@ -66,6 +68,16 @@ ELF_EF Flags; llvm::yaml::Hex64 Entry; }; +struct SectionName { + StringRef Section; +}; +struct ProgramHeader { + ELF_PT Type; + ELF_PF Flags; + llvm::yaml::Hex64 VAddr; + llvm::yaml::Hex64 PAddr; + std::vector Sections; +}; struct Symbol { StringRef Name; ELF_STT Type; @@ -165,6 +177,7 @@ struct Object { FileHeader Header; + std::vector ProgramHeaders; std::vector> Sections; // Although in reality the symbols reside in a section, it is a lot // cleaner and nicer if we read them from the YAML as a separate @@ -176,10 +189,12 @@ } // end namespace ELFYAML } // end namespace llvm +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::Symbol) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::Relocation) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::SectionOrType) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::SectionName) namespace llvm { namespace yaml { @@ -189,6 +204,10 @@ static void enumeration(IO &IO, ELFYAML::ELF_ET &Value); }; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, ELFYAML::ELF_PT &Value); +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, ELFYAML::ELF_EM &Value); @@ -214,6 +233,10 @@ static void bitset(IO &IO, ELFYAML::ELF_EF &Value); }; +template <> struct ScalarBitSetTraits { + static void bitset(IO &IO, ELFYAML::ELF_PF &Value); +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, ELFYAML::ELF_SHT &Value); @@ -284,6 +307,10 @@ static void mapping(IO &IO, ELFYAML::FileHeader &FileHdr); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::ProgramHeader &FileHdr); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::Symbol &Symbol); @@ -313,6 +340,10 @@ static void mapping(IO &IO, ELFYAML::SectionOrType §ionOrType); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::SectionName §ionName); +}; + } // end namespace yaml } // end namespace llvm Index: lib/ObjectYAML/ELFYAML.cpp =================================================================== --- lib/ObjectYAML/ELFYAML.cpp +++ lib/ObjectYAML/ELFYAML.cpp @@ -33,6 +33,21 @@ IO.enumFallback(Value); } +void ScalarEnumerationTraits::enumeration( + IO &IO, ELFYAML::ELF_PT &Value) { +#define ECase(X) IO.enumCase(Value, #X, ELF::X) + ECase(PT_NULL); + ECase(PT_LOAD); + ECase(PT_DYNAMIC); + ECase(PT_INTERP); + ECase(PT_NOTE); + ECase(PT_SHLIB); + ECase(PT_PHDR); + ECase(PT_TLS); +#undef ECase + IO.enumFallback(Value); +} + void ScalarEnumerationTraits::enumeration( IO &IO, ELFYAML::ELF_EM &Value) { #define ECase(X) IO.enumCase(Value, #X, ELF::X) @@ -405,6 +420,14 @@ #undef ECase } +void ScalarBitSetTraits::bitset(IO &IO, + ELFYAML::ELF_PF &Value) { +#define BCase(X) IO.bitSetCase(Value, #X, ELF::X) + BCase(PF_X); + BCase(PF_W); + BCase(PF_R); +} + void ScalarBitSetTraits::bitset(IO &IO, ELFYAML::ELF_SHF &Value) { const auto *Object = static_cast(IO.getContext()); @@ -647,6 +670,15 @@ IO.mapOptional("Entry", FileHdr.Entry, Hex64(0)); } +void MappingTraits::mapping( + IO &IO, ELFYAML::ProgramHeader &Phdr) { + IO.mapRequired("Type", Phdr.Type); + IO.mapOptional("Flags", Phdr.Flags, ELFYAML::ELF_PF(0)); + IO.mapOptional("Sections", Phdr.Sections); + IO.mapOptional("VAddr", Phdr.VAddr, Hex64(0)); + IO.mapOptional("PAddr", Phdr.PAddr, Hex64(0)); +} + namespace { struct NormalizedOther { NormalizedOther(IO &) @@ -716,6 +748,11 @@ IO.mapRequired("SectionOrType", sectionOrType.sectionNameOrType); } +void MappingTraits::mapping( + IO &IO, ELFYAML::SectionName §ionName) { + IO.mapRequired("Section", sectionName.Section); +} + static void sectionMapping(IO &IO, ELFYAML::MipsABIFlags &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Version", Section.Version, Hex16(0)); @@ -831,6 +868,7 @@ IO.setContext(&Object); IO.mapTag("!ELF", true); IO.mapRequired("FileHeader", Object.Header); + IO.mapOptional("ProgramHeaders", Object.ProgramHeaders); IO.mapOptional("Sections", Object.Sections); IO.mapOptional("Symbols", Object.Symbols); IO.setContext(nullptr); Index: test/tools/yaml2obj/program-header-nobits.yaml =================================================================== --- /dev/null +++ test/tools/yaml2obj/program-header-nobits.yaml @@ -0,0 +1,39 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-readobj -program-headers %t | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: "00000000" + - Name: .after + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] + Size: 64 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + Sections: + - Section: .data + - Section: .after + +#CHECK: ProgramHeaders [ +#CHECK-NEXT: ProgramHeader { +#CHECK-NEXT: Type: PT_LOAD +#CHECK-NEXT: Offset: +#CHECK-NEXT: VirtualAddress: +#CHECK-NEXT: PhysicalAddress: +#CHECK-NEXT: FileSize: 4 +#CHECK-NEXT: MemSize: 68 +#CHECK-NEXT: Flags [ +#CHECK-NEXT: PF_R +#CHECK-NEXT: ] +#CHECK-NEXT: Alignment: +#CHECK-NEXT: } +#CHECK-NEXT:] Index: test/tools/yaml2obj/program-header.yaml =================================================================== --- /dev/null +++ test/tools/yaml2obj/program-header.yaml @@ -0,0 +1,67 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-readobj -program-headers %t | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000001000 + Content: "00000000" + - Name: .init + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: "00000000" + AddressAlign: 0x0000000000000010 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: "00000000" + AddressAlign: 0x0000000000001000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0xAAAA1000 + PAddr: 0xFFFF1000 + Sections: + - Section: .text + - Section: .init + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0xAAAA2000 + PAddr: 0xFFFF2000 + Sections: + - Section: .data + +#CHECK: ProgramHeaders [ +#CHECK-NEXT: ProgramHeader { +#CHECK-NEXT: Type: PT_LOAD +#CHECK-NEXT: Offset: 0x1000 +#CHECK-NEXT: VirtualAddress: 0xAAAA1000 +#CHECK-NEXT: PhysicalAddress: 0xFFFF1000 +#CHECK-NEXT: FileSize: 20 +#CHECK-NEXT: MemSize: 20 +#CHECK-NEXT: Flags [ +#CHECK-NEXT: PF_R +#CHECK-NEXT: PF_X +#CHECK-NEXT: ] +#CHECK-NEXT: Alignment: 4096 +#CHECK-NEXT: } +#CHECK-NEXT: ProgramHeader { +#CHECK-NEXT: Type: PT_LOAD +#CHECK-NEXT: Offset: 0x2000 +#CHECK-NEXT: VirtualAddress: 0xAAAA2000 +#CHECK-NEXT: PhysicalAddress: 0xFFFF2000 +#CHECK-NEXT: FileSize: 4 +#CHECK-NEXT: MemSize: 4 +#CHECK-NEXT: Flags [ +#CHECK-NEXT: PF_R +#CHECK-NEXT: ] +#CHECK-NEXT: Alignment: 4096 +#CHECK-NEXT: } +#CHECK-NEXT:] Index: tools/yaml2obj/yaml2elf.cpp =================================================================== --- tools/yaml2obj/yaml2elf.cpp +++ tools/yaml2obj/yaml2elf.cpp @@ -99,6 +99,7 @@ template class ELFState { typedef typename object::ELFFile::Elf_Ehdr Elf_Ehdr; + typedef typename object::ELFFile::Elf_Phdr Elf_Phdr; typedef typename object::ELFFile::Elf_Shdr Elf_Shdr; typedef typename object::ELFFile::Elf_Sym Elf_Sym; typedef typename object::ELFFile::Elf_Rel Elf_Rel; @@ -118,6 +119,7 @@ bool buildSymbolIndex(std::size_t &StartIndex, const std::vector &Symbols); void initELFHeader(Elf_Ehdr &Header); + void initProgramHeaders(std::vector &PHeaders); bool initSectionHeaders(std::vector &SHeaders, ContiguousBlobAccumulator &CBA); void initSymtabSectionHeader(Elf_Shdr &SHeader, @@ -125,6 +127,8 @@ void initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name, StringTableBuilder &STB, ContiguousBlobAccumulator &CBA); + void setProgramHeaderLayout(std::vector &PHeaders, + std::vector &SHeaders); void addSymbols(const std::vector &Symbols, std::vector &Syms, unsigned SymbolBinding); void writeSectionContent(Elf_Shdr &SHeader, @@ -173,15 +177,31 @@ Header.e_machine = Doc.Header.Machine; Header.e_version = EV_CURRENT; Header.e_entry = Doc.Header.Entry; + Header.e_phoff = sizeof(Header); Header.e_flags = Doc.Header.Flags; Header.e_ehsize = sizeof(Elf_Ehdr); + Header.e_phentsize = sizeof(Elf_Phdr); + Header.e_phnum = Doc.ProgramHeaders.size(); Header.e_shentsize = sizeof(Elf_Shdr); - // Immediately following the ELF header. - Header.e_shoff = sizeof(Header); + // Immediately following the ELF header and program headers. + Header.e_shoff = + sizeof(Header) + sizeof(Elf_Phdr) * Doc.ProgramHeaders.size(); Header.e_shnum = getSectionCount(); Header.e_shstrndx = getDotShStrTabSecNo(); } +template +void ELFState::initProgramHeaders(std::vector &PHeaders) { + for (const auto &YamlPhdr : Doc.ProgramHeaders) { + Elf_Phdr Phdr; + Phdr.p_type = YamlPhdr.Type; + Phdr.p_flags = YamlPhdr.Flags; + Phdr.p_vaddr = YamlPhdr.VAddr; + Phdr.p_paddr = YamlPhdr.PAddr; + PHeaders.push_back(Phdr); + } +} + template bool ELFState::initSectionHeaders(std::vector &SHeaders, ContiguousBlobAccumulator &CBA) { @@ -310,6 +330,67 @@ SHeader.sh_addralign = 1; } +template +void ELFState::setProgramHeaderLayout(std::vector &PHeaders, + std::vector &SHeaders) { + uint32_t PhdrIdx = 0; + for (auto &YamlPhdr : Doc.ProgramHeaders) { + auto &PHeader = PHeaders[PhdrIdx++]; + + if (YamlPhdr.Sections.size()) + PHeader.p_offset = UINT32_MAX; + else + PHeader.p_offset = 0; + + // Find the minimum offset for the program header. + for (auto SecName : YamlPhdr.Sections) { + uint32_t Index = 0; + SN2I.lookup(SecName.Section, Index); + const auto &SHeader = SHeaders[Index]; + PHeader.p_offset = std::min(PHeader.p_offset, SHeader.sh_offset); + } + + // Find the maximum offset of the end of a section in order to set p_filesz. + PHeader.p_filesz = 0; + for (auto SecName : YamlPhdr.Sections) { + uint32_t Index = 0; + SN2I.lookup(SecName.Section, Index); + const auto &SHeader = SHeaders[Index]; + uint64_t EndOfSection; + if (SHeader.sh_type == llvm::ELF::SHT_NOBITS) + EndOfSection = SHeader.sh_offset; + else + EndOfSection = SHeader.sh_offset + SHeader.sh_size; + uint64_t EndOfSegment = PHeader.p_offset + PHeader.p_filesz; + EndOfSegment = std::max(EndOfSegment, EndOfSection); + PHeader.p_filesz = EndOfSegment - PHeader.p_offset; + } + + // Find the memory size by adding the size of sections at the end of the + // segment. These should be empty (size of zero) and NOBITS sections. + PHeader.p_memsz = PHeader.p_filesz; + for (auto SecName : YamlPhdr.Sections) { + uint32_t Index = 0; + SN2I.lookup(SecName.Section, Index); + const auto &SHeader = SHeaders[Index]; + if (SHeader.sh_offset == PHeader.p_offset + PHeader.p_filesz) + PHeader.p_memsz += SHeader.sh_size; + } + + // Set the alignment of the segment to be the same as the maximum alignment + // of the the sections with the same offset so that by default the segment + // has a valid and sensible alignment. + PHeader.p_align = 1; + for (auto SecName : YamlPhdr.Sections) { + uint32_t Index = 0; + SN2I.lookup(SecName.Section, Index); + const auto &SHeader = SHeaders[Index]; + if (SHeader.sh_offset == PHeader.p_offset) + PHeader.p_align = std::max(PHeader.p_align, SHeader.sh_addralign); + } + } +} + template void ELFState::addSymbols(const std::vector &Symbols, std::vector &Syms, @@ -508,12 +589,15 @@ State.initELFHeader(Header); // TODO: Flesh out section header support. - // TODO: Program headers. + + std::vector PHeaders; + State.initProgramHeaders(PHeaders); // XXX: This offset is tightly coupled with the order that we write // things to `OS`. - const size_t SectionContentBeginOffset = - Header.e_ehsize + Header.e_shentsize * Header.e_shnum; + const size_t SectionContentBeginOffset = Header.e_ehsize + + Header.e_phentsize * Header.e_phnum + + Header.e_shentsize * Header.e_shnum; ContiguousBlobAccumulator CBA(SectionContentBeginOffset); // Doc might not contain .symtab, .strtab and .shstrtab sections, @@ -543,7 +627,11 @@ CBA); SHeaders.push_back(ShStrTabSHeader); + // Now we can decide segment offsets + State.setProgramHeaderLayout(PHeaders, SHeaders); + OS.write((const char *)&Header, sizeof(Header)); + writeArrayData(OS, makeArrayRef(PHeaders)); writeArrayData(OS, makeArrayRef(SHeaders)); CBA.writeBlobToStream(OS); return 0;