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/IFSStub.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 | #include <optional> |
21 | |
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 ifs { |
30 | |
31 | // Simple struct to hold relevant .dynamic entries. |
32 | struct DynamicEntries { |
33 | uint64_t StrTabAddr = 0; |
34 | uint64_t StrSize = 0; |
35 | std::optional<uint64_t> SONameOffset; |
36 | std::vector<uint64_t> NeededLibNames; |
37 | // Symbol table: |
38 | uint64_t DynSymAddr = 0; |
39 | // Hash tables: |
40 | std::optional<uint64_t> ElfHash; |
41 | std::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 (typename ELFT::Ehdr &, 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::Endianness == llvm::endianness::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 ELFSymbolTableBuilder { |
101 | public: |
102 | using Elf_Sym = typename ELFT::Sym; |
103 | |
104 | ELFSymbolTableBuilder() { Symbols.push_back({}); } |
105 | |
106 | void add(size_t StNameOffset, uint64_t StSize, uint8_t StBind, uint8_t StType, |
107 | uint8_t StOther, uint16_t StShndx) { |
108 | Elf_Sym S{}; |
109 | S.st_name = StNameOffset; |
110 | S.st_size = StSize; |
111 | S.st_info = (StBind << 4) | (StType & 0xf); |
112 | S.st_other = StOther; |
113 | S.st_shndx = StShndx; |
114 | Symbols.push_back(S); |
115 | } |
116 | |
117 | size_t getSize() const { return Symbols.size() * sizeof(Elf_Sym); } |
118 | |
119 | void write(uint8_t *Buf) const { |
120 | memcpy(Buf, Symbols.data(), sizeof(Elf_Sym) * Symbols.size()); |
121 | } |
122 | |
123 | private: |
124 | llvm::SmallVector<Elf_Sym, 8> Symbols; |
125 | }; |
126 | |
127 | template <class ELFT> class ELFDynamicTableBuilder { |
128 | public: |
129 | using Elf_Dyn = typename ELFT::Dyn; |
130 | |
131 | size_t addAddr(uint64_t Tag, uint64_t Addr) { |
132 | Elf_Dyn Entry; |
133 | Entry.d_tag = Tag; |
134 | Entry.d_un.d_ptr = Addr; |
135 | Entries.push_back(Entry); |
136 | return Entries.size() - 1; |
137 | } |
138 | |
139 | void modifyAddr(size_t Index, uint64_t Addr) { |
140 | Entries[Index].d_un.d_ptr = Addr; |
141 | } |
142 | |
143 | size_t addValue(uint64_t Tag, uint64_t Value) { |
144 | Elf_Dyn Entry; |
145 | Entry.d_tag = Tag; |
146 | Entry.d_un.d_val = Value; |
147 | Entries.push_back(Entry); |
148 | return Entries.size() - 1; |
149 | } |
150 | |
151 | void modifyValue(size_t Index, uint64_t Value) { |
152 | Entries[Index].d_un.d_val = Value; |
153 | } |
154 | |
155 | size_t getSize() const { |
156 | // Add DT_NULL entry at the end. |
157 | return (Entries.size() + 1) * sizeof(Elf_Dyn); |
158 | } |
159 | |
160 | void write(uint8_t *Buf) const { |
161 | memcpy(Buf, Entries.data(), sizeof(Elf_Dyn) * Entries.size()); |
162 | // Add DT_NULL entry at the end. |
163 | memset(Buf + sizeof(Elf_Dyn) * Entries.size(), 0, sizeof(Elf_Dyn)); |
164 | } |
165 | |
166 | private: |
167 | llvm::SmallVector<Elf_Dyn, 8> Entries; |
168 | }; |
169 | |
170 | template <class ELFT> class ELFStubBuilder { |
171 | public: |
172 | using Elf_Ehdr = typename ELFT::Ehdr; |
173 | using Elf_Shdr = typename ELFT::Shdr; |
174 | using Elf_Phdr = typename ELFT::Phdr; |
175 | using Elf_Sym = typename ELFT::Sym; |
176 | using Elf_Addr = typename ELFT::Addr; |
177 | using Elf_Dyn = typename ELFT::Dyn; |
178 | |
179 | ELFStubBuilder(const ELFStubBuilder &) = delete; |
180 | ELFStubBuilder(ELFStubBuilder &&) = default; |
181 | |
182 | explicit ELFStubBuilder(const IFSStub &Stub) { |
183 | DynSym.Name = ".dynsym" ; |
184 | DynSym.Align = sizeof(Elf_Addr); |
185 | DynStr.Name = ".dynstr" ; |
186 | DynStr.Align = 1; |
187 | DynTab.Name = ".dynamic" ; |
188 | DynTab.Align = sizeof(Elf_Addr); |
189 | ShStrTab.Name = ".shstrtab" ; |
190 | ShStrTab.Align = 1; |
191 | |
192 | // Populate string tables. |
193 | for (const IFSSymbol &Sym : Stub.Symbols) |
194 | DynStr.Content.add(Sym.Name); |
195 | for (const std::string &Lib : Stub.NeededLibs) |
196 | DynStr.Content.add(Lib); |
197 | if (Stub.SoName) |
198 | DynStr.Content.add(*Stub.SoName); |
199 | |
200 | std::vector<OutputSection<ELFT> *> Sections = {&DynSym, &DynStr, &DynTab, |
201 | &ShStrTab}; |
202 | const OutputSection<ELFT> *LastSection = Sections.back(); |
203 | // Now set the Index and put sections names into ".shstrtab". |
204 | uint64_t Index = 1; |
205 | for (OutputSection<ELFT> *Sec : Sections) { |
206 | Sec->Index = Index++; |
207 | ShStrTab.Content.add(Sec->Name); |
208 | } |
209 | ShStrTab.Content.finalize(); |
210 | ShStrTab.Size = ShStrTab.Content.getSize(); |
211 | DynStr.Content.finalize(); |
212 | DynStr.Size = DynStr.Content.getSize(); |
213 | |
214 | // Populate dynamic symbol table. |
215 | for (const IFSSymbol &Sym : Stub.Symbols) { |
216 | uint8_t Bind = Sym.Weak ? STB_WEAK : STB_GLOBAL; |
217 | // For non-undefined symbols, value of the shndx is not relevant at link |
218 | // time as long as it is not SHN_UNDEF. Set shndx to 1, which |
219 | // points to ".dynsym". |
220 | uint16_t Shndx = Sym.Undefined ? SHN_UNDEF : 1; |
221 | uint64_t Size = Sym.Size.value_or(u: 0); |
222 | DynSym.Content.add(DynStr.Content.getOffset(Sym.Name), Size, Bind, |
223 | convertIFSSymbolTypeToELF(SymbolType: Sym.Type), 0, Shndx); |
224 | } |
225 | DynSym.Size = DynSym.Content.getSize(); |
226 | |
227 | // Poplulate dynamic table. |
228 | size_t DynSymIndex = DynTab.Content.addAddr(DT_SYMTAB, 0); |
229 | size_t DynStrIndex = DynTab.Content.addAddr(DT_STRTAB, 0); |
230 | DynTab.Content.addValue(DT_STRSZ, DynSym.Size); |
231 | for (const std::string &Lib : Stub.NeededLibs) |
232 | DynTab.Content.addValue(DT_NEEDED, DynStr.Content.getOffset(Lib)); |
233 | if (Stub.SoName) |
234 | DynTab.Content.addValue(DT_SONAME, |
235 | DynStr.Content.getOffset(*Stub.SoName)); |
236 | DynTab.Size = DynTab.Content.getSize(); |
237 | // Calculate sections' addresses and offsets. |
238 | uint64_t CurrentOffset = sizeof(Elf_Ehdr); |
239 | for (OutputSection<ELFT> *Sec : Sections) { |
240 | Sec->Offset = alignTo(CurrentOffset, Sec->Align); |
241 | Sec->Addr = Sec->Offset; |
242 | CurrentOffset = Sec->Offset + Sec->Size; |
243 | } |
244 | // Fill Addr back to dynamic table. |
245 | DynTab.Content.modifyAddr(DynSymIndex, DynSym.Addr); |
246 | DynTab.Content.modifyAddr(DynStrIndex, DynStr.Addr); |
247 | // Write section headers of string tables. |
248 | fillSymTabShdr(SymTab&: DynSym, ShType: SHT_DYNSYM); |
249 | fillStrTabShdr(StrTab&: DynStr, ShFlags: SHF_ALLOC); |
250 | fillDynTabShdr(DynTab); |
251 | fillStrTabShdr(StrTab&: ShStrTab); |
252 | |
253 | // Finish initializing the ELF header. |
254 | initELFHeader<ELFT>(ElfHeader, static_cast<uint16_t>(*Stub.Target.Arch)); |
255 | ElfHeader.e_shstrndx = ShStrTab.Index; |
256 | ElfHeader.e_shnum = LastSection->Index + 1; |
257 | ElfHeader.e_shoff = |
258 | alignTo(LastSection->Offset + LastSection->Size, sizeof(Elf_Addr)); |
259 | } |
260 | |
261 | size_t getSize() const { |
262 | return ElfHeader.e_shoff + ElfHeader.e_shnum * sizeof(Elf_Shdr); |
263 | } |
264 | |
265 | void write(uint8_t *Data) const { |
266 | write(Data, ElfHeader); |
267 | DynSym.Content.write(Data + DynSym.Shdr.sh_offset); |
268 | DynStr.Content.write(Data + DynStr.Shdr.sh_offset); |
269 | DynTab.Content.write(Data + DynTab.Shdr.sh_offset); |
270 | ShStrTab.Content.write(Data + ShStrTab.Shdr.sh_offset); |
271 | writeShdr(Data, Sec: DynSym); |
272 | writeShdr(Data, Sec: DynStr); |
273 | writeShdr(Data, Sec: DynTab); |
274 | writeShdr(Data, Sec: ShStrTab); |
275 | } |
276 | |
277 | private: |
278 | Elf_Ehdr ; |
279 | ContentSection<ELFStringTableBuilder, ELFT> DynStr; |
280 | ContentSection<ELFStringTableBuilder, ELFT> ShStrTab; |
281 | ContentSection<ELFSymbolTableBuilder<ELFT>, ELFT> DynSym; |
282 | ContentSection<ELFDynamicTableBuilder<ELFT>, ELFT> DynTab; |
283 | |
284 | template <class T> static void write(uint8_t *Data, const T &Value) { |
285 | *reinterpret_cast<T *>(Data) = Value; |
286 | } |
287 | |
288 | void fillStrTabShdr(ContentSection<ELFStringTableBuilder, ELFT> &StrTab, |
289 | uint32_t ShFlags = 0) const { |
290 | StrTab.Shdr.sh_type = SHT_STRTAB; |
291 | StrTab.Shdr.sh_flags = ShFlags; |
292 | StrTab.Shdr.sh_addr = StrTab.Addr; |
293 | StrTab.Shdr.sh_offset = StrTab.Offset; |
294 | StrTab.Shdr.sh_info = 0; |
295 | StrTab.Shdr.sh_size = StrTab.Size; |
296 | StrTab.Shdr.sh_name = ShStrTab.Content.getOffset(StrTab.Name); |
297 | StrTab.Shdr.sh_addralign = StrTab.Align; |
298 | StrTab.Shdr.sh_entsize = 0; |
299 | StrTab.Shdr.sh_link = 0; |
300 | } |
301 | void fillSymTabShdr(ContentSection<ELFSymbolTableBuilder<ELFT>, ELFT> &SymTab, |
302 | uint32_t ShType) const { |
303 | SymTab.Shdr.sh_type = ShType; |
304 | SymTab.Shdr.sh_flags = SHF_ALLOC; |
305 | SymTab.Shdr.sh_addr = SymTab.Addr; |
306 | SymTab.Shdr.sh_offset = SymTab.Offset; |
307 | // Only non-local symbols are included in the tbe file, so .dynsym only |
308 | // contains 1 local symbol (the undefined symbol at index 0). The sh_info |
309 | // should always be 1. |
310 | SymTab.Shdr.sh_info = 1; |
311 | SymTab.Shdr.sh_size = SymTab.Size; |
312 | SymTab.Shdr.sh_name = this->ShStrTab.Content.getOffset(SymTab.Name); |
313 | SymTab.Shdr.sh_addralign = SymTab.Align; |
314 | SymTab.Shdr.sh_entsize = sizeof(Elf_Sym); |
315 | SymTab.Shdr.sh_link = this->DynStr.Index; |
316 | } |
317 | void fillDynTabShdr( |
318 | ContentSection<ELFDynamicTableBuilder<ELFT>, ELFT> &DynTab) const { |
319 | DynTab.Shdr.sh_type = SHT_DYNAMIC; |
320 | DynTab.Shdr.sh_flags = SHF_ALLOC; |
321 | DynTab.Shdr.sh_addr = DynTab.Addr; |
322 | DynTab.Shdr.sh_offset = DynTab.Offset; |
323 | DynTab.Shdr.sh_info = 0; |
324 | DynTab.Shdr.sh_size = DynTab.Size; |
325 | DynTab.Shdr.sh_name = this->ShStrTab.Content.getOffset(DynTab.Name); |
326 | DynTab.Shdr.sh_addralign = DynTab.Align; |
327 | DynTab.Shdr.sh_entsize = sizeof(Elf_Dyn); |
328 | DynTab.Shdr.sh_link = this->DynStr.Index; |
329 | } |
330 | uint64_t shdrOffset(const OutputSection<ELFT> &Sec) const { |
331 | return ElfHeader.e_shoff + Sec.Index * sizeof(Elf_Shdr); |
332 | } |
333 | |
334 | void writeShdr(uint8_t *Data, const OutputSection<ELFT> &Sec) const { |
335 | write(Data + shdrOffset(Sec), Sec.Shdr); |
336 | } |
337 | }; |
338 | |
339 | /// This function takes an error, and appends a string of text to the end of |
340 | /// that error. Since "appending" to an Error isn't supported behavior of an |
341 | /// Error, this function technically creates a new error with the combined |
342 | /// message and consumes the old error. |
343 | /// |
344 | /// @param Err Source error. |
345 | /// @param After Text to append at the end of Err's error message. |
346 | Error appendToError(Error Err, StringRef After) { |
347 | std::string Message; |
348 | raw_string_ostream Stream(Message); |
349 | Stream << Err; |
350 | Stream << " " << After; |
351 | consumeError(Err: std::move(Err)); |
352 | return createError(Err: Stream.str()); |
353 | } |
354 | |
355 | template <class ELFT> class DynSym { |
356 | using Elf_Shdr_Range = typename ELFT::ShdrRange; |
357 | using Elf_Shdr = typename ELFT::Shdr; |
358 | |
359 | public: |
360 | static Expected<DynSym> create(const ELFFile<ELFT> &ElfFile, |
361 | const DynamicEntries &DynEnt) { |
362 | Expected<Elf_Shdr_Range> Shdrs = ElfFile.sections(); |
363 | if (!Shdrs) |
364 | return Shdrs.takeError(); |
365 | return DynSym(ElfFile, DynEnt, *Shdrs); |
366 | } |
367 | |
368 | Expected<const uint8_t *> getDynSym() { |
369 | if (DynSymHdr) |
370 | return ElfFile.base() + DynSymHdr->sh_offset; |
371 | return getDynamicData(EntAddr: DynEnt.DynSymAddr, Name: "dynamic symbol table" ); |
372 | } |
373 | |
374 | Expected<StringRef> getDynStr() { |
375 | if (DynSymHdr) |
376 | return ElfFile.getStringTableForSymtab(*DynSymHdr, Shdrs); |
377 | Expected<const uint8_t *> DataOrErr = getDynamicData( |
378 | EntAddr: DynEnt.StrTabAddr, Name: "dynamic string table" , Size: DynEnt.StrSize); |
379 | if (!DataOrErr) |
380 | return DataOrErr.takeError(); |
381 | return StringRef(reinterpret_cast<const char *>(*DataOrErr), |
382 | DynEnt.StrSize); |
383 | } |
384 | |
385 | private: |
386 | DynSym(const ELFFile<ELFT> &ElfFile, const DynamicEntries &DynEnt, |
387 | Elf_Shdr_Range Shdrs) |
388 | : ElfFile(ElfFile), DynEnt(DynEnt), Shdrs(Shdrs), |
389 | DynSymHdr(findDynSymHdr()) {} |
390 | |
391 | const Elf_Shdr *findDynSymHdr() { |
392 | for (const Elf_Shdr &Sec : Shdrs) |
393 | if (Sec.sh_type == SHT_DYNSYM) { |
394 | // If multiple .dynsym are present, use the first one. |
395 | // This behavior aligns with llvm::object::ELFFile::getDynSymtabSize() |
396 | return &Sec; |
397 | } |
398 | return nullptr; |
399 | } |
400 | |
401 | Expected<const uint8_t *> getDynamicData(uint64_t EntAddr, StringRef Name, |
402 | uint64_t Size = 0) { |
403 | Expected<const uint8_t *> SecPtr = ElfFile.toMappedAddr(EntAddr); |
404 | if (!SecPtr) |
405 | return appendToError( |
406 | Err: SecPtr.takeError(), |
407 | After: ("when locating " + Name + " section contents" ).str()); |
408 | Expected<const uint8_t *> SecEndPtr = ElfFile.toMappedAddr(EntAddr + Size); |
409 | if (!SecEndPtr) |
410 | return appendToError( |
411 | Err: SecEndPtr.takeError(), |
412 | After: ("when locating " + Name + " section contents" ).str()); |
413 | return *SecPtr; |
414 | } |
415 | |
416 | const ELFFile<ELFT> &ElfFile; |
417 | const DynamicEntries &DynEnt; |
418 | Elf_Shdr_Range Shdrs; |
419 | const Elf_Shdr *DynSymHdr; |
420 | }; |
421 | } // end anonymous namespace |
422 | |
423 | /// This function behaves similarly to StringRef::substr(), but attempts to |
424 | /// terminate the returned StringRef at the first null terminator. If no null |
425 | /// terminator is found, an error is returned. |
426 | /// |
427 | /// @param Str Source string to create a substring from. |
428 | /// @param Offset The start index of the desired substring. |
429 | static Expected<StringRef> terminatedSubstr(StringRef Str, size_t Offset) { |
430 | size_t StrEnd = Str.find(C: '\0', From: Offset); |
431 | if (StrEnd == StringLiteral::npos) { |
432 | return createError( |
433 | Err: "String overran bounds of string table (no null terminator)" ); |
434 | } |
435 | |
436 | size_t StrLen = StrEnd - Offset; |
437 | return Str.substr(Start: Offset, N: StrLen); |
438 | } |
439 | |
440 | /// This function populates a DynamicEntries struct using an ELFT::DynRange. |
441 | /// After populating the struct, the members are validated with |
442 | /// some basic correctness checks. |
443 | /// |
444 | /// @param Dyn Target DynamicEntries struct to populate. |
445 | /// @param DynTable Source dynamic table. |
446 | template <class ELFT> |
447 | static Error populateDynamic(DynamicEntries &Dyn, |
448 | typename ELFT::DynRange DynTable) { |
449 | if (DynTable.empty()) |
450 | return createError(Err: "No .dynamic section found" ); |
451 | |
452 | // Search .dynamic for relevant entries. |
453 | bool FoundDynStr = false; |
454 | bool FoundDynStrSz = false; |
455 | bool FoundDynSym = false; |
456 | for (auto &Entry : DynTable) { |
457 | switch (Entry.d_tag) { |
458 | case DT_SONAME: |
459 | Dyn.SONameOffset = Entry.d_un.d_val; |
460 | break; |
461 | case DT_STRTAB: |
462 | Dyn.StrTabAddr = Entry.d_un.d_ptr; |
463 | FoundDynStr = true; |
464 | break; |
465 | case DT_STRSZ: |
466 | Dyn.StrSize = Entry.d_un.d_val; |
467 | FoundDynStrSz = true; |
468 | break; |
469 | case DT_NEEDED: |
470 | Dyn.NeededLibNames.push_back(Entry.d_un.d_val); |
471 | break; |
472 | case DT_SYMTAB: |
473 | Dyn.DynSymAddr = Entry.d_un.d_ptr; |
474 | FoundDynSym = true; |
475 | break; |
476 | case DT_HASH: |
477 | Dyn.ElfHash = Entry.d_un.d_ptr; |
478 | break; |
479 | case DT_GNU_HASH: |
480 | Dyn.GnuHash = Entry.d_un.d_ptr; |
481 | } |
482 | } |
483 | |
484 | if (!FoundDynStr) { |
485 | return createError( |
486 | Err: "Couldn't locate dynamic string table (no DT_STRTAB entry)" ); |
487 | } |
488 | if (!FoundDynStrSz) { |
489 | return createError( |
490 | Err: "Couldn't determine dynamic string table size (no DT_STRSZ entry)" ); |
491 | } |
492 | if (!FoundDynSym) { |
493 | return createError( |
494 | Err: "Couldn't locate dynamic symbol table (no DT_SYMTAB entry)" ); |
495 | } |
496 | if (Dyn.SONameOffset && *Dyn.SONameOffset >= Dyn.StrSize) { |
497 | return createStringError(EC: object_error::parse_failed, |
498 | Fmt: "DT_SONAME string offset (0x%016" PRIx64 |
499 | ") outside of dynamic string table" , |
500 | Vals: *Dyn.SONameOffset); |
501 | } |
502 | for (uint64_t Offset : Dyn.NeededLibNames) { |
503 | if (Offset >= Dyn.StrSize) { |
504 | return createStringError(EC: object_error::parse_failed, |
505 | Fmt: "DT_NEEDED string offset (0x%016" PRIx64 |
506 | ") outside of dynamic string table" , |
507 | Vals: Offset); |
508 | } |
509 | } |
510 | |
511 | return Error::success(); |
512 | } |
513 | |
514 | /// This function creates an IFSSymbol and populates all members using |
515 | /// information from a binary ELFT::Sym. |
516 | /// |
517 | /// @param SymName The desired name of the IFSSymbol. |
518 | /// @param RawSym ELFT::Sym to extract symbol information from. |
519 | template <class ELFT> |
520 | static IFSSymbol createELFSym(StringRef SymName, |
521 | const typename ELFT::Sym &RawSym) { |
522 | IFSSymbol TargetSym{std::string(SymName)}; |
523 | uint8_t Binding = RawSym.getBinding(); |
524 | if (Binding == STB_WEAK) |
525 | TargetSym.Weak = true; |
526 | else |
527 | TargetSym.Weak = false; |
528 | |
529 | TargetSym.Undefined = RawSym.isUndefined(); |
530 | TargetSym.Type = convertELFSymbolTypeToIFS(RawSym.st_info); |
531 | |
532 | if (TargetSym.Type == IFSSymbolType::Func) { |
533 | TargetSym.Size = 0; |
534 | } else { |
535 | TargetSym.Size = RawSym.st_size; |
536 | } |
537 | return TargetSym; |
538 | } |
539 | |
540 | /// This function populates an IFSStub with symbols using information read |
541 | /// from an ELF binary. |
542 | /// |
543 | /// @param TargetStub IFSStub to add symbols to. |
544 | /// @param DynSym Range of dynamic symbols to add to TargetStub. |
545 | /// @param DynStr StringRef to the dynamic string table. |
546 | template <class ELFT> |
547 | static Error populateSymbols(IFSStub &TargetStub, |
548 | const typename ELFT::SymRange DynSym, |
549 | StringRef DynStr) { |
550 | // Skips the first symbol since it's the NULL symbol. |
551 | for (auto RawSym : DynSym.drop_front(1)) { |
552 | // If a symbol does not have global or weak binding, ignore it. |
553 | uint8_t Binding = RawSym.getBinding(); |
554 | if (!(Binding == STB_GLOBAL || Binding == STB_WEAK)) |
555 | continue; |
556 | // If a symbol doesn't have default or protected visibility, ignore it. |
557 | uint8_t Visibility = RawSym.getVisibility(); |
558 | if (!(Visibility == STV_DEFAULT || Visibility == STV_PROTECTED)) |
559 | continue; |
560 | // Create an IFSSymbol and populate it with information from the symbol |
561 | // table entry. |
562 | Expected<StringRef> SymName = terminatedSubstr(DynStr, RawSym.st_name); |
563 | if (!SymName) |
564 | return SymName.takeError(); |
565 | IFSSymbol Sym = createELFSym<ELFT>(*SymName, RawSym); |
566 | TargetStub.Symbols.push_back(x: std::move(Sym)); |
567 | // TODO: Populate symbol warning. |
568 | } |
569 | return Error::success(); |
570 | } |
571 | |
572 | /// Returns a new IFSStub with all members populated from an ELFObjectFile. |
573 | /// @param ElfObj Source ELFObjectFile. |
574 | template <class ELFT> |
575 | static Expected<std::unique_ptr<IFSStub>> |
576 | buildStub(const ELFObjectFile<ELFT> &ElfObj) { |
577 | using Elf_Dyn_Range = typename ELFT::DynRange; |
578 | using Elf_Sym_Range = typename ELFT::SymRange; |
579 | using Elf_Sym = typename ELFT::Sym; |
580 | std::unique_ptr<IFSStub> DestStub = std::make_unique<IFSStub>(); |
581 | const ELFFile<ELFT> &ElfFile = ElfObj.getELFFile(); |
582 | // Fetch .dynamic table. |
583 | Expected<Elf_Dyn_Range> DynTable = ElfFile.dynamicEntries(); |
584 | if (!DynTable) { |
585 | return DynTable.takeError(); |
586 | } |
587 | |
588 | // Collect relevant .dynamic entries. |
589 | DynamicEntries DynEnt; |
590 | if (Error Err = populateDynamic<ELFT>(DynEnt, *DynTable)) |
591 | return std::move(Err); |
592 | Expected<DynSym<ELFT>> EDynSym = DynSym<ELFT>::create(ElfFile, DynEnt); |
593 | if (!EDynSym) |
594 | return EDynSym.takeError(); |
595 | |
596 | Expected<StringRef> EDynStr = EDynSym->getDynStr(); |
597 | if (!EDynStr) |
598 | return EDynStr.takeError(); |
599 | |
600 | StringRef DynStr = *EDynStr; |
601 | |
602 | // Populate Arch from ELF header. |
603 | DestStub->Target.Arch = static_cast<IFSArch>(ElfFile.getHeader().e_machine); |
604 | DestStub->Target.BitWidth = |
605 | convertELFBitWidthToIFS(ElfFile.getHeader().e_ident[EI_CLASS]); |
606 | DestStub->Target.Endianness = |
607 | convertELFEndiannessToIFS(ElfFile.getHeader().e_ident[EI_DATA]); |
608 | DestStub->Target.ObjectFormat = "ELF" ; |
609 | |
610 | // Populate SoName from .dynamic entries and dynamic string table. |
611 | if (DynEnt.SONameOffset) { |
612 | Expected<StringRef> NameOrErr = |
613 | terminatedSubstr(Str: DynStr, Offset: *DynEnt.SONameOffset); |
614 | if (!NameOrErr) { |
615 | return appendToError(Err: NameOrErr.takeError(), After: "when reading DT_SONAME" ); |
616 | } |
617 | DestStub->SoName = std::string(*NameOrErr); |
618 | } |
619 | |
620 | // Populate NeededLibs from .dynamic entries and dynamic string table. |
621 | for (uint64_t NeededStrOffset : DynEnt.NeededLibNames) { |
622 | Expected<StringRef> LibNameOrErr = |
623 | terminatedSubstr(Str: DynStr, Offset: NeededStrOffset); |
624 | if (!LibNameOrErr) { |
625 | return appendToError(Err: LibNameOrErr.takeError(), After: "when reading DT_NEEDED" ); |
626 | } |
627 | DestStub->NeededLibs.push_back(x: std::string(*LibNameOrErr)); |
628 | } |
629 | |
630 | // Populate Symbols from .dynsym table and dynamic string table. |
631 | Expected<uint64_t> SymCount = ElfFile.getDynSymtabSize(); |
632 | if (!SymCount) |
633 | return SymCount.takeError(); |
634 | if (*SymCount > 0) { |
635 | // Get pointer to in-memory location of .dynsym section. |
636 | Expected<const uint8_t *> DynSymPtr = EDynSym->getDynSym(); |
637 | if (!DynSymPtr) |
638 | return appendToError(Err: DynSymPtr.takeError(), |
639 | After: "when locating .dynsym section contents" ); |
640 | Elf_Sym_Range DynSyms = ArrayRef<Elf_Sym>( |
641 | reinterpret_cast<const Elf_Sym *>(*DynSymPtr), *SymCount); |
642 | Error SymReadError = populateSymbols<ELFT>(*DestStub, DynSyms, DynStr); |
643 | if (SymReadError) |
644 | return appendToError(Err: std::move(SymReadError), |
645 | After: "when reading dynamic symbols" ); |
646 | } |
647 | |
648 | return std::move(DestStub); |
649 | } |
650 | |
651 | /// This function opens a file for writing and then writes a binary ELF stub to |
652 | /// the file. |
653 | /// |
654 | /// @param FilePath File path for writing the ELF binary. |
655 | /// @param Stub Source InterFace Stub to generate a binary ELF stub from. |
656 | template <class ELFT> |
657 | static Error writeELFBinaryToFile(StringRef FilePath, const IFSStub &Stub, |
658 | bool WriteIfChanged) { |
659 | ELFStubBuilder<ELFT> Builder{Stub}; |
660 | // Write Stub to memory first. |
661 | std::vector<uint8_t> Buf(Builder.getSize()); |
662 | Builder.write(Buf.data()); |
663 | |
664 | if (WriteIfChanged) { |
665 | if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = |
666 | MemoryBuffer::getFile(Filename: FilePath)) { |
667 | // Compare Stub output with existing Stub file. |
668 | // If Stub file unchanged, abort updating. |
669 | if ((*BufOrError)->getBufferSize() == Builder.getSize() && |
670 | !memcmp((*BufOrError)->getBufferStart(), Buf.data(), |
671 | Builder.getSize())) |
672 | return Error::success(); |
673 | } |
674 | } |
675 | |
676 | Expected<std::unique_ptr<FileOutputBuffer>> BufOrError = |
677 | FileOutputBuffer::create(FilePath, Size: Builder.getSize()); |
678 | if (!BufOrError) |
679 | return createStringError(EC: errc::invalid_argument, |
680 | S: toString(E: BufOrError.takeError()) + |
681 | " when trying to open `" + FilePath + |
682 | "` for writing" ); |
683 | |
684 | // Write binary to file. |
685 | std::unique_ptr<FileOutputBuffer> FileBuf = std::move(*BufOrError); |
686 | memcpy(dest: FileBuf->getBufferStart(), src: Buf.data(), n: Buf.size()); |
687 | |
688 | return FileBuf->commit(); |
689 | } |
690 | |
691 | Expected<std::unique_ptr<IFSStub>> readELFFile(MemoryBufferRef Buf) { |
692 | Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Source: Buf); |
693 | if (!BinOrErr) { |
694 | return BinOrErr.takeError(); |
695 | } |
696 | |
697 | Binary *Bin = BinOrErr->get(); |
698 | if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Val: Bin)) { |
699 | return buildStub(ElfObj: *Obj); |
700 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Val: Bin)) { |
701 | return buildStub(ElfObj: *Obj); |
702 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Val: Bin)) { |
703 | return buildStub(ElfObj: *Obj); |
704 | } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Val: Bin)) { |
705 | return buildStub(ElfObj: *Obj); |
706 | } |
707 | return createStringError(EC: errc::not_supported, S: "unsupported binary format" ); |
708 | } |
709 | |
710 | // This function wraps the ELFT writeELFBinaryToFile() so writeBinaryStub() |
711 | // can be called without having to use ELFType templates directly. |
712 | Error writeBinaryStub(StringRef FilePath, const IFSStub &Stub, |
713 | bool WriteIfChanged) { |
714 | assert(Stub.Target.Arch); |
715 | assert(Stub.Target.BitWidth); |
716 | assert(Stub.Target.Endianness); |
717 | if (Stub.Target.BitWidth == IFSBitWidthType::IFS32) { |
718 | if (Stub.Target.Endianness == IFSEndiannessType::Little) { |
719 | return writeELFBinaryToFile<ELF32LE>(FilePath, Stub, WriteIfChanged); |
720 | } else { |
721 | return writeELFBinaryToFile<ELF32BE>(FilePath, Stub, WriteIfChanged); |
722 | } |
723 | } else { |
724 | if (Stub.Target.Endianness == IFSEndiannessType::Little) { |
725 | return writeELFBinaryToFile<ELF64LE>(FilePath, Stub, WriteIfChanged); |
726 | } else { |
727 | return writeELFBinaryToFile<ELF64BE>(FilePath, Stub, WriteIfChanged); |
728 | } |
729 | } |
730 | llvm_unreachable("invalid binary output target" ); |
731 | } |
732 | |
733 | } // end namespace ifs |
734 | } // end namespace llvm |
735 | |