1 //===- yaml2elf - Convert YAML to a ELF object file -----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file 11 /// \brief The ELF component of yaml2obj. 12 /// 13 //===----------------------------------------------------------------------===// 14 15 #include "yaml2obj.h" 16 #include "llvm/ADT/ArrayRef.h" 17 #include "llvm/Object/ELFObjectFile.h" 18 #include "llvm/Object/ELFYAML.h" 19 #include "llvm/Support/ELF.h" 20 #include "llvm/Support/MemoryBuffer.h" 21 #include "llvm/Support/YAMLTraits.h" 22 #include "llvm/Support/raw_ostream.h" 23 24 using namespace llvm; 25 26 // There is similar code in yaml2coff, but with some slight COFF-specific 27 // variations like different initial state. Might be able to deduplicate 28 // some day, but also want to make sure that the Mach-O use case is served. 29 // 30 // This class has a deliberately small interface, since a lot of 31 // implementation variation is possible. 32 // 33 // TODO: Use an ordered container with a suffix-based comparison in order 34 // to deduplicate suffixes. std::map<> with a custom comparator is likely 35 // to be the simplest implementation, but a suffix trie could be more 36 // suitable for the job. 37 namespace { 38 class StringTableBuilder { 39 /// \brief Indices of strings currently present in `Buf`. 40 StringMap<unsigned> StringIndices; 41 /// \brief The contents of the string table as we build it. 42 std::string Buf; 43 public: 44 StringTableBuilder() { 45 Buf.push_back('\0'); 46 } 47 /// \returns Index of string in string table. 48 unsigned addString(StringRef S) { 49 StringMapEntry<unsigned> &Entry = StringIndices.GetOrCreateValue(S); 50 unsigned &I = Entry.getValue(); 51 if (I != 0) 52 return I; 53 I = Buf.size(); 54 Buf.append(S.begin(), S.end()); 55 Buf.push_back('\0'); 56 return I; 57 } 58 size_t size() const { 59 return Buf.size(); 60 } 61 void writeToStream(raw_ostream &OS) { 62 OS.write(Buf.data(), Buf.size()); 63 } 64 }; 65 } // end anonymous namespace 66 67 // This class is used to build up a contiguous binary blob while keeping 68 // track of an offset in the output (which notionally begins at 69 // `InitialOffset`). 70 namespace { 71 class ContiguousBlobAccumulator { 72 const uint64_t InitialOffset; 73 SmallVector<char, 128> Buf; 74 raw_svector_ostream OS; 75 76 /// \returns The new offset. 77 uint64_t padToAlignment(unsigned Align) { 78 uint64_t CurrentOffset = InitialOffset + OS.tell(); 79 uint64_t AlignedOffset = RoundUpToAlignment(CurrentOffset, Align); 80 for (; CurrentOffset != AlignedOffset; ++CurrentOffset) 81 OS.write('\0'); 82 return AlignedOffset; // == CurrentOffset; 83 } 84 85 public: 86 ContiguousBlobAccumulator(uint64_t InitialOffset_) 87 : InitialOffset(InitialOffset_), Buf(), OS(Buf) {} 88 template <class Integer> 89 raw_ostream &getOSAndAlignedOffset(Integer &Offset, unsigned Align = 16) { 90 Offset = padToAlignment(Align); 91 return OS; 92 } 93 void writeBlobToStream(raw_ostream &Out) { Out << OS.str(); } 94 }; 95 } // end anonymous namespace 96 97 // Used to keep track of section names, so that in the YAML file sections 98 // can be referenced by name instead of by index. 99 namespace { 100 class SectionNameToIdxMap { 101 StringMap<int> Map; 102 public: 103 /// \returns true if name is already present in the map. 104 bool addName(StringRef SecName, unsigned i) { 105 StringMapEntry<int> &Entry = Map.GetOrCreateValue(SecName, -1); 106 if (Entry.getValue() != -1) 107 return true; 108 Entry.setValue((int)i); 109 return false; 110 } 111 /// \returns true if name is not present in the map 112 bool lookupSection(StringRef SecName, unsigned &Idx) const { 113 StringMap<int>::const_iterator I = Map.find(SecName); 114 if (I == Map.end()) 115 return true; 116 Idx = I->getValue(); 117 return false; 118 } 119 }; 120 } // end anonymous namespace 121 122 template <class T> 123 static size_t arrayDataSize(ArrayRef<T> A) { 124 return A.size() * sizeof(T); 125 } 126 127 template <class T> 128 static void writeArrayData(raw_ostream &OS, ArrayRef<T> A) { 129 OS.write((const char *)A.data(), arrayDataSize(A)); 130 } 131 132 template <class T> 133 static void zero(T &Obj) { 134 memset(&Obj, 0, sizeof(Obj)); 135 } 136 137 /// \brief Create a string table in `SHeader`, which we assume is already 138 /// zero'd. 139 template <class Elf_Shdr> 140 static void createStringTableSectionHeader(Elf_Shdr &SHeader, 141 StringTableBuilder &STB, 142 ContiguousBlobAccumulator &CBA) { 143 SHeader.sh_type = ELF::SHT_STRTAB; 144 STB.writeToStream(CBA.getOSAndAlignedOffset(SHeader.sh_offset)); 145 SHeader.sh_size = STB.size(); 146 SHeader.sh_addralign = 1; 147 } 148 149 namespace { 150 /// \brief "Single point of truth" for the ELF file construction. 151 /// TODO: This class still has a ways to go before it is truly a "single 152 /// point of truth". 153 template <class ELFT> 154 class ELFState { 155 /// \brief The future ".strtab" section. 156 StringTableBuilder DotStrtab; 157 /// \brief The section number of the ".strtab" section. 158 unsigned DotStrtabSecNo; 159 /// \brief The accumulated contents of all sections so far. 160 ContiguousBlobAccumulator &SectionContentAccum; 161 typedef typename object::ELFFile<ELFT>::Elf_Ehdr Elf_Ehdr; 162 /// \brief The ELF file header. 163 Elf_Ehdr &Header; 164 165 SectionNameToIdxMap &SN2I; 166 167 public: 168 169 ELFState(Elf_Ehdr &Header_, ContiguousBlobAccumulator &Accum, 170 unsigned DotStrtabSecNo_, SectionNameToIdxMap &SN2I_) 171 : DotStrtab(), DotStrtabSecNo(DotStrtabSecNo_), 172 SectionContentAccum(Accum), Header(Header_), SN2I(SN2I_) {} 173 174 unsigned getDotStrTabSecNo() const { return DotStrtabSecNo; } 175 StringTableBuilder &getStringTable() { return DotStrtab; } 176 ContiguousBlobAccumulator &getSectionContentAccum() { 177 return SectionContentAccum; 178 } 179 SectionNameToIdxMap &getSN2I() { return SN2I; } 180 }; 181 } // end anonymous namespace 182 183 // FIXME: At this point it is fairly clear that we need to refactor these 184 // static functions into methods of a class sharing some typedefs. These 185 // ELF type names are insane. 186 template <class ELFT> 187 static void 188 addSymbols(const std::vector<ELFYAML::Symbol> &Symbols, ELFState<ELFT> &State, 189 std::vector<typename object::ELFFile<ELFT>::Elf_Sym> &Syms, 190 unsigned SymbolBinding) { 191 typedef typename object::ELFFile<ELFT>::Elf_Sym Elf_Sym; 192 for (unsigned i = 0, e = Symbols.size(); i != e; ++i) { 193 const ELFYAML::Symbol &Sym = Symbols[i]; 194 Elf_Sym Symbol; 195 zero(Symbol); 196 if (!Sym.Name.empty()) 197 Symbol.st_name = State.getStringTable().addString(Sym.Name); 198 Symbol.setBindingAndType(SymbolBinding, Sym.Type); 199 if (!Sym.Section.empty()) { 200 unsigned Index; 201 if (State.getSN2I().lookupSection(Sym.Section, Index)) { 202 errs() << "error: Unknown section referenced: '" << Sym.Section 203 << "' by YAML symbol " << Sym.Name << ".\n"; 204 exit(1); 205 } 206 Symbol.st_shndx = Index; 207 } // else Symbol.st_shndex == SHN_UNDEF (== 0), since it was zero'd earlier. 208 Symbol.st_value = Sym.Value; 209 Symbol.st_size = Sym.Size; 210 Syms.push_back(Symbol); 211 } 212 } 213 214 template <class ELFT> 215 static void 216 handleSymtabSectionHeader(const ELFYAML::LocalGlobalWeakSymbols &Symbols, 217 ELFState<ELFT> &State, 218 typename object::ELFFile<ELFT>::Elf_Shdr &SHeader) { 219 220 typedef typename object::ELFFile<ELFT>::Elf_Sym Elf_Sym; 221 SHeader.sh_type = ELF::SHT_SYMTAB; 222 SHeader.sh_link = State.getDotStrTabSecNo(); 223 // One greater than symbol table index of the last local symbol. 224 SHeader.sh_info = Symbols.Local.size() + 1; 225 SHeader.sh_entsize = sizeof(Elf_Sym); 226 227 std::vector<Elf_Sym> Syms; 228 { 229 // Ensure STN_UNDEF is present 230 Elf_Sym Sym; 231 zero(Sym); 232 Syms.push_back(Sym); 233 } 234 addSymbols(Symbols.Local, State, Syms, ELF::STB_LOCAL); 235 addSymbols(Symbols.Global, State, Syms, ELF::STB_GLOBAL); 236 addSymbols(Symbols.Weak, State, Syms, ELF::STB_WEAK); 237 238 ContiguousBlobAccumulator &CBA = State.getSectionContentAccum(); 239 writeArrayData(CBA.getOSAndAlignedOffset(SHeader.sh_offset), 240 makeArrayRef(Syms)); 241 SHeader.sh_size = arrayDataSize(makeArrayRef(Syms)); 242 } 243 244 template <class ELFT> 245 static int writeELF(raw_ostream &OS, const ELFYAML::Object &Doc) { 246 using namespace llvm::ELF; 247 typedef typename object::ELFFile<ELFT>::Elf_Ehdr Elf_Ehdr; 248 typedef typename object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr; 249 250 const ELFYAML::FileHeader &Hdr = Doc.Header; 251 252 Elf_Ehdr Header; 253 zero(Header); 254 Header.e_ident[EI_MAG0] = 0x7f; 255 Header.e_ident[EI_MAG1] = 'E'; 256 Header.e_ident[EI_MAG2] = 'L'; 257 Header.e_ident[EI_MAG3] = 'F'; 258 Header.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; 259 bool IsLittleEndian = ELFT::TargetEndianness == support::little; 260 Header.e_ident[EI_DATA] = IsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB; 261 Header.e_ident[EI_VERSION] = EV_CURRENT; 262 Header.e_ident[EI_OSABI] = Hdr.OSABI; 263 Header.e_ident[EI_ABIVERSION] = 0; 264 Header.e_type = Hdr.Type; 265 Header.e_machine = Hdr.Machine; 266 Header.e_version = EV_CURRENT; 267 Header.e_entry = Hdr.Entry; 268 Header.e_ehsize = sizeof(Elf_Ehdr); 269 270 // TODO: Flesh out section header support. 271 // TODO: Program headers. 272 273 Header.e_shentsize = sizeof(Elf_Shdr); 274 // Immediately following the ELF header. 275 Header.e_shoff = sizeof(Header); 276 const std::vector<ELFYAML::Section> &Sections = Doc.Sections; 277 // "+ 4" for 278 // - SHT_NULL entry (placed first, i.e. 0'th entry) 279 // - symbol table (.symtab) (placed third to last) 280 // - string table (.strtab) (placed second to last) 281 // - section header string table. (placed last) 282 Header.e_shnum = Sections.size() + 4; 283 // Place section header string table last. 284 Header.e_shstrndx = Header.e_shnum - 1; 285 const unsigned DotStrtabSecNo = Header.e_shnum - 2; 286 287 // XXX: This offset is tightly coupled with the order that we write 288 // things to `OS`. 289 const size_t SectionContentBeginOffset = 290 Header.e_ehsize + Header.e_shentsize * Header.e_shnum; 291 ContiguousBlobAccumulator CBA(SectionContentBeginOffset); 292 SectionNameToIdxMap SN2I; 293 for (unsigned i = 0, e = Sections.size(); i != e; ++i) { 294 StringRef Name = Sections[i].Name; 295 if (Name.empty()) 296 continue; 297 // "+ 1" to take into account the SHT_NULL entry. 298 if (SN2I.addName(Name, i + 1)) { 299 errs() << "error: Repeated section name: '" << Name 300 << "' at YAML section number " << i << ".\n"; 301 return 1; 302 } 303 } 304 305 ELFState<ELFT> State(Header, CBA, DotStrtabSecNo, SN2I); 306 307 StringTableBuilder SHStrTab; 308 std::vector<Elf_Shdr> SHeaders; 309 { 310 // Ensure SHN_UNDEF entry is present. An all-zero section header is a 311 // valid SHN_UNDEF entry since SHT_NULL == 0. 312 Elf_Shdr SHdr; 313 zero(SHdr); 314 SHeaders.push_back(SHdr); 315 } 316 for (unsigned i = 0, e = Sections.size(); i != e; ++i) { 317 const ELFYAML::Section &Sec = Sections[i]; 318 Elf_Shdr SHeader; 319 zero(SHeader); 320 SHeader.sh_name = SHStrTab.addString(Sec.Name); 321 SHeader.sh_type = Sec.Type; 322 SHeader.sh_flags = Sec.Flags; 323 SHeader.sh_addr = Sec.Address; 324 325 Sec.Content.writeAsBinary(CBA.getOSAndAlignedOffset(SHeader.sh_offset)); 326 SHeader.sh_size = Sec.Content.binary_size(); 327 328 if (!Sec.Link.empty()) { 329 unsigned Index; 330 if (SN2I.lookupSection(Sec.Link, Index)) { 331 errs() << "error: Unknown section referenced: '" << Sec.Link 332 << "' at YAML section number " << i << ".\n"; 333 return 1; 334 } 335 SHeader.sh_link = Index; 336 } 337 SHeader.sh_info = 0; 338 SHeader.sh_addralign = Sec.AddressAlign; 339 SHeader.sh_entsize = 0; 340 SHeaders.push_back(SHeader); 341 } 342 343 // .symtab section. 344 Elf_Shdr SymtabSHeader; 345 zero(SymtabSHeader); 346 SymtabSHeader.sh_name = SHStrTab.addString(StringRef(".symtab")); 347 handleSymtabSectionHeader<ELFT>(Doc.Symbols, State, SymtabSHeader); 348 SHeaders.push_back(SymtabSHeader); 349 350 // .strtab string table header. 351 Elf_Shdr DotStrTabSHeader; 352 zero(DotStrTabSHeader); 353 DotStrTabSHeader.sh_name = SHStrTab.addString(StringRef(".strtab")); 354 createStringTableSectionHeader(DotStrTabSHeader, State.getStringTable(), CBA); 355 SHeaders.push_back(DotStrTabSHeader); 356 357 // Section header string table header. 358 Elf_Shdr SHStrTabSHeader; 359 zero(SHStrTabSHeader); 360 createStringTableSectionHeader(SHStrTabSHeader, SHStrTab, CBA); 361 SHeaders.push_back(SHStrTabSHeader); 362 363 OS.write((const char *)&Header, sizeof(Header)); 364 writeArrayData(OS, makeArrayRef(SHeaders)); 365 CBA.writeBlobToStream(OS); 366 return 0; 367 } 368 369 static bool is64Bit(const ELFYAML::Object &Doc) { 370 return Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64); 371 } 372 373 static bool isLittleEndian(const ELFYAML::Object &Doc) { 374 return Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB); 375 } 376 377 int yaml2elf(llvm::raw_ostream &Out, llvm::MemoryBuffer *Buf) { 378 yaml::Input YIn(Buf->getBuffer()); 379 ELFYAML::Object Doc; 380 YIn >> Doc; 381 if (YIn.error()) { 382 errs() << "yaml2obj: Failed to parse YAML file!\n"; 383 return 1; 384 } 385 using object::ELFType; 386 typedef ELFType<support::little, 8, true> LE64; 387 typedef ELFType<support::big, 8, true> BE64; 388 typedef ELFType<support::little, 4, false> LE32; 389 typedef ELFType<support::big, 4, false> BE32; 390 if (is64Bit(Doc)) { 391 if (isLittleEndian(Doc)) 392 return writeELF<LE64>(outs(), Doc); 393 else 394 return writeELF<BE64>(outs(), Doc); 395 } else { 396 if (isLittleEndian(Doc)) 397 return writeELF<LE32>(outs(), Doc); 398 else 399 return writeELF<BE32>(outs(), Doc); 400 } 401 } 402