Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 1 | //===- ELFObjHandler.cpp --------------------------------------------------===// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 6 | // |
| 7 | //===-----------------------------------------------------------------------===/ |
| 8 | |
| 9 | #include "ELFObjHandler.h" |
| 10 | #include "llvm/Object/Binary.h" |
| 11 | #include "llvm/Object/ELFObjectFile.h" |
| 12 | #include "llvm/Object/ELFTypes.h" |
| 13 | #include "llvm/Support/Errc.h" |
| 14 | #include "llvm/Support/Error.h" |
| 15 | #include "llvm/Support/MemoryBuffer.h" |
| 16 | #include "llvm/TextAPI/ELF/ELFStub.h" |
| 17 | |
| 18 | using llvm::MemoryBufferRef; |
| 19 | using llvm::object::ELFObjectFile; |
| 20 | |
| 21 | using namespace llvm; |
| 22 | using namespace llvm::object; |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 23 | using namespace llvm::ELF; |
| 24 | |
| 25 | namespace llvm { |
| 26 | namespace elfabi { |
| 27 | |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 28 | // Simple struct to hold relevant .dynamic entries. |
| 29 | struct DynamicEntries { |
| 30 | uint64_t StrTabAddr = 0; |
| 31 | uint64_t StrSize = 0; |
| 32 | Optional<uint64_t> SONameOffset; |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame] | 33 | std::vector<uint64_t> NeededLibNames; |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 34 | // Symbol table: |
| 35 | uint64_t DynSymAddr = 0; |
| 36 | // Hash tables: |
| 37 | Optional<uint64_t> ElfHash; |
| 38 | Optional<uint64_t> GnuHash; |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 39 | }; |
| 40 | |
| 41 | /// This function behaves similarly to StringRef::substr(), but attempts to |
| 42 | /// terminate the returned StringRef at the first null terminator. If no null |
| 43 | /// terminator is found, an error is returned. |
| 44 | /// |
| 45 | /// @param Str Source string to create a substring from. |
| 46 | /// @param Offset The start index of the desired substring. |
| 47 | static Expected<StringRef> terminatedSubstr(StringRef Str, size_t Offset) { |
| 48 | size_t StrEnd = Str.find('\0', Offset); |
| 49 | if (StrEnd == StringLiteral::npos) { |
| 50 | return createError( |
| 51 | "String overran bounds of string table (no null terminator)"); |
| 52 | } |
| 53 | |
| 54 | size_t StrLen = StrEnd - Offset; |
| 55 | return Str.substr(Offset, StrLen); |
| 56 | } |
| 57 | |
| 58 | /// This function takes an error, and appends a string of text to the end of |
| 59 | /// that error. Since "appending" to an Error isn't supported behavior of an |
| 60 | /// Error, this function technically creates a new error with the combined |
| 61 | /// message and consumes the old error. |
| 62 | /// |
| 63 | /// @param Err Source error. |
| 64 | /// @param After Text to append at the end of Err's error message. |
| 65 | Error appendToError(Error Err, StringRef After) { |
| 66 | std::string Message; |
| 67 | raw_string_ostream Stream(Message); |
| 68 | Stream << Err; |
| 69 | Stream << " " << After; |
| 70 | consumeError(std::move(Err)); |
| 71 | return createError(Stream.str().c_str()); |
| 72 | } |
| 73 | |
| 74 | /// This function populates a DynamicEntries struct using an ELFT::DynRange. |
| 75 | /// After populating the struct, the members are validated with |
| 76 | /// some basic sanity checks. |
| 77 | /// |
| 78 | /// @param Dyn Target DynamicEntries struct to populate. |
| 79 | /// @param DynTable Source dynamic table. |
| 80 | template <class ELFT> |
| 81 | static Error populateDynamic(DynamicEntries &Dyn, |
| 82 | typename ELFT::DynRange DynTable) { |
| 83 | if (DynTable.empty()) |
| 84 | return createError("No .dynamic section found"); |
| 85 | |
| 86 | // Search .dynamic for relevant entries. |
| 87 | bool FoundDynStr = false; |
| 88 | bool FoundDynStrSz = false; |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 89 | bool FoundDynSym = false; |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 90 | for (auto &Entry : DynTable) { |
| 91 | switch (Entry.d_tag) { |
| 92 | case DT_SONAME: |
| 93 | Dyn.SONameOffset = Entry.d_un.d_val; |
| 94 | break; |
| 95 | case DT_STRTAB: |
| 96 | Dyn.StrTabAddr = Entry.d_un.d_ptr; |
| 97 | FoundDynStr = true; |
| 98 | break; |
| 99 | case DT_STRSZ: |
| 100 | Dyn.StrSize = Entry.d_un.d_val; |
| 101 | FoundDynStrSz = true; |
| 102 | break; |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame] | 103 | case DT_NEEDED: |
| 104 | Dyn.NeededLibNames.push_back(Entry.d_un.d_val); |
| 105 | break; |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 106 | case DT_SYMTAB: |
| 107 | Dyn.DynSymAddr = Entry.d_un.d_ptr; |
| 108 | FoundDynSym = true; |
| 109 | break; |
| 110 | case DT_HASH: |
| 111 | Dyn.ElfHash = Entry.d_un.d_ptr; |
| 112 | break; |
| 113 | case DT_GNU_HASH: |
| 114 | Dyn.GnuHash = Entry.d_un.d_ptr; |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 115 | } |
| 116 | } |
| 117 | |
| 118 | if (!FoundDynStr) { |
| 119 | return createError( |
| 120 | "Couldn't locate dynamic string table (no DT_STRTAB entry)"); |
| 121 | } |
| 122 | if (!FoundDynStrSz) { |
| 123 | return createError( |
| 124 | "Couldn't determine dynamic string table size (no DT_STRSZ entry)"); |
| 125 | } |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 126 | if (!FoundDynSym) { |
| 127 | return createError( |
| 128 | "Couldn't locate dynamic symbol table (no DT_SYMTAB entry)"); |
| 129 | } |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 130 | if (Dyn.SONameOffset.hasValue() && *Dyn.SONameOffset >= Dyn.StrSize) { |
| 131 | return createStringError( |
| 132 | object_error::parse_failed, |
Petar Jovanovic | b33f00f | 2019-02-05 22:23:46 +0000 | [diff] [blame] | 133 | "DT_SONAME string offset (0x%016" PRIx64 |
| 134 | ") outside of dynamic string table", |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 135 | *Dyn.SONameOffset); |
| 136 | } |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame] | 137 | for (uint64_t Offset : Dyn.NeededLibNames) { |
| 138 | if (Offset >= Dyn.StrSize) { |
| 139 | return createStringError( |
| 140 | object_error::parse_failed, |
Petar Jovanovic | b33f00f | 2019-02-05 22:23:46 +0000 | [diff] [blame] | 141 | "DT_NEEDED string offset (0x%016" PRIx64 |
| 142 | ") outside of dynamic string table", |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame] | 143 | Offset); |
| 144 | } |
| 145 | } |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 146 | |
| 147 | return Error::success(); |
| 148 | } |
| 149 | |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 150 | /// This function finds the number of dynamic symbols using a GNU hash table. |
| 151 | /// |
| 152 | /// @param Table The GNU hash table for .dynsym. |
| 153 | template <class ELFT> |
| 154 | static uint64_t getDynSymtabSize(const typename ELFT::GnuHash &Table) { |
| 155 | using Elf_Word = typename ELFT::Word; |
| 156 | if (Table.nbuckets == 0) |
| 157 | return Table.symndx + 1; |
| 158 | uint64_t LastSymIdx = 0; |
| 159 | uint64_t BucketVal = 0; |
| 160 | // Find the index of the first symbol in the last chain. |
| 161 | for (Elf_Word Val : Table.buckets()) { |
| 162 | BucketVal = std::max(BucketVal, (uint64_t)Val); |
| 163 | } |
| 164 | LastSymIdx += BucketVal; |
| 165 | const Elf_Word *It = |
| 166 | reinterpret_cast<const Elf_Word *>(Table.values(BucketVal).end()); |
| 167 | // Locate the end of the chain to find the last symbol index. |
| 168 | while ((*It & 1) == 0) { |
| 169 | LastSymIdx++; |
| 170 | It++; |
| 171 | } |
| 172 | return LastSymIdx + 1; |
| 173 | } |
| 174 | |
| 175 | /// This function determines the number of dynamic symbols. |
| 176 | /// Without access to section headers, the number of symbols must be determined |
| 177 | /// by parsing dynamic hash tables. |
| 178 | /// |
| 179 | /// @param Dyn Entries with the locations of hash tables. |
| 180 | /// @param ElfFile The ElfFile that the section contents reside in. |
| 181 | template <class ELFT> |
| 182 | static Expected<uint64_t> getNumSyms(DynamicEntries &Dyn, |
| 183 | const ELFFile<ELFT> &ElfFile) { |
| 184 | using Elf_Hash = typename ELFT::Hash; |
| 185 | using Elf_GnuHash = typename ELFT::GnuHash; |
| 186 | // Search GNU hash table to try to find the upper bound of dynsym. |
| 187 | if (Dyn.GnuHash.hasValue()) { |
| 188 | Expected<const uint8_t *> TablePtr = ElfFile.toMappedAddr(*Dyn.GnuHash); |
| 189 | if (!TablePtr) |
| 190 | return TablePtr.takeError(); |
| 191 | const Elf_GnuHash *Table = |
| 192 | reinterpret_cast<const Elf_GnuHash *>(TablePtr.get()); |
| 193 | return getDynSymtabSize<ELFT>(*Table); |
| 194 | } |
| 195 | // Search SYSV hash table to try to find the upper bound of dynsym. |
| 196 | if (Dyn.ElfHash.hasValue()) { |
| 197 | Expected<const uint8_t *> TablePtr = ElfFile.toMappedAddr(*Dyn.ElfHash); |
| 198 | if (!TablePtr) |
| 199 | return TablePtr.takeError(); |
| 200 | const Elf_Hash *Table = reinterpret_cast<const Elf_Hash *>(TablePtr.get()); |
| 201 | return Table->nchain; |
| 202 | } |
| 203 | return 0; |
| 204 | } |
| 205 | |
| 206 | /// This function extracts symbol type from a symbol's st_info member and |
| 207 | /// maps it to an ELFSymbolType enum. |
| 208 | /// Currently, STT_NOTYPE, STT_OBJECT, STT_FUNC, and STT_TLS are supported. |
| 209 | /// Other symbol types are mapped to ELFSymbolType::Unknown. |
| 210 | /// |
| 211 | /// @param Info Binary symbol st_info to extract symbol type from. |
| 212 | static ELFSymbolType convertInfoToType(uint8_t Info) { |
| 213 | Info = Info & 0xf; |
| 214 | switch (Info) { |
| 215 | case ELF::STT_NOTYPE: |
| 216 | return ELFSymbolType::NoType; |
| 217 | case ELF::STT_OBJECT: |
| 218 | return ELFSymbolType::Object; |
| 219 | case ELF::STT_FUNC: |
| 220 | return ELFSymbolType::Func; |
| 221 | case ELF::STT_TLS: |
| 222 | return ELFSymbolType::TLS; |
| 223 | default: |
| 224 | return ELFSymbolType::Unknown; |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | /// This function creates an ELFSymbol and populates all members using |
| 229 | /// information from a binary ELFT::Sym. |
| 230 | /// |
| 231 | /// @param SymName The desired name of the ELFSymbol. |
| 232 | /// @param RawSym ELFT::Sym to extract symbol information from. |
| 233 | template <class ELFT> |
| 234 | static ELFSymbol createELFSym(StringRef SymName, |
| 235 | const typename ELFT::Sym &RawSym) { |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 236 | ELFSymbol TargetSym{std::string(SymName)}; |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 237 | uint8_t Binding = RawSym.getBinding(); |
| 238 | if (Binding == STB_WEAK) |
| 239 | TargetSym.Weak = true; |
| 240 | else |
| 241 | TargetSym.Weak = false; |
| 242 | |
| 243 | TargetSym.Undefined = RawSym.isUndefined(); |
| 244 | TargetSym.Type = convertInfoToType(RawSym.st_info); |
| 245 | |
| 246 | if (TargetSym.Type == ELFSymbolType::Func) { |
| 247 | TargetSym.Size = 0; |
| 248 | } else { |
| 249 | TargetSym.Size = RawSym.st_size; |
| 250 | } |
| 251 | return TargetSym; |
| 252 | } |
| 253 | |
| 254 | /// This function populates an ELFStub with symbols using information read |
| 255 | /// from an ELF binary. |
| 256 | /// |
| 257 | /// @param TargetStub ELFStub to add symbols to. |
| 258 | /// @param DynSym Range of dynamic symbols to add to TargetStub. |
| 259 | /// @param DynStr StringRef to the dynamic string table. |
| 260 | template <class ELFT> |
| 261 | static Error populateSymbols(ELFStub &TargetStub, |
| 262 | const typename ELFT::SymRange DynSym, |
| 263 | StringRef DynStr) { |
| 264 | // Skips the first symbol since it's the NULL symbol. |
| 265 | for (auto RawSym : DynSym.drop_front(1)) { |
| 266 | // If a symbol does not have global or weak binding, ignore it. |
| 267 | uint8_t Binding = RawSym.getBinding(); |
| 268 | if (!(Binding == STB_GLOBAL || Binding == STB_WEAK)) |
| 269 | continue; |
| 270 | // If a symbol doesn't have default or protected visibility, ignore it. |
| 271 | uint8_t Visibility = RawSym.getVisibility(); |
| 272 | if (!(Visibility == STV_DEFAULT || Visibility == STV_PROTECTED)) |
| 273 | continue; |
| 274 | // Create an ELFSymbol and populate it with information from the symbol |
| 275 | // table entry. |
| 276 | Expected<StringRef> SymName = terminatedSubstr(DynStr, RawSym.st_name); |
| 277 | if (!SymName) |
| 278 | return SymName.takeError(); |
| 279 | ELFSymbol Sym = createELFSym<ELFT>(*SymName, RawSym); |
| 280 | TargetStub.Symbols.insert(std::move(Sym)); |
| 281 | // TODO: Populate symbol warning. |
| 282 | } |
| 283 | return Error::success(); |
| 284 | } |
| 285 | |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 286 | /// Returns a new ELFStub with all members populated from an ELFObjectFile. |
| 287 | /// @param ElfObj Source ELFObjectFile. |
| 288 | template <class ELFT> |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 289 | static Expected<std::unique_ptr<ELFStub>> |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 290 | buildStub(const ELFObjectFile<ELFT> &ElfObj) { |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 291 | using Elf_Dyn_Range = typename ELFT::DynRange; |
| 292 | using Elf_Phdr_Range = typename ELFT::PhdrRange; |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 293 | using Elf_Sym_Range = typename ELFT::SymRange; |
| 294 | using Elf_Sym = typename ELFT::Sym; |
Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 295 | std::unique_ptr<ELFStub> DestStub = std::make_unique<ELFStub>(); |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 296 | const ELFFile<ELFT> *ElfFile = ElfObj.getELFFile(); |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 297 | // Fetch .dynamic table. |
| 298 | Expected<Elf_Dyn_Range> DynTable = ElfFile->dynamicEntries(); |
| 299 | if (!DynTable) { |
| 300 | return DynTable.takeError(); |
| 301 | } |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 302 | |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 303 | // Fetch program headers. |
| 304 | Expected<Elf_Phdr_Range> PHdrs = ElfFile->program_headers(); |
| 305 | if (!PHdrs) { |
| 306 | return PHdrs.takeError(); |
| 307 | } |
| 308 | |
| 309 | // Collect relevant .dynamic entries. |
| 310 | DynamicEntries DynEnt; |
| 311 | if (Error Err = populateDynamic<ELFT>(DynEnt, *DynTable)) |
Bill Wendling | c55cf4a | 2020-02-10 07:06:45 -0800 | [diff] [blame] | 312 | return std::move(Err); |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 313 | |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 314 | // Get pointer to in-memory location of .dynstr section. |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 315 | Expected<const uint8_t *> DynStrPtr = |
| 316 | ElfFile->toMappedAddr(DynEnt.StrTabAddr); |
| 317 | if (!DynStrPtr) |
| 318 | return appendToError(DynStrPtr.takeError(), |
| 319 | "when locating .dynstr section contents"); |
| 320 | |
| 321 | StringRef DynStr(reinterpret_cast<const char *>(DynStrPtr.get()), |
| 322 | DynEnt.StrSize); |
| 323 | |
| 324 | // Populate Arch from ELF header. |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 325 | DestStub->Arch = ElfFile->getHeader()->e_machine; |
| 326 | |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 327 | // Populate SoName from .dynamic entries and dynamic string table. |
| 328 | if (DynEnt.SONameOffset.hasValue()) { |
| 329 | Expected<StringRef> NameOrErr = |
| 330 | terminatedSubstr(DynStr, *DynEnt.SONameOffset); |
| 331 | if (!NameOrErr) { |
| 332 | return appendToError(NameOrErr.takeError(), "when reading DT_SONAME"); |
| 333 | } |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 334 | DestStub->SoName = std::string(*NameOrErr); |
Armando Montanez | fe7ab3c | 2019-01-16 17:47:16 +0000 | [diff] [blame] | 335 | } |
| 336 | |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame] | 337 | // Populate NeededLibs from .dynamic entries and dynamic string table. |
| 338 | for (uint64_t NeededStrOffset : DynEnt.NeededLibNames) { |
| 339 | Expected<StringRef> LibNameOrErr = |
| 340 | terminatedSubstr(DynStr, NeededStrOffset); |
| 341 | if (!LibNameOrErr) { |
| 342 | return appendToError(LibNameOrErr.takeError(), "when reading DT_NEEDED"); |
| 343 | } |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 344 | DestStub->NeededLibs.push_back(std::string(*LibNameOrErr)); |
Armando Montanez | 56d1812 | 2019-01-18 20:56:03 +0000 | [diff] [blame] | 345 | } |
| 346 | |
Armando Montanez | 8367b07 | 2019-01-24 22:39:21 +0000 | [diff] [blame] | 347 | // Populate Symbols from .dynsym table and dynamic string table. |
| 348 | Expected<uint64_t> SymCount = getNumSyms(DynEnt, *ElfFile); |
| 349 | if (!SymCount) |
| 350 | return SymCount.takeError(); |
| 351 | if (*SymCount > 0) { |
| 352 | // Get pointer to in-memory location of .dynsym section. |
| 353 | Expected<const uint8_t *> DynSymPtr = |
| 354 | ElfFile->toMappedAddr(DynEnt.DynSymAddr); |
| 355 | if (!DynSymPtr) |
| 356 | return appendToError(DynSymPtr.takeError(), |
| 357 | "when locating .dynsym section contents"); |
| 358 | Elf_Sym_Range DynSyms = |
| 359 | ArrayRef<Elf_Sym>(reinterpret_cast<const Elf_Sym *>(*DynSymPtr), |
| 360 | *SymCount); |
| 361 | Error SymReadError = populateSymbols<ELFT>(*DestStub, DynSyms, DynStr); |
| 362 | if (SymReadError) |
| 363 | return appendToError(std::move(SymReadError), |
| 364 | "when reading dynamic symbols"); |
| 365 | } |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 366 | |
Bill Wendling | c55cf4a | 2020-02-10 07:06:45 -0800 | [diff] [blame] | 367 | return std::move(DestStub); |
Armando Montanez | 31f0f65 | 2019-01-03 18:32:36 +0000 | [diff] [blame] | 368 | } |
| 369 | |
| 370 | Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) { |
| 371 | Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buf); |
| 372 | if (!BinOrErr) { |
| 373 | return BinOrErr.takeError(); |
| 374 | } |
| 375 | |
| 376 | Binary *Bin = BinOrErr->get(); |
| 377 | if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { |
| 378 | return buildStub(*Obj); |
| 379 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { |
| 380 | return buildStub(*Obj); |
| 381 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { |
| 382 | return buildStub(*Obj); |
| 383 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { |
| 384 | return buildStub(*Obj); |
| 385 | } |
| 386 | |
| 387 | return createStringError(errc::not_supported, "Unsupported binary format"); |
| 388 | } |
| 389 | |
| 390 | } // end namespace elfabi |
| 391 | } // end namespace llvm |