1 | //===- SyntheticSections.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 | // This file contains linker-synthesized sections. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "SyntheticSections.h" |
14 | |
15 | #include "InputChunks.h" |
16 | #include "InputElement.h" |
17 | #include "OutputSegment.h" |
18 | #include "SymbolTable.h" |
19 | #include "llvm/BinaryFormat/Wasm.h" |
20 | #include "llvm/Support/Path.h" |
21 | #include <optional> |
22 | |
23 | using namespace llvm; |
24 | using namespace llvm::wasm; |
25 | |
26 | namespace lld::wasm { |
27 | |
28 | OutStruct out; |
29 | |
30 | namespace { |
31 | |
32 | // Some synthetic sections (e.g. "name" and "linking") have subsections. |
33 | // Just like the synthetic sections themselves these need to be created before |
34 | // they can be written out (since they are preceded by their length). This |
35 | // class is used to create subsections and then write them into the stream |
36 | // of the parent section. |
37 | class SubSection { |
38 | public: |
39 | explicit SubSection(uint32_t type) : type(type) {} |
40 | |
41 | void writeTo(raw_ostream &to) { |
42 | writeUleb128(os&: to, number: type, msg: "subsection type"); |
43 | writeUleb128(os&: to, number: body.size(), msg: "subsection size"); |
44 | to.write(Ptr: body.data(), Size: body.size()); |
45 | } |
46 | |
47 | private: |
48 | uint32_t type; |
49 | std::string body; |
50 | |
51 | public: |
52 | raw_string_ostream os{body}; |
53 | }; |
54 | |
55 | } // namespace |
56 | |
57 | bool DylinkSection::isNeeded() const { |
58 | return ctx.isPic || |
59 | ctx.arg.unresolvedSymbols == UnresolvedPolicy::ImportDynamic || |
60 | !ctx.sharedFiles.empty(); |
61 | } |
62 | |
63 | void DylinkSection::writeBody() { |
64 | raw_ostream &os = bodyOutputStream; |
65 | |
66 | { |
67 | SubSection sub(WASM_DYLINK_MEM_INFO); |
68 | writeUleb128(os&: sub.os, number: memSize, msg: "MemSize"); |
69 | writeUleb128(os&: sub.os, number: memAlign, msg: "MemAlign"); |
70 | writeUleb128(os&: sub.os, number: out.elemSec->numEntries(), msg: "TableSize"); |
71 | writeUleb128(os&: sub.os, number: 0, msg: "TableAlign"); |
72 | sub.writeTo(to&: os); |
73 | } |
74 | |
75 | if (ctx.sharedFiles.size()) { |
76 | SubSection sub(WASM_DYLINK_NEEDED); |
77 | writeUleb128(os&: sub.os, number: ctx.sharedFiles.size(), msg: "Needed"); |
78 | for (auto *so : ctx.sharedFiles) |
79 | writeStr(os&: sub.os, string: llvm::sys::path::filename(path: so->getName()), msg: "so name"); |
80 | sub.writeTo(to&: os); |
81 | } |
82 | |
83 | // Under certain circumstances we need to include extra information about our |
84 | // exports and/or imports to the dynamic linker. |
85 | // For exports we need to notify the linker when an export is TLS since the |
86 | // exported value is relative to __tls_base rather than __memory_base. |
87 | // For imports we need to notify the dynamic linker when an import is weak |
88 | // so that knows not to report an error for such symbols. |
89 | std::vector<const Symbol *> importInfo; |
90 | std::vector<const Symbol *> exportInfo; |
91 | for (const Symbol *sym : symtab->symbols()) { |
92 | if (sym->isLive()) { |
93 | if (sym->isExported() && sym->isTLS() && isa<DefinedData>(Val: sym)) { |
94 | exportInfo.push_back(x: sym); |
95 | } |
96 | if (sym->isUndefWeak()) { |
97 | importInfo.push_back(x: sym); |
98 | } |
99 | } |
100 | } |
101 | |
102 | if (!exportInfo.empty()) { |
103 | SubSection sub(WASM_DYLINK_EXPORT_INFO); |
104 | writeUleb128(os&: sub.os, number: exportInfo.size(), msg: "num exports"); |
105 | |
106 | for (const Symbol *sym : exportInfo) { |
107 | LLVM_DEBUG(llvm::dbgs() << "export info: "<< toString(*sym) << "\n"); |
108 | StringRef name = sym->getName(); |
109 | if (auto *f = dyn_cast<DefinedFunction>(Val: sym)) { |
110 | if (std::optional<StringRef> exportName = |
111 | f->function->getExportName()) { |
112 | name = *exportName; |
113 | } |
114 | } |
115 | writeStr(os&: sub.os, string: name, msg: "sym name"); |
116 | writeUleb128(os&: sub.os, number: sym->flags, msg: "sym flags"); |
117 | } |
118 | |
119 | sub.writeTo(to&: os); |
120 | } |
121 | |
122 | if (!importInfo.empty()) { |
123 | SubSection sub(WASM_DYLINK_IMPORT_INFO); |
124 | writeUleb128(os&: sub.os, number: importInfo.size(), msg: "num imports"); |
125 | |
126 | for (const Symbol *sym : importInfo) { |
127 | LLVM_DEBUG(llvm::dbgs() << "imports info: "<< toString(*sym) << "\n"); |
128 | StringRef module = sym->importModule.value_or(u&: defaultModule); |
129 | StringRef name = sym->importName.value_or(u: sym->getName()); |
130 | writeStr(os&: sub.os, string: module, msg: "import module"); |
131 | writeStr(os&: sub.os, string: name, msg: "import name"); |
132 | writeUleb128(os&: sub.os, number: sym->flags, msg: "sym flags"); |
133 | } |
134 | |
135 | sub.writeTo(to&: os); |
136 | } |
137 | |
138 | if (!ctx.arg.rpath.empty()) { |
139 | SubSection sub(WASM_DYLINK_RUNTIME_PATH); |
140 | writeUleb128(os&: sub.os, number: ctx.arg.rpath.size(), msg: "num rpath entries"); |
141 | for (const auto ref : ctx.arg.rpath) |
142 | writeStr(os&: sub.os, string: ref, msg: "rpath entry"); |
143 | sub.writeTo(to&: os); |
144 | } |
145 | } |
146 | |
147 | uint32_t TypeSection::registerType(const WasmSignature &sig) { |
148 | auto pair = typeIndices.insert(KV: std::make_pair(x: sig, y: types.size())); |
149 | if (pair.second) { |
150 | LLVM_DEBUG(llvm::dbgs() << "registerType "<< toString(sig) << "\n"); |
151 | types.push_back(x: &sig); |
152 | } |
153 | return pair.first->second; |
154 | } |
155 | |
156 | uint32_t TypeSection::lookupType(const WasmSignature &sig) { |
157 | auto it = typeIndices.find(Val: sig); |
158 | if (it == typeIndices.end()) { |
159 | error(msg: "type not found: "+ toString(sig)); |
160 | return 0; |
161 | } |
162 | return it->second; |
163 | } |
164 | |
165 | void TypeSection::writeBody() { |
166 | writeUleb128(os&: bodyOutputStream, number: types.size(), msg: "type count"); |
167 | for (const WasmSignature *sig : types) |
168 | writeSig(os&: bodyOutputStream, sig: *sig); |
169 | } |
170 | |
171 | uint32_t ImportSection::getNumImports() const { |
172 | assert(isSealed); |
173 | uint32_t numImports = importedSymbols.size() + gotSymbols.size(); |
174 | if (ctx.arg.memoryImport.has_value()) |
175 | ++numImports; |
176 | return numImports; |
177 | } |
178 | |
179 | void ImportSection::addGOTEntry(Symbol *sym) { |
180 | assert(!isSealed); |
181 | if (sym->hasGOTIndex()) |
182 | return; |
183 | LLVM_DEBUG(dbgs() << "addGOTEntry: "<< toString(*sym) << "\n"); |
184 | sym->setGOTIndex(numImportedGlobals++); |
185 | if (ctx.isPic) { |
186 | // Any symbol that is assigned an normal GOT entry must be exported |
187 | // otherwise the dynamic linker won't be able create the entry that contains |
188 | // it. |
189 | sym->forceExport = true; |
190 | } |
191 | gotSymbols.push_back(x: sym); |
192 | } |
193 | |
194 | void ImportSection::addImport(Symbol *sym) { |
195 | assert(!isSealed); |
196 | StringRef module = sym->importModule.value_or(u&: defaultModule); |
197 | StringRef name = sym->importName.value_or(u: sym->getName()); |
198 | if (auto *f = dyn_cast<FunctionSymbol>(Val: sym)) { |
199 | ImportKey<WasmSignature> key(*(f->getSignature()), module, name); |
200 | auto entry = importedFunctions.try_emplace(Key: key, Args&: numImportedFunctions); |
201 | if (entry.second) { |
202 | importedSymbols.emplace_back(args&: sym); |
203 | f->setFunctionIndex(numImportedFunctions++); |
204 | } else { |
205 | f->setFunctionIndex(entry.first->second); |
206 | } |
207 | } else if (auto *g = dyn_cast<GlobalSymbol>(Val: sym)) { |
208 | ImportKey<WasmGlobalType> key(*(g->getGlobalType()), module, name); |
209 | auto entry = importedGlobals.try_emplace(Key: key, Args&: numImportedGlobals); |
210 | if (entry.second) { |
211 | importedSymbols.emplace_back(args&: sym); |
212 | g->setGlobalIndex(numImportedGlobals++); |
213 | } else { |
214 | g->setGlobalIndex(entry.first->second); |
215 | } |
216 | } else if (auto *t = dyn_cast<TagSymbol>(Val: sym)) { |
217 | ImportKey<WasmSignature> key(*(t->getSignature()), module, name); |
218 | auto entry = importedTags.try_emplace(Key: key, Args&: numImportedTags); |
219 | if (entry.second) { |
220 | importedSymbols.emplace_back(args&: sym); |
221 | t->setTagIndex(numImportedTags++); |
222 | } else { |
223 | t->setTagIndex(entry.first->second); |
224 | } |
225 | } else { |
226 | assert(TableSymbol::classof(sym)); |
227 | auto *table = cast<TableSymbol>(Val: sym); |
228 | ImportKey<WasmTableType> key(*(table->getTableType()), module, name); |
229 | auto entry = importedTables.try_emplace(Key: key, Args&: numImportedTables); |
230 | if (entry.second) { |
231 | importedSymbols.emplace_back(args&: sym); |
232 | table->setTableNumber(numImportedTables++); |
233 | } else { |
234 | table->setTableNumber(entry.first->second); |
235 | } |
236 | } |
237 | } |
238 | |
239 | void ImportSection::writeBody() { |
240 | raw_ostream &os = bodyOutputStream; |
241 | |
242 | writeUleb128(os, number: getNumImports(), msg: "import count"); |
243 | |
244 | bool is64 = ctx.arg.is64.value_or(u: false); |
245 | |
246 | if (ctx.arg.memoryImport) { |
247 | WasmImport import; |
248 | import.Module = ctx.arg.memoryImport->first; |
249 | import.Field = ctx.arg.memoryImport->second; |
250 | import.Kind = WASM_EXTERNAL_MEMORY; |
251 | import.Memory.Flags = 0; |
252 | import.Memory.Minimum = out.memorySec->numMemoryPages; |
253 | if (out.memorySec->maxMemoryPages != 0 || ctx.arg.sharedMemory) { |
254 | import.Memory.Flags |= WASM_LIMITS_FLAG_HAS_MAX; |
255 | import.Memory.Maximum = out.memorySec->maxMemoryPages; |
256 | } |
257 | if (ctx.arg.sharedMemory) |
258 | import.Memory.Flags |= WASM_LIMITS_FLAG_IS_SHARED; |
259 | if (is64) |
260 | import.Memory.Flags |= WASM_LIMITS_FLAG_IS_64; |
261 | writeImport(os, import); |
262 | } |
263 | |
264 | for (const Symbol *sym : importedSymbols) { |
265 | WasmImport import; |
266 | import.Field = sym->importName.value_or(u: sym->getName()); |
267 | import.Module = sym->importModule.value_or(u&: defaultModule); |
268 | |
269 | if (auto *functionSym = dyn_cast<FunctionSymbol>(Val: sym)) { |
270 | import.Kind = WASM_EXTERNAL_FUNCTION; |
271 | import.SigIndex = out.typeSec->lookupType(sig: *functionSym->signature); |
272 | } else if (auto *globalSym = dyn_cast<GlobalSymbol>(Val: sym)) { |
273 | import.Kind = WASM_EXTERNAL_GLOBAL; |
274 | import.Global = *globalSym->getGlobalType(); |
275 | } else if (auto *tagSym = dyn_cast<TagSymbol>(Val: sym)) { |
276 | import.Kind = WASM_EXTERNAL_TAG; |
277 | import.SigIndex = out.typeSec->lookupType(sig: *tagSym->signature); |
278 | } else { |
279 | auto *tableSym = cast<TableSymbol>(Val: sym); |
280 | import.Kind = WASM_EXTERNAL_TABLE; |
281 | import.Table = *tableSym->getTableType(); |
282 | } |
283 | writeImport(os, import); |
284 | } |
285 | |
286 | for (const Symbol *sym : gotSymbols) { |
287 | WasmImport import; |
288 | import.Kind = WASM_EXTERNAL_GLOBAL; |
289 | auto ptrType = is64 ? WASM_TYPE_I64 : WASM_TYPE_I32; |
290 | import.Global = {.Type: static_cast<uint8_t>(ptrType), .Mutable: true}; |
291 | if (isa<DataSymbol>(Val: sym)) |
292 | import.Module = "GOT.mem"; |
293 | else |
294 | import.Module = "GOT.func"; |
295 | import.Field = sym->getName(); |
296 | writeImport(os, import); |
297 | } |
298 | } |
299 | |
300 | void FunctionSection::writeBody() { |
301 | raw_ostream &os = bodyOutputStream; |
302 | |
303 | writeUleb128(os, number: inputFunctions.size(), msg: "function count"); |
304 | for (const InputFunction *func : inputFunctions) |
305 | writeUleb128(os, number: out.typeSec->lookupType(sig: func->signature), msg: "sig index"); |
306 | } |
307 | |
308 | void FunctionSection::addFunction(InputFunction *func) { |
309 | if (!func->live) |
310 | return; |
311 | uint32_t functionIndex = |
312 | out.importSec->getNumImportedFunctions() + inputFunctions.size(); |
313 | inputFunctions.emplace_back(args&: func); |
314 | func->setFunctionIndex(functionIndex); |
315 | } |
316 | |
317 | void TableSection::writeBody() { |
318 | raw_ostream &os = bodyOutputStream; |
319 | |
320 | writeUleb128(os, number: inputTables.size(), msg: "table count"); |
321 | for (const InputTable *table : inputTables) |
322 | writeTableType(os, type: table->getType()); |
323 | } |
324 | |
325 | void TableSection::addTable(InputTable *table) { |
326 | if (!table->live) |
327 | return; |
328 | // Some inputs require that the indirect function table be assigned to table |
329 | // number 0. |
330 | if (ctx.legacyFunctionTable && |
331 | isa<DefinedTable>(Val: ctx.sym.indirectFunctionTable) && |
332 | cast<DefinedTable>(Val: ctx.sym.indirectFunctionTable)->table == table) { |
333 | if (out.importSec->getNumImportedTables()) { |
334 | // Alack! Some other input imported a table, meaning that we are unable |
335 | // to assign table number 0 to the indirect function table. |
336 | for (const auto *culprit : out.importSec->importedSymbols) { |
337 | if (isa<UndefinedTable>(Val: culprit)) { |
338 | error(msg: "object file not built with 'reference-types' or " |
339 | "'call-indirect-overlong' feature conflicts with import of " |
340 | "table "+ |
341 | culprit->getName() + " by file "+ |
342 | toString(file: culprit->getFile())); |
343 | return; |
344 | } |
345 | } |
346 | llvm_unreachable("failed to find conflicting table import"); |
347 | } |
348 | inputTables.insert(position: inputTables.begin(), x: table); |
349 | return; |
350 | } |
351 | inputTables.push_back(x: table); |
352 | } |
353 | |
354 | void TableSection::assignIndexes() { |
355 | uint32_t tableNumber = out.importSec->getNumImportedTables(); |
356 | for (InputTable *t : inputTables) |
357 | t->assignIndex(index: tableNumber++); |
358 | } |
359 | |
360 | void MemorySection::writeBody() { |
361 | raw_ostream &os = bodyOutputStream; |
362 | |
363 | bool hasMax = maxMemoryPages != 0 || ctx.arg.sharedMemory; |
364 | writeUleb128(os, number: 1, msg: "memory count"); |
365 | unsigned flags = 0; |
366 | if (hasMax) |
367 | flags |= WASM_LIMITS_FLAG_HAS_MAX; |
368 | if (ctx.arg.sharedMemory) |
369 | flags |= WASM_LIMITS_FLAG_IS_SHARED; |
370 | if (ctx.arg.is64.value_or(u: false)) |
371 | flags |= WASM_LIMITS_FLAG_IS_64; |
372 | if (ctx.arg.pageSize != WasmDefaultPageSize) |
373 | flags |= WASM_LIMITS_FLAG_HAS_PAGE_SIZE; |
374 | writeUleb128(os, number: flags, msg: "memory limits flags"); |
375 | writeUleb128(os, number: numMemoryPages, msg: "initial pages"); |
376 | if (hasMax) |
377 | writeUleb128(os, number: maxMemoryPages, msg: "max pages"); |
378 | if (ctx.arg.pageSize != WasmDefaultPageSize) |
379 | writeUleb128(os, number: llvm::Log2_64(Value: ctx.arg.pageSize), msg: "page size"); |
380 | } |
381 | |
382 | void TagSection::writeBody() { |
383 | raw_ostream &os = bodyOutputStream; |
384 | |
385 | writeUleb128(os, number: inputTags.size(), msg: "tag count"); |
386 | for (InputTag *t : inputTags) { |
387 | writeUleb128(os, number: 0, msg: "tag attribute"); // Reserved "attribute" field |
388 | writeUleb128(os, number: out.typeSec->lookupType(sig: t->signature), msg: "sig index"); |
389 | } |
390 | } |
391 | |
392 | void TagSection::addTag(InputTag *tag) { |
393 | if (!tag->live) |
394 | return; |
395 | uint32_t tagIndex = out.importSec->getNumImportedTags() + inputTags.size(); |
396 | LLVM_DEBUG(dbgs() << "addTag: "<< tagIndex << "\n"); |
397 | tag->assignIndex(index: tagIndex); |
398 | inputTags.push_back(x: tag); |
399 | } |
400 | |
401 | void GlobalSection::assignIndexes() { |
402 | uint32_t globalIndex = out.importSec->getNumImportedGlobals(); |
403 | for (InputGlobal *g : inputGlobals) |
404 | g->assignIndex(index: globalIndex++); |
405 | for (Symbol *sym : internalGotSymbols) |
406 | sym->setGOTIndex(globalIndex++); |
407 | isSealed = true; |
408 | } |
409 | |
410 | static void ensureIndirectFunctionTable() { |
411 | if (!ctx.sym.indirectFunctionTable) |
412 | ctx.sym.indirectFunctionTable = |
413 | symtab->resolveIndirectFunctionTable(/*required =*/true); |
414 | } |
415 | |
416 | void GlobalSection::addInternalGOTEntry(Symbol *sym) { |
417 | assert(!isSealed); |
418 | if (sym->requiresGOT) |
419 | return; |
420 | LLVM_DEBUG(dbgs() << "addInternalGOTEntry: "<< sym->getName() << " " |
421 | << toString(sym->kind()) << "\n"); |
422 | sym->requiresGOT = true; |
423 | if (auto *F = dyn_cast<FunctionSymbol>(Val: sym)) { |
424 | ensureIndirectFunctionTable(); |
425 | out.elemSec->addEntry(sym: F); |
426 | } |
427 | internalGotSymbols.push_back(x: sym); |
428 | } |
429 | |
430 | void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const { |
431 | assert(!ctx.arg.extendedConst); |
432 | bool is64 = ctx.arg.is64.value_or(u: false); |
433 | unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST |
434 | : WASM_OPCODE_I32_CONST; |
435 | unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD |
436 | : WASM_OPCODE_I32_ADD; |
437 | |
438 | for (const Symbol *sym : internalGotSymbols) { |
439 | if (TLS != sym->isTLS()) |
440 | continue; |
441 | |
442 | if (auto *d = dyn_cast<DefinedData>(Val: sym)) { |
443 | // Get __memory_base |
444 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET"); |
445 | if (sym->isTLS()) |
446 | writeUleb128(os, number: ctx.sym.tlsBase->getGlobalIndex(), msg: "__tls_base"); |
447 | else |
448 | writeUleb128(os, number: ctx.sym.memoryBase->getGlobalIndex(), msg: "__memory_base"); |
449 | |
450 | // Add the virtual address of the data symbol |
451 | writeU8(os, byte: opcode_ptr_const, msg: "CONST"); |
452 | writeSleb128(os, number: d->getVA(), msg: "offset"); |
453 | } else if (auto *f = dyn_cast<FunctionSymbol>(Val: sym)) { |
454 | if (f->isStub) |
455 | continue; |
456 | // Get __table_base |
457 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET"); |
458 | writeUleb128(os, number: ctx.sym.tableBase->getGlobalIndex(), msg: "__table_base"); |
459 | |
460 | // Add the table index to __table_base |
461 | writeU8(os, byte: opcode_ptr_const, msg: "CONST"); |
462 | writeSleb128(os, number: f->getTableIndex(), msg: "offset"); |
463 | } else { |
464 | assert(isa<UndefinedData>(sym) || isa<SharedData>(sym)); |
465 | continue; |
466 | } |
467 | writeU8(os, byte: opcode_ptr_add, msg: "ADD"); |
468 | writeU8(os, byte: WASM_OPCODE_GLOBAL_SET, msg: "GLOBAL_SET"); |
469 | writeUleb128(os, number: sym->getGOTIndex(), msg: "got_entry"); |
470 | } |
471 | } |
472 | |
473 | void GlobalSection::writeBody() { |
474 | raw_ostream &os = bodyOutputStream; |
475 | |
476 | writeUleb128(os, number: numGlobals(), msg: "global count"); |
477 | for (InputGlobal *g : inputGlobals) { |
478 | writeGlobalType(os, type: g->getType()); |
479 | writeInitExpr(os, initExpr: g->getInitExpr()); |
480 | } |
481 | bool is64 = ctx.arg.is64.value_or(u: false); |
482 | uint8_t itype = is64 ? WASM_TYPE_I64 : WASM_TYPE_I32; |
483 | for (const Symbol *sym : internalGotSymbols) { |
484 | bool mutable_ = false; |
485 | if (!sym->isStub) { |
486 | // In the case of dynamic linking, unless we have 'extended-const' |
487 | // available, these global must to be mutable since they get updated to |
488 | // the correct runtime value during `__wasm_apply_global_relocs`. |
489 | if (!ctx.arg.extendedConst && ctx.isPic && !sym->isTLS()) |
490 | mutable_ = true; |
491 | // With multi-theadeding any TLS globals must be mutable since they get |
492 | // set during `__wasm_apply_global_tls_relocs` |
493 | if (ctx.arg.sharedMemory && sym->isTLS()) |
494 | mutable_ = true; |
495 | } |
496 | WasmGlobalType type{.Type: itype, .Mutable: mutable_}; |
497 | writeGlobalType(os, type); |
498 | |
499 | bool useExtendedConst = false; |
500 | uint32_t globalIdx; |
501 | int64_t offset; |
502 | if (ctx.arg.extendedConst && ctx.isPic) { |
503 | if (auto *d = dyn_cast<DefinedData>(Val: sym)) { |
504 | if (!sym->isTLS()) { |
505 | globalIdx = ctx.sym.memoryBase->getGlobalIndex(); |
506 | offset = d->getVA(); |
507 | useExtendedConst = true; |
508 | } |
509 | } else if (auto *f = dyn_cast<FunctionSymbol>(Val: sym)) { |
510 | if (!sym->isStub) { |
511 | globalIdx = ctx.sym.tableBase->getGlobalIndex(); |
512 | offset = f->getTableIndex(); |
513 | useExtendedConst = true; |
514 | } |
515 | } |
516 | } |
517 | if (useExtendedConst) { |
518 | // We can use an extended init expression to add a constant |
519 | // offset of __memory_base/__table_base. |
520 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "global get"); |
521 | writeUleb128(os, number: globalIdx, msg: "literal (global index)"); |
522 | if (offset) { |
523 | writePtrConst(os, number: offset, is64, msg: "offset"); |
524 | writeU8(os, byte: is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, msg: "add"); |
525 | } |
526 | writeU8(os, byte: WASM_OPCODE_END, msg: "opcode:end"); |
527 | } else { |
528 | WasmInitExpr initExpr; |
529 | if (auto *d = dyn_cast<DefinedData>(Val: sym)) |
530 | // In the sharedMemory case TLS globals are set during |
531 | // `__wasm_apply_global_tls_relocs`, but in the non-shared case |
532 | // we know the absolute value at link time. |
533 | initExpr = intConst(value: d->getVA(/*absolute=*/!ctx.arg.sharedMemory), is64); |
534 | else if (auto *f = dyn_cast<FunctionSymbol>(Val: sym)) |
535 | initExpr = intConst(value: f->isStub ? 0 : f->getTableIndex(), is64); |
536 | else { |
537 | assert(isa<UndefinedData>(sym) || isa<SharedData>(sym)); |
538 | initExpr = intConst(value: 0, is64); |
539 | } |
540 | writeInitExpr(os, initExpr); |
541 | } |
542 | } |
543 | for (const DefinedData *sym : dataAddressGlobals) { |
544 | WasmGlobalType type{.Type: itype, .Mutable: false}; |
545 | writeGlobalType(os, type); |
546 | writeInitExpr(os, initExpr: intConst(value: sym->getVA(), is64)); |
547 | } |
548 | } |
549 | |
550 | void GlobalSection::addGlobal(InputGlobal *global) { |
551 | assert(!isSealed); |
552 | if (!global->live) |
553 | return; |
554 | inputGlobals.push_back(x: global); |
555 | } |
556 | |
557 | void ExportSection::writeBody() { |
558 | raw_ostream &os = bodyOutputStream; |
559 | |
560 | writeUleb128(os, number: exports.size(), msg: "export count"); |
561 | for (const WasmExport &export_ : exports) |
562 | writeExport(os, export_); |
563 | } |
564 | |
565 | bool StartSection::isNeeded() const { return ctx.sym.startFunction != nullptr; } |
566 | |
567 | void StartSection::writeBody() { |
568 | raw_ostream &os = bodyOutputStream; |
569 | writeUleb128(os, number: ctx.sym.startFunction->getFunctionIndex(), msg: "function index"); |
570 | } |
571 | |
572 | void ElemSection::addEntry(FunctionSymbol *sym) { |
573 | // Don't add stub functions to the wasm table. The address of all stub |
574 | // functions should be zero and they should they don't appear in the table. |
575 | // They only exist so that the calls to missing functions can validate. |
576 | if (sym->hasTableIndex() || sym->isStub) |
577 | return; |
578 | sym->setTableIndex(ctx.arg.tableBase + indirectFunctions.size()); |
579 | indirectFunctions.emplace_back(args&: sym); |
580 | } |
581 | |
582 | void ElemSection::writeBody() { |
583 | raw_ostream &os = bodyOutputStream; |
584 | |
585 | assert(ctx.sym.indirectFunctionTable); |
586 | writeUleb128(os, number: 1, msg: "segment count"); |
587 | uint32_t tableNumber = ctx.sym.indirectFunctionTable->getTableNumber(); |
588 | uint32_t flags = 0; |
589 | if (tableNumber) |
590 | flags |= WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER; |
591 | writeUleb128(os, number: flags, msg: "elem segment flags"); |
592 | if (flags & WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER) |
593 | writeUleb128(os, number: tableNumber, msg: "table number"); |
594 | |
595 | WasmInitExpr initExpr; |
596 | initExpr.Extended = false; |
597 | if (ctx.isPic) { |
598 | initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET; |
599 | initExpr.Inst.Value.Global = ctx.sym.tableBase->getGlobalIndex(); |
600 | } else { |
601 | bool is64 = ctx.arg.is64.value_or(u: false); |
602 | initExpr = intConst(value: ctx.arg.tableBase, is64); |
603 | } |
604 | writeInitExpr(os, initExpr); |
605 | |
606 | if (flags & WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) { |
607 | // We only write active function table initializers, for which the elem kind |
608 | // is specified to be written as 0x00 and interpreted to mean "funcref". |
609 | const uint8_t elemKind = 0; |
610 | writeU8(os, byte: elemKind, msg: "elem kind"); |
611 | } |
612 | |
613 | writeUleb128(os, number: indirectFunctions.size(), msg: "elem count"); |
614 | uint32_t tableIndex = ctx.arg.tableBase; |
615 | for (const FunctionSymbol *sym : indirectFunctions) { |
616 | assert(sym->getTableIndex() == tableIndex); |
617 | (void) tableIndex; |
618 | writeUleb128(os, number: sym->getFunctionIndex(), msg: "function index"); |
619 | ++tableIndex; |
620 | } |
621 | } |
622 | |
623 | DataCountSection::DataCountSection(ArrayRef<OutputSegment *> segments) |
624 | : SyntheticSection(llvm::wasm::WASM_SEC_DATACOUNT), |
625 | numSegments(llvm::count_if(Range&: segments, P: [](OutputSegment *const segment) { |
626 | return segment->requiredInBinary(); |
627 | })) {} |
628 | |
629 | void DataCountSection::writeBody() { |
630 | writeUleb128(os&: bodyOutputStream, number: numSegments, msg: "data count"); |
631 | } |
632 | |
633 | bool DataCountSection::isNeeded() const { |
634 | return numSegments && ctx.arg.sharedMemory; |
635 | } |
636 | |
637 | void LinkingSection::writeBody() { |
638 | raw_ostream &os = bodyOutputStream; |
639 | |
640 | writeUleb128(os, number: WasmMetadataVersion, msg: "Version"); |
641 | |
642 | if (!symtabEntries.empty()) { |
643 | SubSection sub(WASM_SYMBOL_TABLE); |
644 | writeUleb128(os&: sub.os, number: symtabEntries.size(), msg: "num symbols"); |
645 | |
646 | for (const Symbol *sym : symtabEntries) { |
647 | assert(sym->isDefined() || sym->isUndefined()); |
648 | WasmSymbolType kind = sym->getWasmType(); |
649 | uint32_t flags = sym->flags; |
650 | |
651 | writeU8(os&: sub.os, byte: kind, msg: "sym kind"); |
652 | writeUleb128(os&: sub.os, number: flags, msg: "sym flags"); |
653 | |
654 | if (auto *f = dyn_cast<FunctionSymbol>(Val: sym)) { |
655 | if (auto *d = dyn_cast<DefinedFunction>(Val: sym)) { |
656 | writeUleb128(os&: sub.os, number: d->getExportedFunctionIndex(), msg: "index"); |
657 | } else { |
658 | writeUleb128(os&: sub.os, number: f->getFunctionIndex(), msg: "index"); |
659 | } |
660 | if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) |
661 | writeStr(os&: sub.os, string: sym->getName(), msg: "sym name"); |
662 | } else if (auto *g = dyn_cast<GlobalSymbol>(Val: sym)) { |
663 | writeUleb128(os&: sub.os, number: g->getGlobalIndex(), msg: "index"); |
664 | if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) |
665 | writeStr(os&: sub.os, string: sym->getName(), msg: "sym name"); |
666 | } else if (auto *t = dyn_cast<TagSymbol>(Val: sym)) { |
667 | writeUleb128(os&: sub.os, number: t->getTagIndex(), msg: "index"); |
668 | if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) |
669 | writeStr(os&: sub.os, string: sym->getName(), msg: "sym name"); |
670 | } else if (auto *t = dyn_cast<TableSymbol>(Val: sym)) { |
671 | writeUleb128(os&: sub.os, number: t->getTableNumber(), msg: "table number"); |
672 | if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0) |
673 | writeStr(os&: sub.os, string: sym->getName(), msg: "sym name"); |
674 | } else if (isa<DataSymbol>(Val: sym)) { |
675 | writeStr(os&: sub.os, string: sym->getName(), msg: "sym name"); |
676 | if (auto *dataSym = dyn_cast<DefinedData>(Val: sym)) { |
677 | if (dataSym->segment) { |
678 | writeUleb128(os&: sub.os, number: dataSym->getOutputSegmentIndex(), msg: "index"); |
679 | writeUleb128(os&: sub.os, number: dataSym->getOutputSegmentOffset(), |
680 | msg: "data offset"); |
681 | } else { |
682 | writeUleb128(os&: sub.os, number: 0, msg: "index"); |
683 | writeUleb128(os&: sub.os, number: dataSym->getVA(), msg: "data offset"); |
684 | } |
685 | writeUleb128(os&: sub.os, number: dataSym->getSize(), msg: "data size"); |
686 | } |
687 | } else { |
688 | auto *s = cast<OutputSectionSymbol>(Val: sym); |
689 | writeUleb128(os&: sub.os, number: s->section->sectionIndex, msg: "sym section index"); |
690 | } |
691 | } |
692 | |
693 | sub.writeTo(to&: os); |
694 | } |
695 | |
696 | if (dataSegments.size()) { |
697 | SubSection sub(WASM_SEGMENT_INFO); |
698 | writeUleb128(os&: sub.os, number: dataSegments.size(), msg: "num data segments"); |
699 | for (const OutputSegment *s : dataSegments) { |
700 | writeStr(os&: sub.os, string: s->name, msg: "segment name"); |
701 | writeUleb128(os&: sub.os, number: s->alignment, msg: "alignment"); |
702 | writeUleb128(os&: sub.os, number: s->linkingFlags, msg: "flags"); |
703 | } |
704 | sub.writeTo(to&: os); |
705 | } |
706 | |
707 | if (!initFunctions.empty()) { |
708 | SubSection sub(WASM_INIT_FUNCS); |
709 | writeUleb128(os&: sub.os, number: initFunctions.size(), msg: "num init functions"); |
710 | for (const WasmInitEntry &f : initFunctions) { |
711 | writeUleb128(os&: sub.os, number: f.priority, msg: "priority"); |
712 | writeUleb128(os&: sub.os, number: f.sym->getOutputSymbolIndex(), msg: "function index"); |
713 | } |
714 | sub.writeTo(to&: os); |
715 | } |
716 | |
717 | struct ComdatEntry { |
718 | unsigned kind; |
719 | uint32_t index; |
720 | }; |
721 | std::map<StringRef, std::vector<ComdatEntry>> comdats; |
722 | |
723 | for (const InputFunction *f : out.functionSec->inputFunctions) { |
724 | StringRef comdat = f->getComdatName(); |
725 | if (!comdat.empty()) |
726 | comdats[comdat].emplace_back( |
727 | args: ComdatEntry{.kind: WASM_COMDAT_FUNCTION, .index: f->getFunctionIndex()}); |
728 | } |
729 | for (uint32_t i = 0; i < dataSegments.size(); ++i) { |
730 | const auto &inputSegments = dataSegments[i]->inputSegments; |
731 | if (inputSegments.empty()) |
732 | continue; |
733 | StringRef comdat = inputSegments[0]->getComdatName(); |
734 | #ifndef NDEBUG |
735 | for (const InputChunk *isec : inputSegments) |
736 | assert(isec->getComdatName() == comdat); |
737 | #endif |
738 | if (!comdat.empty()) |
739 | comdats[comdat].emplace_back(args: ComdatEntry{.kind: WASM_COMDAT_DATA, .index: i}); |
740 | } |
741 | |
742 | if (!comdats.empty()) { |
743 | SubSection sub(WASM_COMDAT_INFO); |
744 | writeUleb128(os&: sub.os, number: comdats.size(), msg: "num comdats"); |
745 | for (const auto &c : comdats) { |
746 | writeStr(os&: sub.os, string: c.first, msg: "comdat name"); |
747 | writeUleb128(os&: sub.os, number: 0, msg: "comdat flags"); // flags for future use |
748 | writeUleb128(os&: sub.os, number: c.second.size(), msg: "num entries"); |
749 | for (const ComdatEntry &entry : c.second) { |
750 | writeU8(os&: sub.os, byte: entry.kind, msg: "entry kind"); |
751 | writeUleb128(os&: sub.os, number: entry.index, msg: "entry index"); |
752 | } |
753 | } |
754 | sub.writeTo(to&: os); |
755 | } |
756 | } |
757 | |
758 | void LinkingSection::addToSymtab(Symbol *sym) { |
759 | sym->setOutputSymbolIndex(symtabEntries.size()); |
760 | symtabEntries.emplace_back(args&: sym); |
761 | } |
762 | |
763 | unsigned NameSection::numNamedFunctions() const { |
764 | unsigned numNames = out.importSec->getNumImportedFunctions(); |
765 | |
766 | for (const InputFunction *f : out.functionSec->inputFunctions) |
767 | if (!f->name.empty() || !f->debugName.empty()) |
768 | ++numNames; |
769 | |
770 | return numNames; |
771 | } |
772 | |
773 | unsigned NameSection::numNamedGlobals() const { |
774 | unsigned numNames = out.importSec->getNumImportedGlobals(); |
775 | |
776 | for (const InputGlobal *g : out.globalSec->inputGlobals) |
777 | if (!g->getName().empty()) |
778 | ++numNames; |
779 | |
780 | numNames += out.globalSec->internalGotSymbols.size(); |
781 | return numNames; |
782 | } |
783 | |
784 | unsigned NameSection::numNamedDataSegments() const { |
785 | unsigned numNames = 0; |
786 | |
787 | for (const OutputSegment *s : segments) |
788 | if (!s->name.empty() && s->requiredInBinary()) |
789 | ++numNames; |
790 | |
791 | return numNames; |
792 | } |
793 | |
794 | // Create the custom "name" section containing debug symbol names. |
795 | void NameSection::writeBody() { |
796 | { |
797 | SubSection sub(WASM_NAMES_MODULE); |
798 | StringRef moduleName = ctx.arg.soName; |
799 | if (ctx.arg.soName.empty()) |
800 | moduleName = llvm::sys::path::filename(path: ctx.arg.outputFile); |
801 | writeStr(os&: sub.os, string: moduleName, msg: "module name"); |
802 | sub.writeTo(to&: bodyOutputStream); |
803 | } |
804 | |
805 | unsigned count = numNamedFunctions(); |
806 | if (count) { |
807 | SubSection sub(WASM_NAMES_FUNCTION); |
808 | writeUleb128(os&: sub.os, number: count, msg: "name count"); |
809 | |
810 | // Function names appear in function index order. As it happens |
811 | // importedSymbols and inputFunctions are numbered in order with imported |
812 | // functions coming first. |
813 | for (const Symbol *s : out.importSec->importedSymbols) { |
814 | if (auto *f = dyn_cast<FunctionSymbol>(Val: s)) { |
815 | writeUleb128(os&: sub.os, number: f->getFunctionIndex(), msg: "func index"); |
816 | writeStr(os&: sub.os, string: toString(sym: *s), msg: "symbol name"); |
817 | } |
818 | } |
819 | for (const InputFunction *f : out.functionSec->inputFunctions) { |
820 | if (!f->name.empty()) { |
821 | writeUleb128(os&: sub.os, number: f->getFunctionIndex(), msg: "func index"); |
822 | if (!f->debugName.empty()) { |
823 | writeStr(os&: sub.os, string: f->debugName, msg: "symbol name"); |
824 | } else { |
825 | writeStr(os&: sub.os, string: maybeDemangleSymbol(name: f->name), msg: "symbol name"); |
826 | } |
827 | } |
828 | } |
829 | sub.writeTo(to&: bodyOutputStream); |
830 | } |
831 | |
832 | count = numNamedGlobals(); |
833 | if (count) { |
834 | SubSection sub(WASM_NAMES_GLOBAL); |
835 | writeUleb128(os&: sub.os, number: count, msg: "name count"); |
836 | |
837 | for (const Symbol *s : out.importSec->importedSymbols) { |
838 | if (auto *g = dyn_cast<GlobalSymbol>(Val: s)) { |
839 | writeUleb128(os&: sub.os, number: g->getGlobalIndex(), msg: "global index"); |
840 | writeStr(os&: sub.os, string: toString(sym: *s), msg: "symbol name"); |
841 | } |
842 | } |
843 | for (const Symbol *s : out.importSec->gotSymbols) { |
844 | writeUleb128(os&: sub.os, number: s->getGOTIndex(), msg: "global index"); |
845 | writeStr(os&: sub.os, string: toString(sym: *s), msg: "symbol name"); |
846 | } |
847 | for (const InputGlobal *g : out.globalSec->inputGlobals) { |
848 | if (!g->getName().empty()) { |
849 | writeUleb128(os&: sub.os, number: g->getAssignedIndex(), msg: "global index"); |
850 | writeStr(os&: sub.os, string: maybeDemangleSymbol(name: g->getName()), msg: "symbol name"); |
851 | } |
852 | } |
853 | for (Symbol *s : out.globalSec->internalGotSymbols) { |
854 | writeUleb128(os&: sub.os, number: s->getGOTIndex(), msg: "global index"); |
855 | if (isa<FunctionSymbol>(Val: s)) |
856 | writeStr(os&: sub.os, string: "GOT.func.internal."+ toString(sym: *s), msg: "symbol name"); |
857 | else |
858 | writeStr(os&: sub.os, string: "GOT.data.internal."+ toString(sym: *s), msg: "symbol name"); |
859 | } |
860 | |
861 | sub.writeTo(to&: bodyOutputStream); |
862 | } |
863 | |
864 | count = numNamedDataSegments(); |
865 | if (count) { |
866 | SubSection sub(WASM_NAMES_DATA_SEGMENT); |
867 | writeUleb128(os&: sub.os, number: count, msg: "name count"); |
868 | |
869 | for (OutputSegment *s : segments) { |
870 | if (!s->name.empty() && s->requiredInBinary()) { |
871 | writeUleb128(os&: sub.os, number: s->index, msg: "global index"); |
872 | writeStr(os&: sub.os, string: s->name, msg: "segment name"); |
873 | } |
874 | } |
875 | |
876 | sub.writeTo(to&: bodyOutputStream); |
877 | } |
878 | } |
879 | |
880 | void ProducersSection::addInfo(const WasmProducerInfo &info) { |
881 | for (auto &producers : |
882 | {std::make_pair(x: &info.Languages, y: &languages), |
883 | std::make_pair(x: &info.Tools, y: &tools), std::make_pair(x: &info.SDKs, y: &sDKs)}) |
884 | for (auto &producer : *producers.first) |
885 | if (llvm::none_of(Range&: *producers.second, |
886 | P: [&](std::pair<std::string, std::string> seen) { |
887 | return seen.first == producer.first; |
888 | })) |
889 | producers.second->push_back(Elt: producer); |
890 | } |
891 | |
892 | void ProducersSection::writeBody() { |
893 | auto &os = bodyOutputStream; |
894 | writeUleb128(os, number: fieldCount(), msg: "field count"); |
895 | for (auto &field : |
896 | {std::make_pair(x: "language", y&: languages), |
897 | std::make_pair(x: "processed-by", y&: tools), std::make_pair(x: "sdk", y&: sDKs)}) { |
898 | if (field.second.empty()) |
899 | continue; |
900 | writeStr(os, string: field.first, msg: "field name"); |
901 | writeUleb128(os, number: field.second.size(), msg: "number of entries"); |
902 | for (auto &entry : field.second) { |
903 | writeStr(os, string: entry.first, msg: "producer name"); |
904 | writeStr(os, string: entry.second, msg: "producer version"); |
905 | } |
906 | } |
907 | } |
908 | |
909 | void TargetFeaturesSection::writeBody() { |
910 | SmallVector<std::string, 8> emitted(features.begin(), features.end()); |
911 | llvm::sort(C&: emitted); |
912 | auto &os = bodyOutputStream; |
913 | writeUleb128(os, number: emitted.size(), msg: "feature count"); |
914 | for (auto &feature : emitted) { |
915 | writeU8(os, byte: WASM_FEATURE_PREFIX_USED, msg: "feature used prefix"); |
916 | writeStr(os, string: feature, msg: "feature name"); |
917 | } |
918 | } |
919 | |
920 | void RelocSection::writeBody() { |
921 | uint32_t count = sec->getNumRelocations(); |
922 | assert(sec->sectionIndex != UINT32_MAX); |
923 | writeUleb128(os&: bodyOutputStream, number: sec->sectionIndex, msg: "reloc section"); |
924 | writeUleb128(os&: bodyOutputStream, number: count, msg: "reloc count"); |
925 | sec->writeRelocations(os&: bodyOutputStream); |
926 | } |
927 | |
928 | static size_t getHashSize() { |
929 | switch (ctx.arg.buildId) { |
930 | case BuildIdKind::Fast: |
931 | case BuildIdKind::Uuid: |
932 | return 16; |
933 | case BuildIdKind::Sha1: |
934 | return 20; |
935 | case BuildIdKind::Hexstring: |
936 | return ctx.arg.buildIdVector.size(); |
937 | case BuildIdKind::None: |
938 | return 0; |
939 | } |
940 | llvm_unreachable("build id kind not implemented"); |
941 | } |
942 | |
943 | BuildIdSection::BuildIdSection() |
944 | : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, buildIdSectionName), |
945 | hashSize(getHashSize()) {} |
946 | |
947 | void BuildIdSection::writeBody() { |
948 | LLVM_DEBUG(llvm::dbgs() << "BuildId writebody\n"); |
949 | // Write hash size |
950 | auto &os = bodyOutputStream; |
951 | writeUleb128(os, number: hashSize, msg: "build id size"); |
952 | writeBytes(os, bytes: std::vector<char>(hashSize, ' ').data(), count: hashSize, |
953 | msg: "placeholder"); |
954 | } |
955 | |
956 | void BuildIdSection::writeBuildId(llvm::ArrayRef<uint8_t> buf) { |
957 | assert(buf.size() == hashSize); |
958 | LLVM_DEBUG(dbgs() << "buildid write "<< buf.size() << " " |
959 | << hashPlaceholderPtr << '\n'); |
960 | memcpy(dest: hashPlaceholderPtr, src: buf.data(), n: hashSize); |
961 | } |
962 | |
963 | } // namespace wasm::lld |
964 |
Definitions
- out
- SubSection
- SubSection
- writeTo
- isNeeded
- writeBody
- registerType
- lookupType
- writeBody
- getNumImports
- addGOTEntry
- addImport
- writeBody
- writeBody
- addFunction
- writeBody
- addTable
- assignIndexes
- writeBody
- writeBody
- addTag
- assignIndexes
- ensureIndirectFunctionTable
- addInternalGOTEntry
- generateRelocationCode
- writeBody
- addGlobal
- writeBody
- isNeeded
- writeBody
- addEntry
- writeBody
- DataCountSection
- writeBody
- isNeeded
- writeBody
- addToSymtab
- numNamedFunctions
- numNamedGlobals
- numNamedDataSegments
- writeBody
- addInfo
- writeBody
- writeBody
- writeBody
- getHashSize
- BuildIdSection
- writeBody
Improve your Profiling and Debugging skills
Find out more