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