1 | //===- InputFiles.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 "InputFiles.h" |
10 | #include "Config.h" |
11 | #include "InputChunks.h" |
12 | #include "InputElement.h" |
13 | #include "OutputSegment.h" |
14 | #include "SymbolTable.h" |
15 | #include "lld/Common/Args.h" |
16 | #include "lld/Common/CommonLinkerContext.h" |
17 | #include "lld/Common/Reproduce.h" |
18 | #include "llvm/BinaryFormat/Wasm.h" |
19 | #include "llvm/Object/Binary.h" |
20 | #include "llvm/Object/Wasm.h" |
21 | #include "llvm/Support/Path.h" |
22 | #include "llvm/Support/TarWriter.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | #include <optional> |
25 | |
26 | #define DEBUG_TYPE "lld" |
27 | |
28 | using namespace llvm; |
29 | using namespace llvm::object; |
30 | using namespace llvm::wasm; |
31 | using namespace llvm::sys; |
32 | |
33 | namespace lld { |
34 | |
35 | // Returns a string in the format of "foo.o" or "foo.a(bar.o)". |
36 | std::string toString(const wasm::InputFile *file) { |
37 | if (!file) |
38 | return "<internal>" ; |
39 | |
40 | if (file->archiveName.empty()) |
41 | return std::string(file->getName()); |
42 | |
43 | return (file->archiveName + "(" + file->getName() + ")" ).str(); |
44 | } |
45 | |
46 | namespace wasm { |
47 | |
48 | void InputFile::checkArch(Triple::ArchType arch) const { |
49 | bool is64 = arch == Triple::wasm64; |
50 | if (is64 && !config->is64) { |
51 | fatal(msg: toString(file: this) + |
52 | ": must specify -mwasm64 to process wasm64 object files" ); |
53 | } else if (config->is64.value_or(u: false) != is64) { |
54 | fatal(msg: toString(file: this) + |
55 | ": wasm32 object file can't be linked in wasm64 mode" ); |
56 | } |
57 | } |
58 | |
59 | std::unique_ptr<llvm::TarWriter> tar; |
60 | |
61 | std::optional<MemoryBufferRef> readFile(StringRef path) { |
62 | log(msg: "Loading: " + path); |
63 | |
64 | auto mbOrErr = MemoryBuffer::getFile(Filename: path); |
65 | if (auto ec = mbOrErr.getError()) { |
66 | error(msg: "cannot open " + path + ": " + ec.message()); |
67 | return std::nullopt; |
68 | } |
69 | std::unique_ptr<MemoryBuffer> &mb = *mbOrErr; |
70 | MemoryBufferRef mbref = mb->getMemBufferRef(); |
71 | make<std::unique_ptr<MemoryBuffer>>(args: std::move(mb)); // take MB ownership |
72 | |
73 | if (tar) |
74 | tar->append(Path: relativeToRoot(path), Data: mbref.getBuffer()); |
75 | return mbref; |
76 | } |
77 | |
78 | InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName, |
79 | uint64_t offsetInArchive, bool lazy) { |
80 | file_magic magic = identify_magic(magic: mb.getBuffer()); |
81 | if (magic == file_magic::wasm_object) { |
82 | std::unique_ptr<Binary> bin = |
83 | CHECK(createBinary(mb), mb.getBufferIdentifier()); |
84 | auto *obj = cast<WasmObjectFile>(Val: bin.get()); |
85 | if (obj->hasUnmodeledTypes()) |
86 | fatal(msg: toString(s: mb.getBufferIdentifier()) + |
87 | "file has unmodeled reference or GC types" ); |
88 | if (obj->isSharedObject()) |
89 | return make<SharedFile>(args&: mb); |
90 | return make<ObjFile>(args&: mb, args&: archiveName, args&: lazy); |
91 | } |
92 | |
93 | assert(magic == file_magic::bitcode); |
94 | return make<BitcodeFile>(args&: mb, args&: archiveName, args&: offsetInArchive, args&: lazy); |
95 | } |
96 | |
97 | // Relocations contain either symbol or type indices. This function takes a |
98 | // relocation and returns relocated index (i.e. translates from the input |
99 | // symbol/type space to the output symbol/type space). |
100 | uint32_t ObjFile::calcNewIndex(const WasmRelocation &reloc) const { |
101 | if (reloc.Type == R_WASM_TYPE_INDEX_LEB) { |
102 | assert(typeIsUsed[reloc.Index]); |
103 | return typeMap[reloc.Index]; |
104 | } |
105 | const Symbol *sym = symbols[reloc.Index]; |
106 | if (auto *ss = dyn_cast<SectionSymbol>(Val: sym)) |
107 | sym = ss->getOutputSectionSymbol(); |
108 | return sym->getOutputSymbolIndex(); |
109 | } |
110 | |
111 | // Relocations can contain addend for combined sections. This function takes a |
112 | // relocation and returns updated addend by offset in the output section. |
113 | int64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const { |
114 | switch (reloc.Type) { |
115 | case R_WASM_MEMORY_ADDR_LEB: |
116 | case R_WASM_MEMORY_ADDR_LEB64: |
117 | case R_WASM_MEMORY_ADDR_SLEB64: |
118 | case R_WASM_MEMORY_ADDR_SLEB: |
119 | case R_WASM_MEMORY_ADDR_REL_SLEB: |
120 | case R_WASM_MEMORY_ADDR_REL_SLEB64: |
121 | case R_WASM_MEMORY_ADDR_I32: |
122 | case R_WASM_MEMORY_ADDR_I64: |
123 | case R_WASM_MEMORY_ADDR_TLS_SLEB: |
124 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: |
125 | case R_WASM_FUNCTION_OFFSET_I32: |
126 | case R_WASM_FUNCTION_OFFSET_I64: |
127 | case R_WASM_MEMORY_ADDR_LOCREL_I32: |
128 | return reloc.Addend; |
129 | case R_WASM_SECTION_OFFSET_I32: |
130 | return getSectionSymbol(index: reloc.Index)->section->getOffset(offset: reloc.Addend); |
131 | default: |
132 | llvm_unreachable("unexpected relocation type" ); |
133 | } |
134 | } |
135 | |
136 | // Translate from the relocation's index into the final linked output value. |
137 | uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, |
138 | const InputChunk *chunk) const { |
139 | const Symbol* sym = nullptr; |
140 | if (reloc.Type != R_WASM_TYPE_INDEX_LEB) { |
141 | sym = symbols[reloc.Index]; |
142 | |
143 | // We can end up with relocations against non-live symbols. For example |
144 | // in debug sections. We return a tombstone value in debug symbol sections |
145 | // so this will not produce a valid range conflicting with ranges of actual |
146 | // code. In other sections we return reloc.Addend. |
147 | |
148 | if (!isa<SectionSymbol>(Val: sym) && !sym->isLive()) |
149 | return tombstone ? tombstone : reloc.Addend; |
150 | } |
151 | |
152 | switch (reloc.Type) { |
153 | case R_WASM_TABLE_INDEX_I32: |
154 | case R_WASM_TABLE_INDEX_I64: |
155 | case R_WASM_TABLE_INDEX_SLEB: |
156 | case R_WASM_TABLE_INDEX_SLEB64: |
157 | case R_WASM_TABLE_INDEX_REL_SLEB: |
158 | case R_WASM_TABLE_INDEX_REL_SLEB64: { |
159 | if (!getFunctionSymbol(index: reloc.Index)->hasTableIndex()) |
160 | return 0; |
161 | uint32_t index = getFunctionSymbol(index: reloc.Index)->getTableIndex(); |
162 | if (reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB || |
163 | reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB64) |
164 | index -= config->tableBase; |
165 | return index; |
166 | } |
167 | case R_WASM_MEMORY_ADDR_LEB: |
168 | case R_WASM_MEMORY_ADDR_LEB64: |
169 | case R_WASM_MEMORY_ADDR_SLEB: |
170 | case R_WASM_MEMORY_ADDR_SLEB64: |
171 | case R_WASM_MEMORY_ADDR_REL_SLEB: |
172 | case R_WASM_MEMORY_ADDR_REL_SLEB64: |
173 | case R_WASM_MEMORY_ADDR_I32: |
174 | case R_WASM_MEMORY_ADDR_I64: |
175 | case R_WASM_MEMORY_ADDR_TLS_SLEB: |
176 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: |
177 | case R_WASM_MEMORY_ADDR_LOCREL_I32: { |
178 | if (isa<UndefinedData>(Val: sym) || sym->isShared() || sym->isUndefWeak()) |
179 | return 0; |
180 | auto D = cast<DefinedData>(Val: sym); |
181 | uint64_t value = D->getVA() + reloc.Addend; |
182 | if (reloc.Type == R_WASM_MEMORY_ADDR_LOCREL_I32) { |
183 | const auto *segment = cast<InputSegment>(Val: chunk); |
184 | uint64_t p = segment->outputSeg->startVA + segment->outputSegmentOffset + |
185 | reloc.Offset - segment->getInputSectionOffset(); |
186 | value -= p; |
187 | } |
188 | return value; |
189 | } |
190 | case R_WASM_TYPE_INDEX_LEB: |
191 | return typeMap[reloc.Index]; |
192 | case R_WASM_FUNCTION_INDEX_LEB: |
193 | case R_WASM_FUNCTION_INDEX_I32: |
194 | return getFunctionSymbol(index: reloc.Index)->getFunctionIndex(); |
195 | case R_WASM_GLOBAL_INDEX_LEB: |
196 | case R_WASM_GLOBAL_INDEX_I32: |
197 | if (auto gs = dyn_cast<GlobalSymbol>(Val: sym)) |
198 | return gs->getGlobalIndex(); |
199 | return sym->getGOTIndex(); |
200 | case R_WASM_TAG_INDEX_LEB: |
201 | return getTagSymbol(index: reloc.Index)->getTagIndex(); |
202 | case R_WASM_FUNCTION_OFFSET_I32: |
203 | case R_WASM_FUNCTION_OFFSET_I64: { |
204 | if (isa<UndefinedFunction>(Val: sym)) { |
205 | return tombstone ? tombstone : reloc.Addend; |
206 | } |
207 | auto *f = cast<DefinedFunction>(Val: sym); |
208 | return f->function->getOffset(offset: f->function->getFunctionCodeOffset() + |
209 | reloc.Addend); |
210 | } |
211 | case R_WASM_SECTION_OFFSET_I32: |
212 | return getSectionSymbol(index: reloc.Index)->section->getOffset(offset: reloc.Addend); |
213 | case R_WASM_TABLE_NUMBER_LEB: |
214 | return getTableSymbol(index: reloc.Index)->getTableNumber(); |
215 | default: |
216 | llvm_unreachable("unknown relocation type" ); |
217 | } |
218 | } |
219 | |
220 | template <class T> |
221 | static void setRelocs(const std::vector<T *> &chunks, |
222 | const WasmSection *section) { |
223 | if (!section) |
224 | return; |
225 | |
226 | ArrayRef<WasmRelocation> relocs = section->Relocations; |
227 | assert(llvm::is_sorted( |
228 | relocs, [](const WasmRelocation &r1, const WasmRelocation &r2) { |
229 | return r1.Offset < r2.Offset; |
230 | })); |
231 | assert(llvm::is_sorted(chunks, [](InputChunk *c1, InputChunk *c2) { |
232 | return c1->getInputSectionOffset() < c2->getInputSectionOffset(); |
233 | })); |
234 | |
235 | auto relocsNext = relocs.begin(); |
236 | auto relocsEnd = relocs.end(); |
237 | auto relocLess = [](const WasmRelocation &r, uint32_t val) { |
238 | return r.Offset < val; |
239 | }; |
240 | for (InputChunk *c : chunks) { |
241 | auto relocsStart = std::lower_bound(relocsNext, relocsEnd, |
242 | c->getInputSectionOffset(), relocLess); |
243 | relocsNext = std::lower_bound( |
244 | relocsStart, relocsEnd, c->getInputSectionOffset() + c->getInputSize(), |
245 | relocLess); |
246 | c->setRelocations(ArrayRef<WasmRelocation>(relocsStart, relocsNext)); |
247 | } |
248 | } |
249 | |
250 | // An object file can have two approaches to tables. With the reference-types |
251 | // feature enabled, input files that define or use tables declare the tables |
252 | // using symbols, and record each use with a relocation. This way when the |
253 | // linker combines inputs, it can collate the tables used by the inputs, |
254 | // assigning them distinct table numbers, and renumber all the uses as |
255 | // appropriate. At the same time, the linker has special logic to build the |
256 | // indirect function table if it is needed. |
257 | // |
258 | // However, MVP object files (those that target WebAssembly 1.0, the "minimum |
259 | // viable product" version of WebAssembly) neither write table symbols nor |
260 | // record relocations. These files can have at most one table, the indirect |
261 | // function table used by call_indirect and which is the address space for |
262 | // function pointers. If this table is present, it is always an import. If we |
263 | // have a file with a table import but no table symbols, it is an MVP object |
264 | // file. synthesizeMVPIndirectFunctionTableSymbolIfNeeded serves as a shim when |
265 | // loading these input files, defining the missing symbol to allow the indirect |
266 | // function table to be built. |
267 | // |
268 | // As indirect function table table usage in MVP objects cannot be relocated, |
269 | // the linker must ensure that this table gets assigned index zero. |
270 | void ObjFile::addLegacyIndirectFunctionTableIfNeeded( |
271 | uint32_t tableSymbolCount) { |
272 | uint32_t tableCount = wasmObj->getNumImportedTables() + tables.size(); |
273 | |
274 | // If there are symbols for all tables, then all is good. |
275 | if (tableCount == tableSymbolCount) |
276 | return; |
277 | |
278 | // It's possible for an input to define tables and also use the indirect |
279 | // function table, but forget to compile with -mattr=+reference-types. |
280 | // For these newer files, we require symbols for all tables, and |
281 | // relocations for all of their uses. |
282 | if (tableSymbolCount != 0) { |
283 | error(msg: toString(file: this) + |
284 | ": expected one symbol table entry for each of the " + |
285 | Twine(tableCount) + " table(s) present, but got " + |
286 | Twine(tableSymbolCount) + " symbol(s) instead." ); |
287 | return; |
288 | } |
289 | |
290 | // An MVP object file can have up to one table import, for the indirect |
291 | // function table, but will have no table definitions. |
292 | if (tables.size()) { |
293 | error(msg: toString(file: this) + |
294 | ": unexpected table definition(s) without corresponding " |
295 | "symbol-table entries." ); |
296 | return; |
297 | } |
298 | |
299 | // An MVP object file can have only one table import. |
300 | if (tableCount != 1) { |
301 | error(msg: toString(file: this) + |
302 | ": multiple table imports, but no corresponding symbol-table " |
303 | "entries." ); |
304 | return; |
305 | } |
306 | |
307 | const WasmImport *tableImport = nullptr; |
308 | for (const auto &import : wasmObj->imports()) { |
309 | if (import.Kind == WASM_EXTERNAL_TABLE) { |
310 | assert(!tableImport); |
311 | tableImport = &import; |
312 | } |
313 | } |
314 | assert(tableImport); |
315 | |
316 | // We can only synthesize a symtab entry for the indirect function table; if |
317 | // it has an unexpected name or type, assume that it's not actually the |
318 | // indirect function table. |
319 | if (tableImport->Field != functionTableName || |
320 | tableImport->Table.ElemType != ValType::FUNCREF) { |
321 | error(msg: toString(file: this) + ": table import " + Twine(tableImport->Field) + |
322 | " is missing a symbol table entry." ); |
323 | return; |
324 | } |
325 | |
326 | WasmSymbolInfo info; |
327 | info.Name = tableImport->Field; |
328 | info.Kind = WASM_SYMBOL_TYPE_TABLE; |
329 | info.ImportModule = tableImport->Module; |
330 | info.ImportName = tableImport->Field; |
331 | info.Flags = WASM_SYMBOL_UNDEFINED | WASM_SYMBOL_NO_STRIP; |
332 | info.ElementIndex = 0; |
333 | LLVM_DEBUG(dbgs() << "Synthesizing symbol for table import: " << info.Name |
334 | << "\n" ); |
335 | const WasmGlobalType *globalType = nullptr; |
336 | const WasmSignature *signature = nullptr; |
337 | auto *wasmSym = |
338 | make<WasmSymbol>(args&: info, args&: globalType, args: &tableImport->Table, args&: signature); |
339 | Symbol *sym = createUndefined(sym: *wasmSym, isCalledDirectly: false); |
340 | // We're only sure it's a TableSymbol if the createUndefined succeeded. |
341 | if (errorCount()) |
342 | return; |
343 | symbols.push_back(x: sym); |
344 | // Because there are no TABLE_NUMBER relocs, we can't compute accurate |
345 | // liveness info; instead, just mark the symbol as always live. |
346 | sym->markLive(); |
347 | |
348 | // We assume that this compilation unit has unrelocatable references to |
349 | // this table. |
350 | ctx.legacyFunctionTable = true; |
351 | } |
352 | |
353 | static bool shouldMerge(const WasmSection &sec) { |
354 | if (config->optimize == 0) |
355 | return false; |
356 | // Sadly we don't have section attributes yet for custom sections, so we |
357 | // currently go by the name alone. |
358 | // TODO(sbc): Add ability for wasm sections to carry flags so we don't |
359 | // need to use names here. |
360 | // For now, keep in sync with uses of wasm::WASM_SEG_FLAG_STRINGS in |
361 | // MCObjectFileInfo::initWasmMCObjectFileInfo which creates these custom |
362 | // sections. |
363 | return sec.Name == ".debug_str" || sec.Name == ".debug_str.dwo" || |
364 | sec.Name == ".debug_line_str" ; |
365 | } |
366 | |
367 | static bool shouldMerge(const WasmSegment &seg) { |
368 | // As of now we only support merging strings, and only with single byte |
369 | // alignment (2^0). |
370 | if (!(seg.Data.LinkingFlags & WASM_SEG_FLAG_STRINGS) || |
371 | (seg.Data.Alignment != 0)) |
372 | return false; |
373 | |
374 | // On a regular link we don't merge sections if -O0 (default is -O1). This |
375 | // sometimes makes the linker significantly faster, although the output will |
376 | // be bigger. |
377 | if (config->optimize == 0) |
378 | return false; |
379 | |
380 | // A mergeable section with size 0 is useless because they don't have |
381 | // any data to merge. A mergeable string section with size 0 can be |
382 | // argued as invalid because it doesn't end with a null character. |
383 | // We'll avoid a mess by handling them as if they were non-mergeable. |
384 | if (seg.Data.Content.size() == 0) |
385 | return false; |
386 | |
387 | return true; |
388 | } |
389 | |
390 | void ObjFile::parseLazy() { |
391 | LLVM_DEBUG(dbgs() << "ObjFile::parseLazy: " << toString(this) << " " |
392 | << wasmObj.get() << "\n" ); |
393 | for (const SymbolRef &sym : wasmObj->symbols()) { |
394 | const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(Symb: sym.getRawDataRefImpl()); |
395 | if (!wasmSym.isDefined()) |
396 | continue; |
397 | symtab->addLazy(name: wasmSym.Info.Name, f: this); |
398 | // addLazy() may trigger this->extract() if an existing symbol is an |
399 | // undefined symbol. If that happens, this function has served its purpose, |
400 | // and we can exit from the loop early. |
401 | if (!lazy) |
402 | break; |
403 | } |
404 | } |
405 | |
406 | ObjFile::ObjFile(MemoryBufferRef m, StringRef archiveName, bool lazy) |
407 | : WasmFileBase(ObjectKind, m) { |
408 | this->lazy = lazy; |
409 | this->archiveName = std::string(archiveName); |
410 | |
411 | // Currently we only do this check for regular object file, and not for shared |
412 | // object files. This is because architecture detection for shared objects is |
413 | // currently based on a heuristic, which is fallable: |
414 | // https://github.com/llvm/llvm-project/issues/98778 |
415 | checkArch(arch: wasmObj->getArch()); |
416 | |
417 | // If this isn't part of an archive, it's eagerly linked, so mark it live. |
418 | if (archiveName.empty()) |
419 | markLive(); |
420 | } |
421 | |
422 | void SharedFile::parse() { |
423 | assert(wasmObj->isSharedObject()); |
424 | |
425 | for (const SymbolRef &sym : wasmObj->symbols()) { |
426 | const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(Symb: sym.getRawDataRefImpl()); |
427 | if (wasmSym.isDefined()) { |
428 | StringRef name = wasmSym.Info.Name; |
429 | // Certain shared library exports are known to be DSO-local so we |
430 | // don't want to add them to the symbol table. |
431 | // TODO(sbc): Instead of hardcoding these here perhaps we could add |
432 | // this as extra metadata in the `dylink` section. |
433 | if (name == "__wasm_apply_data_relocs" || name == "__wasm_call_ctors" || |
434 | name.starts_with(Prefix: "__start_" ) || name.starts_with(Prefix: "__stop_" )) |
435 | continue; |
436 | uint32_t flags = wasmSym.Info.Flags; |
437 | Symbol *s; |
438 | LLVM_DEBUG(dbgs() << "shared symbol: " << name << "\n" ); |
439 | switch (wasmSym.Info.Kind) { |
440 | case WASM_SYMBOL_TYPE_FUNCTION: |
441 | s = symtab->addSharedFunction(name, flags, file: this, sig: wasmSym.Signature); |
442 | break; |
443 | case WASM_SYMBOL_TYPE_DATA: |
444 | s = symtab->addSharedData(name, flags, file: this); |
445 | break; |
446 | default: |
447 | continue; |
448 | } |
449 | symbols.push_back(x: s); |
450 | } |
451 | } |
452 | } |
453 | |
454 | WasmFileBase::WasmFileBase(Kind k, MemoryBufferRef m) : InputFile(k, m) { |
455 | // Parse a memory buffer as a wasm file. |
456 | LLVM_DEBUG(dbgs() << "Reading object: " << toString(this) << "\n" ); |
457 | std::unique_ptr<Binary> bin = CHECK(createBinary(mb), toString(this)); |
458 | |
459 | auto *obj = dyn_cast<WasmObjectFile>(Val: bin.get()); |
460 | if (!obj) |
461 | fatal(msg: toString(file: this) + ": not a wasm file" ); |
462 | |
463 | bin.release(); |
464 | wasmObj.reset(p: obj); |
465 | } |
466 | |
467 | void ObjFile::parse(bool ignoreComdats) { |
468 | // Parse a memory buffer as a wasm file. |
469 | LLVM_DEBUG(dbgs() << "ObjFile::parse: " << toString(this) << "\n" ); |
470 | |
471 | if (!wasmObj->isRelocatableObject()) |
472 | fatal(msg: toString(file: this) + ": not a relocatable wasm file" ); |
473 | |
474 | // Build up a map of function indices to table indices for use when |
475 | // verifying the existing table index relocations |
476 | uint32_t totalFunctions = |
477 | wasmObj->getNumImportedFunctions() + wasmObj->functions().size(); |
478 | tableEntriesRel.resize(new_size: totalFunctions); |
479 | tableEntries.resize(new_size: totalFunctions); |
480 | for (const WasmElemSegment &seg : wasmObj->elements()) { |
481 | int64_t offset; |
482 | if (seg.Offset.Extended) |
483 | fatal(msg: toString(file: this) + ": extended init exprs not supported" ); |
484 | else if (seg.Offset.Inst.Opcode == WASM_OPCODE_I32_CONST) |
485 | offset = seg.Offset.Inst.Value.Int32; |
486 | else if (seg.Offset.Inst.Opcode == WASM_OPCODE_I64_CONST) |
487 | offset = seg.Offset.Inst.Value.Int64; |
488 | else |
489 | fatal(msg: toString(file: this) + ": invalid table elements" ); |
490 | for (size_t index = 0; index < seg.Functions.size(); index++) { |
491 | auto functionIndex = seg.Functions[index]; |
492 | tableEntriesRel[functionIndex] = index; |
493 | tableEntries[functionIndex] = offset + index; |
494 | } |
495 | } |
496 | |
497 | ArrayRef<StringRef> comdats = wasmObj->linkingData().Comdats; |
498 | for (StringRef comdat : comdats) { |
499 | bool isNew = ignoreComdats || symtab->addComdat(name: comdat); |
500 | keptComdats.push_back(x: isNew); |
501 | } |
502 | |
503 | uint32_t sectionIndex = 0; |
504 | |
505 | // Bool for each symbol, true if called directly. This allows us to implement |
506 | // a weaker form of signature checking where undefined functions that are not |
507 | // called directly (i.e. only address taken) don't have to match the defined |
508 | // function's signature. We cannot do this for directly called functions |
509 | // because those signatures are checked at validation times. |
510 | // See https://github.com/llvm/llvm-project/issues/39758 |
511 | std::vector<bool> isCalledDirectly(wasmObj->getNumberOfSymbols(), false); |
512 | for (const SectionRef &sec : wasmObj->sections()) { |
513 | const WasmSection §ion = wasmObj->getWasmSection(Section: sec); |
514 | // Wasm objects can have at most one code and one data section. |
515 | if (section.Type == WASM_SEC_CODE) { |
516 | assert(!codeSection); |
517 | codeSection = §ion; |
518 | } else if (section.Type == WASM_SEC_DATA) { |
519 | assert(!dataSection); |
520 | dataSection = §ion; |
521 | } else if (section.Type == WASM_SEC_CUSTOM) { |
522 | InputChunk *customSec; |
523 | if (shouldMerge(sec: section)) |
524 | customSec = make<MergeInputChunk>(args: section, args: this); |
525 | else |
526 | customSec = make<InputSection>(args: section, args: this); |
527 | customSec->discarded = isExcludedByComdat(chunk: customSec); |
528 | customSections.emplace_back(args&: customSec); |
529 | customSections.back()->setRelocations(section.Relocations); |
530 | customSectionsByIndex[sectionIndex] = customSections.back(); |
531 | } |
532 | sectionIndex++; |
533 | // Scans relocations to determine if a function symbol is called directly. |
534 | for (const WasmRelocation &reloc : section.Relocations) |
535 | if (reloc.Type == R_WASM_FUNCTION_INDEX_LEB) |
536 | isCalledDirectly[reloc.Index] = true; |
537 | } |
538 | |
539 | typeMap.resize(new_size: getWasmObj()->types().size()); |
540 | typeIsUsed.resize(new_size: getWasmObj()->types().size(), x: false); |
541 | |
542 | |
543 | // Populate `Segments`. |
544 | for (const WasmSegment &s : wasmObj->dataSegments()) { |
545 | InputChunk *seg; |
546 | if (shouldMerge(seg: s)) |
547 | seg = make<MergeInputChunk>(args: s, args: this); |
548 | else |
549 | seg = make<InputSegment>(args: s, args: this); |
550 | seg->discarded = isExcludedByComdat(chunk: seg); |
551 | // Older object files did not include WASM_SEG_FLAG_TLS and instead |
552 | // relied on the naming convention. To maintain compat with such objects |
553 | // we still imply the TLS flag based on the name of the segment. |
554 | if (!seg->isTLS() && |
555 | (seg->name.starts_with(Prefix: ".tdata" ) || seg->name.starts_with(Prefix: ".tbss" ))) |
556 | seg->flags |= WASM_SEG_FLAG_TLS; |
557 | segments.emplace_back(args&: seg); |
558 | } |
559 | setRelocs(chunks: segments, section: dataSection); |
560 | |
561 | // Populate `Functions`. |
562 | ArrayRef<WasmFunction> funcs = wasmObj->functions(); |
563 | ArrayRef<WasmSignature> types = wasmObj->types(); |
564 | functions.reserve(n: funcs.size()); |
565 | |
566 | for (auto &f : funcs) { |
567 | auto *func = make<InputFunction>(args: types[f.SigIndex], args: &f, args: this); |
568 | func->discarded = isExcludedByComdat(chunk: func); |
569 | functions.emplace_back(args&: func); |
570 | } |
571 | setRelocs(chunks: functions, section: codeSection); |
572 | |
573 | // Populate `Tables`. |
574 | for (const WasmTable &t : wasmObj->tables()) |
575 | tables.emplace_back(args: make<InputTable>(args: t, args: this)); |
576 | |
577 | // Populate `Globals`. |
578 | for (const WasmGlobal &g : wasmObj->globals()) |
579 | globals.emplace_back(args: make<InputGlobal>(args: g, args: this)); |
580 | |
581 | // Populate `Tags`. |
582 | for (const WasmTag &t : wasmObj->tags()) |
583 | tags.emplace_back(args: make<InputTag>(args: types[t.SigIndex], args: t, args: this)); |
584 | |
585 | // Populate `Symbols` based on the symbols in the object. |
586 | symbols.reserve(n: wasmObj->getNumberOfSymbols()); |
587 | uint32_t tableSymbolCount = 0; |
588 | for (const SymbolRef &sym : wasmObj->symbols()) { |
589 | const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(Symb: sym.getRawDataRefImpl()); |
590 | if (wasmSym.isTypeTable()) |
591 | tableSymbolCount++; |
592 | if (wasmSym.isDefined()) { |
593 | // createDefined may fail if the symbol is comdat excluded in which case |
594 | // we fall back to creating an undefined symbol |
595 | if (Symbol *d = createDefined(sym: wasmSym)) { |
596 | symbols.push_back(x: d); |
597 | continue; |
598 | } |
599 | } |
600 | size_t idx = symbols.size(); |
601 | symbols.push_back(x: createUndefined(sym: wasmSym, isCalledDirectly: isCalledDirectly[idx])); |
602 | } |
603 | |
604 | addLegacyIndirectFunctionTableIfNeeded(tableSymbolCount); |
605 | } |
606 | |
607 | bool ObjFile::isExcludedByComdat(const InputChunk *chunk) const { |
608 | uint32_t c = chunk->getComdat(); |
609 | if (c == UINT32_MAX) |
610 | return false; |
611 | return !keptComdats[c]; |
612 | } |
613 | |
614 | FunctionSymbol *ObjFile::getFunctionSymbol(uint32_t index) const { |
615 | return cast<FunctionSymbol>(Val: symbols[index]); |
616 | } |
617 | |
618 | GlobalSymbol *ObjFile::getGlobalSymbol(uint32_t index) const { |
619 | return cast<GlobalSymbol>(Val: symbols[index]); |
620 | } |
621 | |
622 | TagSymbol *ObjFile::getTagSymbol(uint32_t index) const { |
623 | return cast<TagSymbol>(Val: symbols[index]); |
624 | } |
625 | |
626 | TableSymbol *ObjFile::getTableSymbol(uint32_t index) const { |
627 | return cast<TableSymbol>(Val: symbols[index]); |
628 | } |
629 | |
630 | SectionSymbol *ObjFile::getSectionSymbol(uint32_t index) const { |
631 | return cast<SectionSymbol>(Val: symbols[index]); |
632 | } |
633 | |
634 | DataSymbol *ObjFile::getDataSymbol(uint32_t index) const { |
635 | return cast<DataSymbol>(Val: symbols[index]); |
636 | } |
637 | |
638 | Symbol *ObjFile::createDefined(const WasmSymbol &sym) { |
639 | StringRef name = sym.Info.Name; |
640 | uint32_t flags = sym.Info.Flags; |
641 | |
642 | switch (sym.Info.Kind) { |
643 | case WASM_SYMBOL_TYPE_FUNCTION: { |
644 | InputFunction *func = |
645 | functions[sym.Info.ElementIndex - wasmObj->getNumImportedFunctions()]; |
646 | if (sym.isBindingLocal()) |
647 | return make<DefinedFunction>(args&: name, args&: flags, args: this, args&: func); |
648 | if (func->discarded) |
649 | return nullptr; |
650 | return symtab->addDefinedFunction(name, flags, file: this, function: func); |
651 | } |
652 | case WASM_SYMBOL_TYPE_DATA: { |
653 | InputChunk *seg = segments[sym.Info.DataRef.Segment]; |
654 | auto offset = sym.Info.DataRef.Offset; |
655 | auto size = sym.Info.DataRef.Size; |
656 | // Support older (e.g. llvm 13) object files that pre-date the per-symbol |
657 | // TLS flag, and symbols were assumed to be TLS by being defined in a TLS |
658 | // segment. |
659 | if (!(flags & WASM_SYMBOL_TLS) && seg->isTLS()) |
660 | flags |= WASM_SYMBOL_TLS; |
661 | if (sym.isBindingLocal()) |
662 | return make<DefinedData>(args&: name, args&: flags, args: this, args&: seg, args&: offset, args&: size); |
663 | if (seg->discarded) |
664 | return nullptr; |
665 | return symtab->addDefinedData(name, flags, file: this, segment: seg, address: offset, size); |
666 | } |
667 | case WASM_SYMBOL_TYPE_GLOBAL: { |
668 | InputGlobal *global = |
669 | globals[sym.Info.ElementIndex - wasmObj->getNumImportedGlobals()]; |
670 | if (sym.isBindingLocal()) |
671 | return make<DefinedGlobal>(args&: name, args&: flags, args: this, args&: global); |
672 | return symtab->addDefinedGlobal(name, flags, file: this, g: global); |
673 | } |
674 | case WASM_SYMBOL_TYPE_SECTION: { |
675 | InputChunk *section = customSectionsByIndex[sym.Info.ElementIndex]; |
676 | assert(sym.isBindingLocal()); |
677 | // Need to return null if discarded here? data and func only do that when |
678 | // binding is not local. |
679 | if (section->discarded) |
680 | return nullptr; |
681 | return make<SectionSymbol>(args&: flags, args&: section, args: this); |
682 | } |
683 | case WASM_SYMBOL_TYPE_TAG: { |
684 | InputTag *tag = tags[sym.Info.ElementIndex - wasmObj->getNumImportedTags()]; |
685 | if (sym.isBindingLocal()) |
686 | return make<DefinedTag>(args&: name, args&: flags, args: this, args&: tag); |
687 | return symtab->addDefinedTag(name, flags, file: this, t: tag); |
688 | } |
689 | case WASM_SYMBOL_TYPE_TABLE: { |
690 | InputTable *table = |
691 | tables[sym.Info.ElementIndex - wasmObj->getNumImportedTables()]; |
692 | if (sym.isBindingLocal()) |
693 | return make<DefinedTable>(args&: name, args&: flags, args: this, args&: table); |
694 | return symtab->addDefinedTable(name, flags, file: this, t: table); |
695 | } |
696 | } |
697 | llvm_unreachable("unknown symbol kind" ); |
698 | } |
699 | |
700 | Symbol *ObjFile::createUndefined(const WasmSymbol &sym, bool isCalledDirectly) { |
701 | StringRef name = sym.Info.Name; |
702 | uint32_t flags = sym.Info.Flags | WASM_SYMBOL_UNDEFINED; |
703 | |
704 | switch (sym.Info.Kind) { |
705 | case WASM_SYMBOL_TYPE_FUNCTION: |
706 | if (sym.isBindingLocal()) |
707 | return make<UndefinedFunction>(args&: name, args: sym.Info.ImportName, |
708 | args: sym.Info.ImportModule, args&: flags, args: this, |
709 | args: sym.Signature, args&: isCalledDirectly); |
710 | return symtab->addUndefinedFunction(name, importName: sym.Info.ImportName, |
711 | importModule: sym.Info.ImportModule, flags, file: this, |
712 | signature: sym.Signature, isCalledDirectly); |
713 | case WASM_SYMBOL_TYPE_DATA: |
714 | if (sym.isBindingLocal()) |
715 | return make<UndefinedData>(args&: name, args&: flags, args: this); |
716 | return symtab->addUndefinedData(name, flags, file: this); |
717 | case WASM_SYMBOL_TYPE_GLOBAL: |
718 | if (sym.isBindingLocal()) |
719 | return make<UndefinedGlobal>(args&: name, args: sym.Info.ImportName, |
720 | args: sym.Info.ImportModule, args&: flags, args: this, |
721 | args: sym.GlobalType); |
722 | return symtab->addUndefinedGlobal(name, importName: sym.Info.ImportName, |
723 | importModule: sym.Info.ImportModule, flags, file: this, |
724 | type: sym.GlobalType); |
725 | case WASM_SYMBOL_TYPE_TABLE: |
726 | if (sym.isBindingLocal()) |
727 | return make<UndefinedTable>(args&: name, args: sym.Info.ImportName, |
728 | args: sym.Info.ImportModule, args&: flags, args: this, |
729 | args: sym.TableType); |
730 | return symtab->addUndefinedTable(name, importName: sym.Info.ImportName, |
731 | importModule: sym.Info.ImportModule, flags, file: this, |
732 | type: sym.TableType); |
733 | case WASM_SYMBOL_TYPE_TAG: |
734 | if (sym.isBindingLocal()) |
735 | return make<UndefinedTag>(args&: name, args: sym.Info.ImportName, |
736 | args: sym.Info.ImportModule, args&: flags, args: this, |
737 | args: sym.Signature); |
738 | return symtab->addUndefinedTag(name, importName: sym.Info.ImportName, |
739 | importModule: sym.Info.ImportModule, flags, file: this, |
740 | sig: sym.Signature); |
741 | case WASM_SYMBOL_TYPE_SECTION: |
742 | llvm_unreachable("section symbols cannot be undefined" ); |
743 | } |
744 | llvm_unreachable("unknown symbol kind" ); |
745 | } |
746 | |
747 | StringRef strip(StringRef s) { return s.trim(Char: ' '); } |
748 | |
749 | void StubFile::parse() { |
750 | bool first = true; |
751 | |
752 | SmallVector<StringRef> lines; |
753 | mb.getBuffer().split(A&: lines, Separator: '\n'); |
754 | for (StringRef line : lines) { |
755 | line = line.trim(); |
756 | |
757 | // File must begin with #STUB |
758 | if (first) { |
759 | assert(line == "#STUB" ); |
760 | first = false; |
761 | } |
762 | |
763 | // Lines starting with # are considered comments |
764 | if (line.starts_with(Prefix: "#" )) |
765 | continue; |
766 | |
767 | StringRef sym; |
768 | StringRef rest; |
769 | std::tie(args&: sym, args&: rest) = line.split(Separator: ':'); |
770 | sym = strip(s: sym); |
771 | rest = strip(s: rest); |
772 | |
773 | symbolDependencies[sym] = {}; |
774 | |
775 | while (rest.size()) { |
776 | StringRef dep; |
777 | std::tie(args&: dep, args&: rest) = rest.split(Separator: ','); |
778 | dep = strip(s: dep); |
779 | symbolDependencies[sym].push_back(x: dep); |
780 | } |
781 | } |
782 | } |
783 | |
784 | static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) { |
785 | switch (gvVisibility) { |
786 | case GlobalValue::DefaultVisibility: |
787 | return WASM_SYMBOL_VISIBILITY_DEFAULT; |
788 | case GlobalValue::HiddenVisibility: |
789 | case GlobalValue::ProtectedVisibility: |
790 | return WASM_SYMBOL_VISIBILITY_HIDDEN; |
791 | } |
792 | llvm_unreachable("unknown visibility" ); |
793 | } |
794 | |
795 | static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats, |
796 | const lto::InputFile::Symbol &objSym, |
797 | BitcodeFile &f) { |
798 | StringRef name = saver().save(S: objSym.getName()); |
799 | |
800 | uint32_t flags = objSym.isWeak() ? WASM_SYMBOL_BINDING_WEAK : 0; |
801 | flags |= mapVisibility(gvVisibility: objSym.getVisibility()); |
802 | |
803 | int c = objSym.getComdatIndex(); |
804 | bool excludedByComdat = c != -1 && !keptComdats[c]; |
805 | |
806 | if (objSym.isUndefined() || excludedByComdat) { |
807 | flags |= WASM_SYMBOL_UNDEFINED; |
808 | if (objSym.isExecutable()) |
809 | return symtab->addUndefinedFunction(name, importName: std::nullopt, importModule: std::nullopt, |
810 | flags, file: &f, signature: nullptr, isCalledDirectly: true); |
811 | return symtab->addUndefinedData(name, flags, file: &f); |
812 | } |
813 | |
814 | if (objSym.isExecutable()) |
815 | return symtab->addDefinedFunction(name, flags, file: &f, function: nullptr); |
816 | return symtab->addDefinedData(name, flags, file: &f, segment: nullptr, address: 0, size: 0); |
817 | } |
818 | |
819 | BitcodeFile::BitcodeFile(MemoryBufferRef m, StringRef archiveName, |
820 | uint64_t offsetInArchive, bool lazy) |
821 | : InputFile(BitcodeKind, m) { |
822 | this->lazy = lazy; |
823 | this->archiveName = std::string(archiveName); |
824 | |
825 | std::string path = mb.getBufferIdentifier().str(); |
826 | |
827 | // ThinLTO assumes that all MemoryBufferRefs given to it have a unique |
828 | // name. If two archives define two members with the same name, this |
829 | // causes a collision which result in only one of the objects being taken |
830 | // into consideration at LTO time (which very likely causes undefined |
831 | // symbols later in the link stage). So we append file offset to make |
832 | // filename unique. |
833 | StringRef name = archiveName.empty() |
834 | ? saver().save(S: path) |
835 | : saver().save(S: archiveName + "(" + path::filename(path) + |
836 | " at " + utostr(X: offsetInArchive) + ")" ); |
837 | MemoryBufferRef mbref(mb.getBuffer(), name); |
838 | |
839 | obj = check(e: lto::InputFile::create(Object: mbref)); |
840 | |
841 | // If this isn't part of an archive, it's eagerly linked, so mark it live. |
842 | if (archiveName.empty()) |
843 | markLive(); |
844 | } |
845 | |
846 | bool BitcodeFile::doneLTO = false; |
847 | |
848 | void BitcodeFile::parseLazy() { |
849 | for (auto [i, irSym] : llvm::enumerate(First: obj->symbols())) { |
850 | if (irSym.isUndefined()) |
851 | continue; |
852 | StringRef name = saver().save(S: irSym.getName()); |
853 | symtab->addLazy(name, f: this); |
854 | // addLazy() may trigger this->extract() if an existing symbol is an |
855 | // undefined symbol. If that happens, this function has served its purpose, |
856 | // and we can exit from the loop early. |
857 | if (!lazy) |
858 | break; |
859 | } |
860 | } |
861 | |
862 | void BitcodeFile::parse(StringRef symName) { |
863 | if (doneLTO) { |
864 | error(msg: toString(file: this) + ": attempt to add bitcode file after LTO (" + symName + ")" ); |
865 | return; |
866 | } |
867 | |
868 | Triple t(obj->getTargetTriple()); |
869 | if (!t.isWasm()) { |
870 | error(msg: toString(file: this) + ": machine type must be wasm32 or wasm64" ); |
871 | return; |
872 | } |
873 | checkArch(arch: t.getArch()); |
874 | std::vector<bool> keptComdats; |
875 | // TODO Support nodeduplicate |
876 | // https://github.com/llvm/llvm-project/issues/49875 |
877 | for (std::pair<StringRef, Comdat::SelectionKind> s : obj->getComdatTable()) |
878 | keptComdats.push_back(x: symtab->addComdat(name: s.first)); |
879 | |
880 | for (const lto::InputFile::Symbol &objSym : obj->symbols()) |
881 | symbols.push_back(x: createBitcodeSymbol(keptComdats, objSym, f&: *this)); |
882 | } |
883 | |
884 | } // namespace wasm |
885 | } // namespace lld |
886 | |