1 //===- ELFObjHandler.cpp --------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===-----------------------------------------------------------------------===/ 8 9 #include "llvm/InterfaceStub/ELFObjHandler.h" 10 #include "llvm/InterfaceStub/ELFStub.h" 11 #include "llvm/MC/StringTableBuilder.h" 12 #include "llvm/Object/Binary.h" 13 #include "llvm/Object/ELFObjectFile.h" 14 #include "llvm/Object/ELFTypes.h" 15 #include "llvm/Support/Errc.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/FileOutputBuffer.h" 18 #include "llvm/Support/MathExtras.h" 19 #include "llvm/Support/MemoryBuffer.h" 20 21 using llvm::MemoryBufferRef; 22 using llvm::object::ELFObjectFile; 23 24 using namespace llvm; 25 using namespace llvm::object; 26 using namespace llvm::ELF; 27 28 namespace llvm { 29 namespace elfabi { 30 31 // Simple struct to hold relevant .dynamic entries. 32 struct DynamicEntries { 33 uint64_t StrTabAddr = 0; 34 uint64_t StrSize = 0; 35 Optional<uint64_t> SONameOffset; 36 std::vector<uint64_t> NeededLibNames; 37 // Symbol table: 38 uint64_t DynSymAddr = 0; 39 // Hash tables: 40 Optional<uint64_t> ElfHash; 41 Optional<uint64_t> GnuHash; 42 }; 43 44 /// This initializes an ELF file header with information specific to a binary 45 /// dynamic shared object. 46 /// Offsets, indexes, links, etc. for section and program headers are just 47 /// zero-initialized as they will be updated elsewhere. 48 /// 49 /// @param ElfHeader Target ELFT::Ehdr to populate. 50 /// @param Machine Target architecture (e_machine from ELF specifications). 51 template <class ELFT> 52 static void initELFHeader(typename ELFT::Ehdr &ElfHeader, uint16_t Machine) { 53 memset(&ElfHeader, 0, sizeof(ElfHeader)); 54 // ELF identification. 55 ElfHeader.e_ident[EI_MAG0] = ElfMagic[EI_MAG0]; 56 ElfHeader.e_ident[EI_MAG1] = ElfMagic[EI_MAG1]; 57 ElfHeader.e_ident[EI_MAG2] = ElfMagic[EI_MAG2]; 58 ElfHeader.e_ident[EI_MAG3] = ElfMagic[EI_MAG3]; 59 ElfHeader.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; 60 bool IsLittleEndian = ELFT::TargetEndianness == support::little; 61 ElfHeader.e_ident[EI_DATA] = IsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB; 62 ElfHeader.e_ident[EI_VERSION] = EV_CURRENT; 63 ElfHeader.e_ident[EI_OSABI] = ELFOSABI_NONE; 64 65 // Remainder of ELF header. 66 ElfHeader.e_type = ET_DYN; 67 ElfHeader.e_machine = Machine; 68 ElfHeader.e_version = EV_CURRENT; 69 ElfHeader.e_ehsize = sizeof(typename ELFT::Ehdr); 70 ElfHeader.e_phentsize = sizeof(typename ELFT::Phdr); 71 ElfHeader.e_shentsize = sizeof(typename ELFT::Shdr); 72 } 73 74 namespace { 75 template <class ELFT> struct OutputSection { 76 using Elf_Shdr = typename ELFT::Shdr; 77 std::string Name; 78 Elf_Shdr Shdr; 79 uint64_t Addr; 80 uint64_t Offset; 81 uint64_t Size; 82 uint64_t Align; 83 uint32_t Index; 84 bool NoBits = true; 85 }; 86 87 template <class T, class ELFT> 88 struct ContentSection : public OutputSection<ELFT> { 89 T Content; 90 ContentSection() { this->NoBits = false; } 91 }; 92 93 // This class just wraps StringTableBuilder for the purpose of adding a 94 // default constructor. 95 class ELFStringTableBuilder : public StringTableBuilder { 96 public: 97 ELFStringTableBuilder() : StringTableBuilder(StringTableBuilder::ELF) {} 98 }; 99 100 template <class ELFT> class ELFStubBuilder { 101 public: 102 using Elf_Ehdr = typename ELFT::Ehdr; 103 using Elf_Shdr = typename ELFT::Shdr; 104 using Elf_Phdr = typename ELFT::Phdr; 105 using Elf_Sym = typename ELFT::Sym; 106 using Elf_Addr = typename ELFT::Addr; 107 using Elf_Dyn = typename ELFT::Dyn; 108 109 ELFStubBuilder(const ELFStubBuilder &) = delete; 110 ELFStubBuilder(ELFStubBuilder &&) = default; 111 112 explicit ELFStubBuilder(const ELFStub &Stub) { 113 // Populate string tables. 114 ShStrTab.Name = ".shstrtab"; 115 ShStrTab.Align = 1; 116 DynStr.Name = ".dynstr"; 117 DynStr.Align = 1; 118 for (const ELFSymbol &Sym : Stub.Symbols) 119 DynStr.Content.add(Sym.Name); 120 121 std::vector<OutputSection<ELFT> *> Sections = {&DynStr, &ShStrTab}; 122 const OutputSection<ELFT> *LastSection = Sections.back(); 123 // Now set the Index and put sections names into ".shstrtab". 124 uint64_t Index = 1; 125 for (OutputSection<ELFT> *Sec : Sections) { 126 Sec->Index = Index++; 127 ShStrTab.Content.add(Sec->Name); 128 } 129 ShStrTab.Content.finalize(); 130 ShStrTab.Size = ShStrTab.Content.getSize(); 131 DynStr.Content.finalize(); 132 DynStr.Size = DynStr.Content.getSize(); 133 // Calculate sections' addresses and offsets. 134 uint64_t CurrentOffset = sizeof(Elf_Ehdr); 135 for (OutputSection<ELFT> *Sec : Sections) { 136 Sec->Offset = alignTo(CurrentOffset, Sec->Align); 137 Sec->Addr = Sec->Offset; 138 CurrentOffset = Sec->Offset + Sec->Size; 139 } 140 // Write section headers of string tables. 141 fillStrTabShdr(DynStr, SHF_ALLOC); 142 fillStrTabShdr(ShStrTab); 143 // Finish initializing the ELF header. 144 initELFHeader<ELFT>(ElfHeader, Stub.Arch); 145 ElfHeader.e_shstrndx = ShStrTab.Index; 146 ElfHeader.e_shnum = LastSection->Index + 1; 147 ElfHeader.e_shoff = 148 alignTo(LastSection->Offset + LastSection->Size, sizeof(Elf_Addr)); 149 } 150 151 size_t getSize() const { 152 return ElfHeader.e_shoff + ElfHeader.e_shnum * sizeof(Elf_Shdr); 153 } 154 155 void write(uint8_t *Data) const { 156 write(Data, ElfHeader); 157 DynStr.Content.write(Data + DynStr.Shdr.sh_offset); 158 ShStrTab.Content.write(Data + ShStrTab.Shdr.sh_offset); 159 writeShdr(Data, DynStr); 160 writeShdr(Data, ShStrTab); 161 } 162 163 private: 164 Elf_Ehdr ElfHeader; 165 ContentSection<ELFStringTableBuilder, ELFT> DynStr; 166 ContentSection<ELFStringTableBuilder, ELFT> ShStrTab; 167 168 template <class T> static void write(uint8_t *Data, const T &Value) { 169 *reinterpret_cast<T *>(Data) = Value; 170 } 171 172 void fillStrTabShdr(ContentSection<ELFStringTableBuilder, ELFT> &StrTab, 173 uint32_t ShFlags = 0) const { 174 StrTab.Shdr.sh_type = SHT_STRTAB; 175 StrTab.Shdr.sh_flags = ShFlags; 176 StrTab.Shdr.sh_addr = StrTab.Addr; 177 StrTab.Shdr.sh_offset = StrTab.Offset; 178 StrTab.Shdr.sh_info = 0; 179 StrTab.Shdr.sh_size = StrTab.Size; 180 StrTab.Shdr.sh_name = ShStrTab.Content.getOffset(StrTab.Name); 181 StrTab.Shdr.sh_addralign = StrTab.Align; 182 StrTab.Shdr.sh_entsize = 0; 183 StrTab.Shdr.sh_link = 0; 184 } 185 186 uint64_t shdrOffset(const OutputSection<ELFT> &Sec) const { 187 return ElfHeader.e_shoff + Sec.Index * sizeof(Elf_Shdr); 188 } 189 190 void writeShdr(uint8_t *Data, const OutputSection<ELFT> &Sec) const { 191 write(Data + shdrOffset(Sec), Sec.Shdr); 192 } 193 }; 194 } // end anonymous namespace 195 196 /// This function behaves similarly to StringRef::substr(), but attempts to 197 /// terminate the returned StringRef at the first null terminator. If no null 198 /// terminator is found, an error is returned. 199 /// 200 /// @param Str Source string to create a substring from. 201 /// @param Offset The start index of the desired substring. 202 static Expected<StringRef> terminatedSubstr(StringRef Str, size_t Offset) { 203 size_t StrEnd = Str.find('\0', Offset); 204 if (StrEnd == StringLiteral::npos) { 205 return createError( 206 "String overran bounds of string table (no null terminator)"); 207 } 208 209 size_t StrLen = StrEnd - Offset; 210 return Str.substr(Offset, StrLen); 211 } 212 213 /// This function takes an error, and appends a string of text to the end of 214 /// that error. Since "appending" to an Error isn't supported behavior of an 215 /// Error, this function technically creates a new error with the combined 216 /// message and consumes the old error. 217 /// 218 /// @param Err Source error. 219 /// @param After Text to append at the end of Err's error message. 220 Error appendToError(Error Err, StringRef After) { 221 std::string Message; 222 raw_string_ostream Stream(Message); 223 Stream << Err; 224 Stream << " " << After; 225 consumeError(std::move(Err)); 226 return createError(Stream.str().c_str()); 227 } 228 229 /// This function populates a DynamicEntries struct using an ELFT::DynRange. 230 /// After populating the struct, the members are validated with 231 /// some basic sanity checks. 232 /// 233 /// @param Dyn Target DynamicEntries struct to populate. 234 /// @param DynTable Source dynamic table. 235 template <class ELFT> 236 static Error populateDynamic(DynamicEntries &Dyn, 237 typename ELFT::DynRange DynTable) { 238 if (DynTable.empty()) 239 return createError("No .dynamic section found"); 240 241 // Search .dynamic for relevant entries. 242 bool FoundDynStr = false; 243 bool FoundDynStrSz = false; 244 bool FoundDynSym = false; 245 for (auto &Entry : DynTable) { 246 switch (Entry.d_tag) { 247 case DT_SONAME: 248 Dyn.SONameOffset = Entry.d_un.d_val; 249 break; 250 case DT_STRTAB: 251 Dyn.StrTabAddr = Entry.d_un.d_ptr; 252 FoundDynStr = true; 253 break; 254 case DT_STRSZ: 255 Dyn.StrSize = Entry.d_un.d_val; 256 FoundDynStrSz = true; 257 break; 258 case DT_NEEDED: 259 Dyn.NeededLibNames.push_back(Entry.d_un.d_val); 260 break; 261 case DT_SYMTAB: 262 Dyn.DynSymAddr = Entry.d_un.d_ptr; 263 FoundDynSym = true; 264 break; 265 case DT_HASH: 266 Dyn.ElfHash = Entry.d_un.d_ptr; 267 break; 268 case DT_GNU_HASH: 269 Dyn.GnuHash = Entry.d_un.d_ptr; 270 } 271 } 272 273 if (!FoundDynStr) { 274 return createError( 275 "Couldn't locate dynamic string table (no DT_STRTAB entry)"); 276 } 277 if (!FoundDynStrSz) { 278 return createError( 279 "Couldn't determine dynamic string table size (no DT_STRSZ entry)"); 280 } 281 if (!FoundDynSym) { 282 return createError( 283 "Couldn't locate dynamic symbol table (no DT_SYMTAB entry)"); 284 } 285 if (Dyn.SONameOffset.hasValue() && *Dyn.SONameOffset >= Dyn.StrSize) { 286 return createStringError(object_error::parse_failed, 287 "DT_SONAME string offset (0x%016" PRIx64 288 ") outside of dynamic string table", 289 *Dyn.SONameOffset); 290 } 291 for (uint64_t Offset : Dyn.NeededLibNames) { 292 if (Offset >= Dyn.StrSize) { 293 return createStringError(object_error::parse_failed, 294 "DT_NEEDED string offset (0x%016" PRIx64 295 ") outside of dynamic string table", 296 Offset); 297 } 298 } 299 300 return Error::success(); 301 } 302 303 /// This function finds the number of dynamic symbols using a GNU hash table. 304 /// 305 /// @param Table The GNU hash table for .dynsym. 306 template <class ELFT> 307 static uint64_t getDynSymtabSize(const typename ELFT::GnuHash &Table) { 308 using Elf_Word = typename ELFT::Word; 309 if (Table.nbuckets == 0) 310 return Table.symndx + 1; 311 uint64_t LastSymIdx = 0; 312 uint64_t BucketVal = 0; 313 // Find the index of the first symbol in the last chain. 314 for (Elf_Word Val : Table.buckets()) { 315 BucketVal = std::max(BucketVal, (uint64_t)Val); 316 } 317 LastSymIdx += BucketVal; 318 const Elf_Word *It = 319 reinterpret_cast<const Elf_Word *>(Table.values(BucketVal).end()); 320 // Locate the end of the chain to find the last symbol index. 321 while ((*It & 1) == 0) { 322 LastSymIdx++; 323 It++; 324 } 325 return LastSymIdx + 1; 326 } 327 328 /// This function determines the number of dynamic symbols. 329 /// Without access to section headers, the number of symbols must be determined 330 /// by parsing dynamic hash tables. 331 /// 332 /// @param Dyn Entries with the locations of hash tables. 333 /// @param ElfFile The ElfFile that the section contents reside in. 334 template <class ELFT> 335 static Expected<uint64_t> getNumSyms(DynamicEntries &Dyn, 336 const ELFFile<ELFT> &ElfFile) { 337 using Elf_Hash = typename ELFT::Hash; 338 using Elf_GnuHash = typename ELFT::GnuHash; 339 // Search GNU hash table to try to find the upper bound of dynsym. 340 if (Dyn.GnuHash.hasValue()) { 341 Expected<const uint8_t *> TablePtr = ElfFile.toMappedAddr(*Dyn.GnuHash); 342 if (!TablePtr) 343 return TablePtr.takeError(); 344 const Elf_GnuHash *Table = 345 reinterpret_cast<const Elf_GnuHash *>(TablePtr.get()); 346 return getDynSymtabSize<ELFT>(*Table); 347 } 348 // Search SYSV hash table to try to find the upper bound of dynsym. 349 if (Dyn.ElfHash.hasValue()) { 350 Expected<const uint8_t *> TablePtr = ElfFile.toMappedAddr(*Dyn.ElfHash); 351 if (!TablePtr) 352 return TablePtr.takeError(); 353 const Elf_Hash *Table = reinterpret_cast<const Elf_Hash *>(TablePtr.get()); 354 return Table->nchain; 355 } 356 return 0; 357 } 358 359 /// This function extracts symbol type from a symbol's st_info member and 360 /// maps it to an ELFSymbolType enum. 361 /// Currently, STT_NOTYPE, STT_OBJECT, STT_FUNC, and STT_TLS are supported. 362 /// Other symbol types are mapped to ELFSymbolType::Unknown. 363 /// 364 /// @param Info Binary symbol st_info to extract symbol type from. 365 static ELFSymbolType convertInfoToType(uint8_t Info) { 366 Info = Info & 0xf; 367 switch (Info) { 368 case ELF::STT_NOTYPE: 369 return ELFSymbolType::NoType; 370 case ELF::STT_OBJECT: 371 return ELFSymbolType::Object; 372 case ELF::STT_FUNC: 373 return ELFSymbolType::Func; 374 case ELF::STT_TLS: 375 return ELFSymbolType::TLS; 376 default: 377 return ELFSymbolType::Unknown; 378 } 379 } 380 381 /// This function creates an ELFSymbol and populates all members using 382 /// information from a binary ELFT::Sym. 383 /// 384 /// @param SymName The desired name of the ELFSymbol. 385 /// @param RawSym ELFT::Sym to extract symbol information from. 386 template <class ELFT> 387 static ELFSymbol createELFSym(StringRef SymName, 388 const typename ELFT::Sym &RawSym) { 389 ELFSymbol TargetSym{std::string(SymName)}; 390 uint8_t Binding = RawSym.getBinding(); 391 if (Binding == STB_WEAK) 392 TargetSym.Weak = true; 393 else 394 TargetSym.Weak = false; 395 396 TargetSym.Undefined = RawSym.isUndefined(); 397 TargetSym.Type = convertInfoToType(RawSym.st_info); 398 399 if (TargetSym.Type == ELFSymbolType::Func) { 400 TargetSym.Size = 0; 401 } else { 402 TargetSym.Size = RawSym.st_size; 403 } 404 return TargetSym; 405 } 406 407 /// This function populates an ELFStub with symbols using information read 408 /// from an ELF binary. 409 /// 410 /// @param TargetStub ELFStub to add symbols to. 411 /// @param DynSym Range of dynamic symbols to add to TargetStub. 412 /// @param DynStr StringRef to the dynamic string table. 413 template <class ELFT> 414 static Error populateSymbols(ELFStub &TargetStub, 415 const typename ELFT::SymRange DynSym, 416 StringRef DynStr) { 417 // Skips the first symbol since it's the NULL symbol. 418 for (auto RawSym : DynSym.drop_front(1)) { 419 // If a symbol does not have global or weak binding, ignore it. 420 uint8_t Binding = RawSym.getBinding(); 421 if (!(Binding == STB_GLOBAL || Binding == STB_WEAK)) 422 continue; 423 // If a symbol doesn't have default or protected visibility, ignore it. 424 uint8_t Visibility = RawSym.getVisibility(); 425 if (!(Visibility == STV_DEFAULT || Visibility == STV_PROTECTED)) 426 continue; 427 // Create an ELFSymbol and populate it with information from the symbol 428 // table entry. 429 Expected<StringRef> SymName = terminatedSubstr(DynStr, RawSym.st_name); 430 if (!SymName) 431 return SymName.takeError(); 432 ELFSymbol Sym = createELFSym<ELFT>(*SymName, RawSym); 433 TargetStub.Symbols.insert(std::move(Sym)); 434 // TODO: Populate symbol warning. 435 } 436 return Error::success(); 437 } 438 439 /// Returns a new ELFStub with all members populated from an ELFObjectFile. 440 /// @param ElfObj Source ELFObjectFile. 441 template <class ELFT> 442 static Expected<std::unique_ptr<ELFStub>> 443 buildStub(const ELFObjectFile<ELFT> &ElfObj) { 444 using Elf_Dyn_Range = typename ELFT::DynRange; 445 using Elf_Phdr_Range = typename ELFT::PhdrRange; 446 using Elf_Sym_Range = typename ELFT::SymRange; 447 using Elf_Sym = typename ELFT::Sym; 448 std::unique_ptr<ELFStub> DestStub = std::make_unique<ELFStub>(); 449 const ELFFile<ELFT> *ElfFile = ElfObj.getELFFile(); 450 // Fetch .dynamic table. 451 Expected<Elf_Dyn_Range> DynTable = ElfFile->dynamicEntries(); 452 if (!DynTable) { 453 return DynTable.takeError(); 454 } 455 456 // Fetch program headers. 457 Expected<Elf_Phdr_Range> PHdrs = ElfFile->program_headers(); 458 if (!PHdrs) { 459 return PHdrs.takeError(); 460 } 461 462 // Collect relevant .dynamic entries. 463 DynamicEntries DynEnt; 464 if (Error Err = populateDynamic<ELFT>(DynEnt, *DynTable)) 465 return std::move(Err); 466 467 // Get pointer to in-memory location of .dynstr section. 468 Expected<const uint8_t *> DynStrPtr = 469 ElfFile->toMappedAddr(DynEnt.StrTabAddr); 470 if (!DynStrPtr) 471 return appendToError(DynStrPtr.takeError(), 472 "when locating .dynstr section contents"); 473 474 StringRef DynStr(reinterpret_cast<const char *>(DynStrPtr.get()), 475 DynEnt.StrSize); 476 477 // Populate Arch from ELF header. 478 DestStub->Arch = ElfFile->getHeader().e_machine; 479 480 // Populate SoName from .dynamic entries and dynamic string table. 481 if (DynEnt.SONameOffset.hasValue()) { 482 Expected<StringRef> NameOrErr = 483 terminatedSubstr(DynStr, *DynEnt.SONameOffset); 484 if (!NameOrErr) { 485 return appendToError(NameOrErr.takeError(), "when reading DT_SONAME"); 486 } 487 DestStub->SoName = std::string(*NameOrErr); 488 } 489 490 // Populate NeededLibs from .dynamic entries and dynamic string table. 491 for (uint64_t NeededStrOffset : DynEnt.NeededLibNames) { 492 Expected<StringRef> LibNameOrErr = 493 terminatedSubstr(DynStr, NeededStrOffset); 494 if (!LibNameOrErr) { 495 return appendToError(LibNameOrErr.takeError(), "when reading DT_NEEDED"); 496 } 497 DestStub->NeededLibs.push_back(std::string(*LibNameOrErr)); 498 } 499 500 // Populate Symbols from .dynsym table and dynamic string table. 501 Expected<uint64_t> SymCount = getNumSyms(DynEnt, *ElfFile); 502 if (!SymCount) 503 return SymCount.takeError(); 504 if (*SymCount > 0) { 505 // Get pointer to in-memory location of .dynsym section. 506 Expected<const uint8_t *> DynSymPtr = 507 ElfFile->toMappedAddr(DynEnt.DynSymAddr); 508 if (!DynSymPtr) 509 return appendToError(DynSymPtr.takeError(), 510 "when locating .dynsym section contents"); 511 Elf_Sym_Range DynSyms = ArrayRef<Elf_Sym>( 512 reinterpret_cast<const Elf_Sym *>(*DynSymPtr), *SymCount); 513 Error SymReadError = populateSymbols<ELFT>(*DestStub, DynSyms, DynStr); 514 if (SymReadError) 515 return appendToError(std::move(SymReadError), 516 "when reading dynamic symbols"); 517 } 518 519 return std::move(DestStub); 520 } 521 522 /// This function opens a file for writing and then writes a binary ELF stub to 523 /// the file. 524 /// 525 /// @param FilePath File path for writing the ELF binary. 526 /// @param Stub Source ELFStub to generate a binary ELF stub from. 527 template <class ELFT> 528 static Error writeELFBinaryToFile(StringRef FilePath, const ELFStub &Stub) { 529 ELFStubBuilder<ELFT> Builder{Stub}; 530 Expected<std::unique_ptr<FileOutputBuffer>> BufOrError = 531 FileOutputBuffer::create(FilePath, Builder.getSize()); 532 if (!BufOrError) 533 return createStringError(errc::invalid_argument, 534 toString(BufOrError.takeError()) + 535 " when trying to open `" + FilePath + 536 "` for writing"); 537 538 // Write binary to file. 539 std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufOrError); 540 Builder.write(Buf->getBufferStart()); 541 542 if (Error E = Buf->commit()) 543 return E; 544 545 return Error::success(); 546 } 547 548 Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) { 549 Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buf); 550 if (!BinOrErr) { 551 return BinOrErr.takeError(); 552 } 553 554 Binary *Bin = BinOrErr->get(); 555 if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { 556 return buildStub(*Obj); 557 } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { 558 return buildStub(*Obj); 559 } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { 560 return buildStub(*Obj); 561 } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { 562 return buildStub(*Obj); 563 } 564 return createStringError(errc::not_supported, "unsupported binary format"); 565 } 566 567 // This function wraps the ELFT writeELFBinaryToFile() so writeBinaryStub() 568 // can be called without having to use ELFType templates directly. 569 Error writeBinaryStub(StringRef FilePath, const ELFStub &Stub, 570 ELFTarget OutputFormat) { 571 if (OutputFormat == ELFTarget::ELF32LE) 572 return writeELFBinaryToFile<ELF32LE>(FilePath, Stub); 573 if (OutputFormat == ELFTarget::ELF32BE) 574 return writeELFBinaryToFile<ELF32BE>(FilePath, Stub); 575 if (OutputFormat == ELFTarget::ELF64LE) 576 return writeELFBinaryToFile<ELF64LE>(FilePath, Stub); 577 if (OutputFormat == ELFTarget::ELF64BE) 578 return writeELFBinaryToFile<ELF64BE>(FilePath, Stub); 579 llvm_unreachable("invalid binary output target"); 580 } 581 582 } // end namespace elfabi 583 } // end namespace llvm 584