]> granicus.if.org Git - llvm/commitdiff
[yaml2obj][ELF] Add support for program headers
authorPetr Hosek <phosek@chromium.org>
Wed, 19 Jul 2017 20:38:46 +0000 (20:38 +0000)
committerPetr Hosek <phosek@chromium.org>
Wed, 19 Jul 2017 20:38:46 +0000 (20:38 +0000)
This change adds basic support for program headers.

I need to do some testing which requires generating program headers but
I can't use ld.lld or clang to produce programs that have headers. I'd
also like to test some strange things that those programs may never
produce.

Patch by Jake Ehrlich

Differential Revision: https://reviews.llvm.org/D35276

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@308520 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ObjectYAML/ELFYAML.h
lib/ObjectYAML/ELFYAML.cpp
test/tools/yaml2obj/program-header-nobits.yaml [new file with mode: 0644]
test/tools/yaml2obj/program-header.yaml [new file with mode: 0644]
tools/yaml2obj/yaml2elf.cpp

index ed455311696eab39f7dd3c53ebb9da49d9595cc6..c4a1fe2ce39bfc28849fac385540a768feebeb2a 100644 (file)
@@ -37,12 +37,14 @@ namespace ELFYAML {
 // 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)
@@ -71,6 +73,18 @@ struct FileHeader {
   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<SectionName> Sections;
+};
+
 struct Symbol {
   StringRef Name;
   ELF_STT Type;
@@ -183,6 +197,7 @@ struct MipsABIFlags : Section {
 
 struct Object {
   FileHeader Header;
+  std::vector<ProgramHeader> ProgramHeaders;
   std::vector<std::unique_ptr<Section>> 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
@@ -194,10 +209,12 @@ struct Object {
 } // end namespace ELFYAML
 } // end namespace llvm
 
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader)
 LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<llvm::ELFYAML::Section>)
 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 {
@@ -207,6 +224,10 @@ struct ScalarEnumerationTraits<ELFYAML::ELF_ET> {
   static void enumeration(IO &IO, ELFYAML::ELF_ET &Value);
 };
 
+template <> struct ScalarEnumerationTraits<ELFYAML::ELF_PT> {
+  static void enumeration(IO &IO, ELFYAML::ELF_PT &Value);
+};
+
 template <>
 struct ScalarEnumerationTraits<ELFYAML::ELF_EM> {
   static void enumeration(IO &IO, ELFYAML::ELF_EM &Value);
@@ -232,6 +253,10 @@ struct ScalarBitSetTraits<ELFYAML::ELF_EF> {
   static void bitset(IO &IO, ELFYAML::ELF_EF &Value);
 };
 
+template <> struct ScalarBitSetTraits<ELFYAML::ELF_PF> {
+  static void bitset(IO &IO, ELFYAML::ELF_PF &Value);
+};
+
 template <>
 struct ScalarEnumerationTraits<ELFYAML::ELF_SHT> {
   static void enumeration(IO &IO, ELFYAML::ELF_SHT &Value);
@@ -302,6 +327,10 @@ struct MappingTraits<ELFYAML::FileHeader> {
   static void mapping(IO &IO, ELFYAML::FileHeader &FileHdr);
 };
 
+template <> struct MappingTraits<ELFYAML::ProgramHeader> {
+  static void mapping(IO &IO, ELFYAML::ProgramHeader &FileHdr);
+};
+
 template <>
 struct MappingTraits<ELFYAML::Symbol> {
   static void mapping(IO &IO, ELFYAML::Symbol &Symbol);
@@ -331,6 +360,10 @@ template <> struct MappingTraits<ELFYAML::SectionOrType> {
   static void mapping(IO &IO, ELFYAML::SectionOrType &sectionOrType);
 };
 
+template <> struct MappingTraits<ELFYAML::SectionName> {
+  static void mapping(IO &IO, ELFYAML::SectionName &sectionName);
+};
+
 } // end namespace yaml
 } // end namespace llvm
 
index 39741dab327a5ed14fa497b24b216ecddb517511..fd0aa49445f9227ace5e1398bc2283317f090b19 100644 (file)
@@ -39,6 +39,21 @@ void ScalarEnumerationTraits<ELFYAML::ELF_ET>::enumeration(
   IO.enumFallback<Hex16>(Value);
 }
 
+void ScalarEnumerationTraits<ELFYAML::ELF_PT>::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<Hex32>(Value);
+}
+
 void ScalarEnumerationTraits<ELFYAML::ELF_EM>::enumeration(
     IO &IO, ELFYAML::ELF_EM &Value) {
 #define ECase(X) IO.enumCase(Value, #X, ELF::X)
@@ -412,6 +427,14 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::enumeration(
 #undef ECase
 }
 
+void ScalarBitSetTraits<ELFYAML::ELF_PF>::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<ELFYAML::ELF_SHF>::bitset(IO &IO,
                                                   ELFYAML::ELF_SHF &Value) {
   const auto *Object = static_cast<ELFYAML::Object *>(IO.getContext());
@@ -649,6 +672,15 @@ void MappingTraits<ELFYAML::FileHeader>::mapping(IO &IO,
   IO.mapOptional("Entry", FileHdr.Entry, Hex64(0));
 }
 
+void MappingTraits<ELFYAML::ProgramHeader>::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 {
@@ -720,6 +752,11 @@ void MappingTraits<ELFYAML::SectionOrType>::mapping(
   IO.mapRequired("SectionOrType", sectionOrType.sectionNameOrType);
 }
 
+void MappingTraits<ELFYAML::SectionName>::mapping(
+    IO &IO, ELFYAML::SectionName &sectionName) {
+  IO.mapRequired("Section", sectionName.Section);
+}
+
 static void sectionMapping(IO &IO, ELFYAML::MipsABIFlags &Section) {
   commonSectionMapping(IO, Section);
   IO.mapOptional("Version", Section.Version, Hex16(0));
@@ -837,6 +874,7 @@ void MappingTraits<ELFYAML::Object>::mapping(IO &IO, ELFYAML::Object &Object) {
   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);
diff --git a/test/tools/yaml2obj/program-header-nobits.yaml b/test/tools/yaml2obj/program-header-nobits.yaml
new file mode 100644 (file)
index 0000000..4664753
--- /dev/null
@@ -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:]
diff --git a/test/tools/yaml2obj/program-header.yaml b/test/tools/yaml2obj/program-header.yaml
new file mode 100644 (file)
index 0000000..f07e393
--- /dev/null
@@ -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 c89f768ed6ff5cd12902bf869be66ca396abbec5..1d28a1abffcf3a605f39dc4061add8ca59a94595 100644 (file)
@@ -99,6 +99,7 @@ namespace {
 template <class ELFT>
 class ELFState {
   typedef typename object::ELFFile<ELFT>::Elf_Ehdr Elf_Ehdr;
+  typedef typename object::ELFFile<ELFT>::Elf_Phdr Elf_Phdr;
   typedef typename object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
   typedef typename object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
   typedef typename object::ELFFile<ELFT>::Elf_Rel Elf_Rel;
@@ -118,6 +119,7 @@ class ELFState {
   bool buildSymbolIndex(std::size_t &StartIndex,
                         const std::vector<ELFYAML::Symbol> &Symbols);
   void initELFHeader(Elf_Ehdr &Header);
+  void initProgramHeaders(std::vector<Elf_Phdr> &PHeaders);
   bool initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
                           ContiguousBlobAccumulator &CBA);
   void initSymtabSectionHeader(Elf_Shdr &SHeader,
@@ -125,6 +127,8 @@ class ELFState {
   void initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name,
                                StringTableBuilder &STB,
                                ContiguousBlobAccumulator &CBA);
+  void setProgramHeaderLayout(std::vector<Elf_Phdr> &PHeaders,
+                              std::vector<Elf_Shdr> &SHeaders);
   void addSymbols(const std::vector<ELFYAML::Symbol> &Symbols,
                   std::vector<Elf_Sym> &Syms, unsigned SymbolBinding);
   void writeSectionContent(Elf_Shdr &SHeader,
@@ -173,15 +177,31 @@ void ELFState<ELFT>::initELFHeader(Elf_Ehdr &Header) {
   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 <class ELFT>
+void ELFState<ELFT>::initProgramHeaders(std::vector<Elf_Phdr> &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 <class ELFT>
 bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
                                         ContiguousBlobAccumulator &CBA) {
@@ -310,6 +330,67 @@ void ELFState<ELFT>::initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name,
   SHeader.sh_addralign = 1;
 }
 
+template <class ELFT>
+void ELFState<ELFT>::setProgramHeaderLayout(std::vector<Elf_Phdr> &PHeaders,
+                                            std::vector<Elf_Shdr> &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 <class ELFT>
 void ELFState<ELFT>::addSymbols(const std::vector<ELFYAML::Symbol> &Symbols,
                                 std::vector<Elf_Sym> &Syms,
@@ -508,12 +589,15 @@ int ELFState<ELFT>::writeELF(raw_ostream &OS, const ELFYAML::Object &Doc) {
   State.initELFHeader(Header);
 
   // TODO: Flesh out section header support.
-  // TODO: Program headers.
+
+  std::vector<Elf_Phdr> 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 @@ int ELFState<ELFT>::writeELF(raw_ostream &OS, const ELFYAML::Object &Doc) {
                                 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;