diff --git a/include/llvm/Object/ELFYAML.h b/include/llvm/Object/ELFYAML.h index 029a8ab2e5a..dd9625e99e0 100644 --- a/include/llvm/Object/ELFYAML.h +++ b/include/llvm/Object/ELFYAML.h @@ -36,6 +36,9 @@ LLVM_YAML_STRONG_TYPEDEF(uint16_t, ELF_ET) 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(uint32_t, ELF_SHT) +// Just use 64, since it can hold 32-bit values too. +LLVM_YAML_STRONG_TYPEDEF(uint64_t, ELF_SHF) // For now, hardcode 64 bits everywhere that 32 or 64 would be needed // since 64-bit can hold 32-bit values too. @@ -46,13 +49,21 @@ struct FileHeader { ELF_EM Machine; llvm::yaml::Hex64 Entry; }; +struct Section { + StringRef Name; + ELF_SHT Type; + ELF_SHF Flags; +}; struct Object { FileHeader Header; + std::vector
Sections; }; } // end namespace ELFYAML } // end namespace llvm +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::Section) + namespace llvm { namespace yaml { @@ -76,11 +87,26 @@ struct ScalarEnumerationTraits { static void enumeration(IO &IO, ELFYAML::ELF_ELFDATA &Value); }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, ELFYAML::ELF_SHT &Value); +}; + +template <> +struct ScalarBitSetTraits { + static void bitset(IO &IO, ELFYAML::ELF_SHF &Value); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::FileHeader &FileHdr); }; +template <> +struct MappingTraits { + static void mapping(IO &IO, ELFYAML::Section &Section); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::Object &Object); diff --git a/lib/Object/ELFYAML.cpp b/lib/Object/ELFYAML.cpp index 967fd2b2720..e275a4a40a3 100644 --- a/lib/Object/ELFYAML.cpp +++ b/lib/Object/ELFYAML.cpp @@ -212,6 +212,45 @@ void ScalarEnumerationTraits::enumeration( #undef ECase } +void ScalarEnumerationTraits::enumeration( + IO &IO, ELFYAML::ELF_SHT &Value) { +#define ECase(X) IO.enumCase(Value, #X, ELF::X); + ECase(SHT_NULL) + ECase(SHT_PROGBITS) + ECase(SHT_SYMTAB) + ECase(SHT_STRTAB) + ECase(SHT_RELA) + ECase(SHT_HASH) + ECase(SHT_DYNAMIC) + ECase(SHT_NOTE) + ECase(SHT_NOBITS) + ECase(SHT_REL) + ECase(SHT_SHLIB) + ECase(SHT_DYNSYM) + ECase(SHT_INIT_ARRAY) + ECase(SHT_FINI_ARRAY) + ECase(SHT_PREINIT_ARRAY) + ECase(SHT_GROUP) + ECase(SHT_SYMTAB_SHNDX) +#undef ECase +} + +void ScalarBitSetTraits::bitset(IO &IO, + ELFYAML::ELF_SHF &Value) { +#define BCase(X) IO.bitSetCase(Value, #X, ELF::X); + BCase(SHF_WRITE) + BCase(SHF_ALLOC) + BCase(SHF_EXECINSTR) + BCase(SHF_MERGE) + BCase(SHF_STRINGS) + BCase(SHF_INFO_LINK) + BCase(SHF_LINK_ORDER) + BCase(SHF_OS_NONCONFORMING) + BCase(SHF_GROUP) + BCase(SHF_TLS) +#undef BCase +} + void MappingTraits::mapping(IO &IO, ELFYAML::FileHeader &FileHdr) { IO.mapRequired("Class", FileHdr.Class); @@ -221,8 +260,16 @@ void MappingTraits::mapping(IO &IO, IO.mapOptional("Entry", FileHdr.Entry, Hex64(0)); } +void MappingTraits::mapping(IO &IO, + ELFYAML::Section &Section) { + IO.mapOptional("Name", Section.Name, StringRef()); + IO.mapRequired("Type", Section.Type); + IO.mapOptional("Flags", Section.Flags, ELFYAML::ELF_SHF(0)); +} + void MappingTraits::mapping(IO &IO, ELFYAML::Object &Object) { IO.mapRequired("FileHeader", Object.Header); + IO.mapOptional("Sections", Object.Sections); } } // end namespace yaml diff --git a/test/Object/lit.local.cfg b/test/Object/lit.local.cfg index b2439b2d1b7..d74d039d684 100644 --- a/test/Object/lit.local.cfg +++ b/test/Object/lit.local.cfg @@ -1 +1 @@ -config.suffixes = ['.test', '.ll'] +config.suffixes = ['.test', '.ll', '.yaml'] diff --git a/test/Object/yaml2obj-elf-section-basic.yaml b/test/Object/yaml2obj-elf-section-basic.yaml new file mode 100644 index 00000000000..6d40952ec01 --- /dev/null +++ b/test/Object/yaml2obj-elf-section-basic.yaml @@ -0,0 +1,23 @@ +# RUN: yaml2obj -format=elf %s | llvm-readobj -sections - | FileCheck %s +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +# CHECK: Section { +# CHECK: Index: 0 +# CHECK: Type: SHT_NULL (0x0) +# +# CHECK: Section { +# CHECK: Name: .text +# CHECK: Type: SHT_PROGBITS (0x1) +# CHECK-NEXT: Flags [ (0x6) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_EXECINSTR (0x4) +# CHECK-NEXT: ] diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp index 45ccb629e74..235327bd057 100644 --- a/tools/yaml2obj/yaml2elf.cpp +++ b/tools/yaml2obj/yaml2elf.cpp @@ -22,13 +22,71 @@ using namespace llvm; +// There is similar code in yaml2coff, but with some slight COFF-specific +// variations like different initial state. Might be able to deduplicate +// some day, but also want to make sure that the Mach-O use case is served. +// +// This class has a deliberately small interface, since a lot of +// implementation variation is possible. +// +// TODO: Use an ordered container with a suffix-based comparison in order +// to deduplicate suffixes. std::map<> with a custom comparator is likely +// to be the simplest implementation, but a suffix trie could be more +// suitable for the job. +class StringTableBuilder { + /// \brief Indices of strings currently present in `Buf`. + StringMap StringIndices; + /// \brief The contents of the string table as we build it. + std::string Buf; +public: + StringTableBuilder() { + Buf.push_back('\0'); + } + /// \returns Index of string in string table. + unsigned addString(StringRef S) { + StringMapEntry &Entry = StringIndices.GetOrCreateValue(S); + unsigned &I = Entry.getValue(); + if (I != 0) + return I; + I = Buf.size(); + Buf.append(S.begin(), S.end()); + Buf.push_back('\0'); + return I; + } + size_t size() const { + return Buf.size(); + } + void writeToStream(raw_ostream &OS) { + OS.write(Buf.data(), Buf.size()); + } +}; + +template +static size_t vectorDataSize(const std::vector &Vec) { + return Vec.size() * sizeof(T); +} + +template +static void writeVectorData(raw_ostream &OS, const std::vector &Vec) { + OS.write((const char *)Vec.data(), vectorDataSize(Vec)); +} + +template +static void zero(T &Obj) { + memset(&Obj, 0, sizeof(Obj)); +} + template static void writeELF(raw_ostream &OS, const ELFYAML::Object &Doc) { - const ELFYAML::FileHeader &Hdr = Doc.Header; using namespace llvm::ELF; using namespace llvm::object; - typename ELFObjectFile::Elf_Ehdr Header; - memset(&Header, 0, sizeof(Header)); + typedef typename ELFObjectFile::Elf_Ehdr Elf_Ehdr; + typedef typename ELFObjectFile::Elf_Shdr Elf_Shdr; + + const ELFYAML::FileHeader &Hdr = Doc.Header; + + Elf_Ehdr Header; + zero(Header); Header.e_ident[EI_MAG0] = 0x7f; Header.e_ident[EI_MAG1] = 'E'; Header.e_ident[EI_MAG2] = 'L'; @@ -36,9 +94,7 @@ static void writeELF(raw_ostream &OS, const ELFYAML::Object &Doc) { Header.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; bool IsLittleEndian = ELFT::TargetEndianness == support::little; Header.e_ident[EI_DATA] = IsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB; - Header.e_ident[EI_VERSION] = EV_CURRENT; - // TODO: Implement ELF_ELFOSABI enum. Header.e_ident[EI_OSABI] = ELFOSABI_NONE; // TODO: Implement ELF_ABIVERSION enum. @@ -47,11 +103,63 @@ static void writeELF(raw_ostream &OS, const ELFYAML::Object &Doc) { Header.e_machine = Hdr.Machine; Header.e_version = EV_CURRENT; Header.e_entry = Hdr.Entry; - Header.e_ehsize = sizeof(Header); + Header.e_ehsize = sizeof(Elf_Ehdr); - // TODO: Section headers and program headers. + // TODO: Flesh out section header support. + // TODO: Program headers. + + Header.e_shentsize = sizeof(Elf_Shdr); + // Immediately following the ELF header. + Header.e_shoff = sizeof(Header); + std::vector Sections = Doc.Sections; + if (Sections.empty() || Sections.front().Type != SHT_NULL) { + ELFYAML::Section S; + S.Type = SHT_NULL; + zero(S.Flags); + Sections.insert(Sections.begin(), S); + } + // "+ 1" for string table. + Header.e_shnum = Sections.size() + 1; + // Place section header string table last. + Header.e_shstrndx = Sections.size(); + + StringTableBuilder StrTab; + std::vector SHeaders; + for (unsigned i = 0, e = Sections.size(); i != e; ++i) { + const ELFYAML::Section &Sec = Sections[i]; + Elf_Shdr SHeader; + zero(SHeader); + SHeader.sh_name = StrTab.addString(Sec.Name); + SHeader.sh_type = Sec.Type; + SHeader.sh_flags = Sec.Flags; + SHeader.sh_addr = 0; + SHeader.sh_offset = 0; + SHeader.sh_size = 0; + SHeader.sh_link = 0; + SHeader.sh_info = 0; + SHeader.sh_addralign = 1; + SHeader.sh_entsize = 0; + SHeaders.push_back(SHeader); + } + + // String table header. + Elf_Shdr StrTabSHeader; + zero(StrTabSHeader); + StrTabSHeader.sh_name = 0; + StrTabSHeader.sh_type = SHT_STRTAB; + StrTabSHeader.sh_flags = 0; + StrTabSHeader.sh_addr = 0; + StrTabSHeader.sh_offset = Header.e_ehsize + Header.e_shentsize * Header.e_shnum; + StrTabSHeader.sh_size = StrTab.size(); + StrTabSHeader.sh_link = 0; + StrTabSHeader.sh_info = 0; + StrTabSHeader.sh_addralign = 1; + StrTabSHeader.sh_entsize = 0; OS.write((const char *)&Header, sizeof(Header)); + writeVectorData(OS, SHeaders); + OS.write((const char *)&StrTabSHeader, sizeof(StrTabSHeader)); + StrTab.writeToStream(OS); } int yaml2elf(llvm::raw_ostream &Out, llvm::MemoryBuffer *Buf) {