1 | //===- Writer.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 "Writer.h" |
10 | #include "Config.h" |
11 | #include "InputChunks.h" |
12 | #include "InputElement.h" |
13 | #include "MapFile.h" |
14 | #include "OutputSections.h" |
15 | #include "OutputSegment.h" |
16 | #include "Relocations.h" |
17 | #include "SymbolTable.h" |
18 | #include "SyntheticSections.h" |
19 | #include "WriterUtils.h" |
20 | #include "lld/Common/Arrays.h" |
21 | #include "lld/Common/CommonLinkerContext.h" |
22 | #include "lld/Common/Strings.h" |
23 | #include "llvm/ADT/ArrayRef.h" |
24 | #include "llvm/ADT/DenseSet.h" |
25 | #include "llvm/ADT/MapVector.h" |
26 | #include "llvm/ADT/SmallSet.h" |
27 | #include "llvm/ADT/SmallVector.h" |
28 | #include "llvm/ADT/StringMap.h" |
29 | #include "llvm/BinaryFormat/Wasm.h" |
30 | #include "llvm/BinaryFormat/WasmTraits.h" |
31 | #include "llvm/Support/FileOutputBuffer.h" |
32 | #include "llvm/Support/Format.h" |
33 | #include "llvm/Support/FormatVariadic.h" |
34 | #include "llvm/Support/LEB128.h" |
35 | #include "llvm/Support/Parallel.h" |
36 | #include "llvm/Support/RandomNumberGenerator.h" |
37 | #include "llvm/Support/SHA1.h" |
38 | #include "llvm/Support/xxhash.h" |
39 | |
40 | #include <cstdarg> |
41 | #include <map> |
42 | #include <optional> |
43 | |
44 | #define DEBUG_TYPE "lld" |
45 | |
46 | using namespace llvm; |
47 | using namespace llvm::wasm; |
48 | |
49 | namespace lld::wasm { |
50 | static constexpr int stackAlignment = 16; |
51 | static constexpr int heapAlignment = 16; |
52 | |
53 | namespace { |
54 | |
55 | // The writer writes a SymbolTable result to a file. |
56 | class Writer { |
57 | public: |
58 | void run(); |
59 | |
60 | private: |
61 | void openFile(); |
62 | |
63 | bool needsPassiveInitialization(const OutputSegment *segment); |
64 | bool hasPassiveInitializedSegments(); |
65 | |
66 | void createSyntheticInitFunctions(); |
67 | void createInitMemoryFunction(); |
68 | void createStartFunction(); |
69 | void createApplyDataRelocationsFunction(); |
70 | void createApplyGlobalRelocationsFunction(); |
71 | void createApplyTLSRelocationsFunction(); |
72 | void createApplyGlobalTLSRelocationsFunction(); |
73 | void createCallCtorsFunction(); |
74 | void createInitTLSFunction(); |
75 | void createCommandExportWrappers(); |
76 | void createCommandExportWrapper(uint32_t functionIndex, DefinedFunction *f); |
77 | |
78 | void assignIndexes(); |
79 | void populateSymtab(); |
80 | void populateProducers(); |
81 | void populateTargetFeatures(); |
82 | // populateTargetFeatures happens early on so some checks are delayed |
83 | // until imports and exports are finalized. There are run unstead |
84 | // in checkImportExportTargetFeatures |
85 | void checkImportExportTargetFeatures(); |
86 | void calculateInitFunctions(); |
87 | void calculateImports(); |
88 | void calculateExports(); |
89 | void calculateCustomSections(); |
90 | void calculateTypes(); |
91 | void createOutputSegments(); |
92 | OutputSegment *createOutputSegment(StringRef name); |
93 | void combineOutputSegments(); |
94 | void layoutMemory(); |
95 | void createHeader(); |
96 | |
97 | void addSection(OutputSection *sec); |
98 | |
99 | void addSections(); |
100 | |
101 | void createCustomSections(); |
102 | void createSyntheticSections(); |
103 | void createSyntheticSectionsPostLayout(); |
104 | void finalizeSections(); |
105 | |
106 | // Custom sections |
107 | void createRelocSections(); |
108 | |
109 | void writeHeader(); |
110 | void writeSections(); |
111 | void writeBuildId(); |
112 | |
113 | uint64_t fileSize = 0; |
114 | |
115 | std::vector<WasmInitEntry> initFunctions; |
116 | llvm::MapVector<StringRef, std::vector<InputChunk *>> customSectionMapping; |
117 | |
118 | // Stable storage for command export wrapper function name strings. |
119 | std::list<std::string> commandExportWrapperNames; |
120 | |
121 | // Elements that are used to construct the final output |
122 | std::string ; |
123 | std::vector<OutputSection *> outputSections; |
124 | |
125 | std::unique_ptr<FileOutputBuffer> buffer; |
126 | |
127 | std::vector<OutputSegment *> segments; |
128 | llvm::SmallDenseMap<StringRef, OutputSegment *> segmentMap; |
129 | }; |
130 | |
131 | } // anonymous namespace |
132 | |
133 | void Writer::calculateCustomSections() { |
134 | log(msg: "calculateCustomSections" ); |
135 | bool stripDebug = config->stripDebug || config->stripAll; |
136 | for (ObjFile *file : ctx.objectFiles) { |
137 | for (InputChunk *section : file->customSections) { |
138 | // Exclude COMDAT sections that are not selected for inclusion |
139 | if (section->discarded) |
140 | continue; |
141 | StringRef name = section->name; |
142 | // These custom sections are known the linker and synthesized rather than |
143 | // blindly copied. |
144 | if (name == "linking" || name == "name" || name == "producers" || |
145 | name == "target_features" || name.starts_with(Prefix: "reloc." )) |
146 | continue; |
147 | // These custom sections are generated by `clang -fembed-bitcode`. |
148 | // These are used by the rust toolchain to ship LTO data along with |
149 | // compiled object code, but they don't want this included in the linker |
150 | // output. |
151 | if (name == ".llvmbc" || name == ".llvmcmd" ) |
152 | continue; |
153 | // Strip debug section in that option was specified. |
154 | if (stripDebug && name.starts_with(Prefix: ".debug_" )) |
155 | continue; |
156 | // Otherwise include custom sections by default and concatenate their |
157 | // contents. |
158 | customSectionMapping[name].push_back(x: section); |
159 | } |
160 | } |
161 | } |
162 | |
163 | void Writer::createCustomSections() { |
164 | log(msg: "createCustomSections" ); |
165 | for (auto &pair : customSectionMapping) { |
166 | StringRef name = pair.first; |
167 | LLVM_DEBUG(dbgs() << "createCustomSection: " << name << "\n" ); |
168 | |
169 | OutputSection *sec = make<CustomSection>(args: std::string(name), args&: pair.second); |
170 | if (config->relocatable || config->emitRelocs) { |
171 | auto *sym = make<OutputSectionSymbol>(args&: sec); |
172 | out.linkingSec->addToSymtab(sym); |
173 | sec->sectionSym = sym; |
174 | } |
175 | addSection(sec); |
176 | } |
177 | } |
178 | |
179 | // Create relocations sections in the final output. |
180 | // These are only created when relocatable output is requested. |
181 | void Writer::createRelocSections() { |
182 | log(msg: "createRelocSections" ); |
183 | // Don't use iterator here since we are adding to OutputSection |
184 | size_t origSize = outputSections.size(); |
185 | for (size_t i = 0; i < origSize; i++) { |
186 | LLVM_DEBUG(dbgs() << "check section " << i << "\n" ); |
187 | OutputSection *sec = outputSections[i]; |
188 | |
189 | // Count the number of needed sections. |
190 | uint32_t count = sec->getNumRelocations(); |
191 | if (!count) |
192 | continue; |
193 | |
194 | StringRef name; |
195 | if (sec->type == WASM_SEC_DATA) |
196 | name = "reloc.DATA" ; |
197 | else if (sec->type == WASM_SEC_CODE) |
198 | name = "reloc.CODE" ; |
199 | else if (sec->type == WASM_SEC_CUSTOM) |
200 | name = saver().save(S: "reloc." + sec->name); |
201 | else |
202 | llvm_unreachable( |
203 | "relocations only supported for code, data, or custom sections" ); |
204 | |
205 | addSection(sec: make<RelocSection>(args&: name, args&: sec)); |
206 | } |
207 | } |
208 | |
209 | void Writer::populateProducers() { |
210 | for (ObjFile *file : ctx.objectFiles) { |
211 | const WasmProducerInfo &info = file->getWasmObj()->getProducerInfo(); |
212 | out.producersSec->addInfo(info); |
213 | } |
214 | } |
215 | |
216 | void Writer::() { |
217 | memcpy(dest: buffer->getBufferStart(), src: header.data(), n: header.size()); |
218 | } |
219 | |
220 | void Writer::writeSections() { |
221 | uint8_t *buf = buffer->getBufferStart(); |
222 | parallelForEach(R&: outputSections, Fn: [buf](OutputSection *s) { |
223 | assert(s->isNeeded()); |
224 | s->writeTo(buf); |
225 | }); |
226 | } |
227 | |
228 | // Computes a hash value of Data using a given hash function. |
229 | // In order to utilize multiple cores, we first split data into 1MB |
230 | // chunks, compute a hash for each chunk, and then compute a hash value |
231 | // of the hash values. |
232 | |
233 | static void |
234 | computeHash(llvm::MutableArrayRef<uint8_t> hashBuf, |
235 | llvm::ArrayRef<uint8_t> data, |
236 | std::function<void(uint8_t *dest, ArrayRef<uint8_t> arr)> hashFn) { |
237 | std::vector<ArrayRef<uint8_t>> chunks = split(arr: data, chunkSize: 1024 * 1024); |
238 | std::vector<uint8_t> hashes(chunks.size() * hashBuf.size()); |
239 | |
240 | // Compute hash values. |
241 | parallelFor(Begin: 0, End: chunks.size(), Fn: [&](size_t i) { |
242 | hashFn(hashes.data() + i * hashBuf.size(), chunks[i]); |
243 | }); |
244 | |
245 | // Write to the final output buffer. |
246 | hashFn(hashBuf.data(), hashes); |
247 | } |
248 | |
249 | static void makeUUID(unsigned version, llvm::ArrayRef<uint8_t> fileHash, |
250 | llvm::MutableArrayRef<uint8_t> output) { |
251 | assert((version == 4 || version == 5) && "Unknown UUID version" ); |
252 | assert(output.size() == 16 && "Wrong size for UUID output" ); |
253 | if (version == 5) { |
254 | // Build a valid v5 UUID from a hardcoded (randomly-generated) namespace |
255 | // UUID, and the computed hash of the output. |
256 | std::array<uint8_t, 16> namespaceUUID{0xA1, 0xFA, 0x48, 0x2D, 0x0E, 0x22, |
257 | 0x03, 0x8D, 0x33, 0x8B, 0x52, 0x1C, |
258 | 0xD6, 0xD2, 0x12, 0xB2}; |
259 | SHA1 sha; |
260 | sha.update(Data: namespaceUUID); |
261 | sha.update(Data: fileHash); |
262 | auto s = sha.final(); |
263 | std::copy(first: s.data(), last: &s.data()[output.size()], result: output.data()); |
264 | } else if (version == 4) { |
265 | if (auto ec = llvm::getRandomBytes(Buffer: output.data(), Size: output.size())) |
266 | error(msg: "entropy source failure: " + ec.message()); |
267 | } |
268 | // Set the UUID version and variant fields. |
269 | // The version is the upper nibble of byte 6 (0b0101xxxx or 0b0100xxxx) |
270 | output[6] = (static_cast<uint8_t>(version) << 4) | (output[6] & 0xF); |
271 | |
272 | // The variant is DCE 1.1/ISO 11578 (0b10xxxxxx) |
273 | output[8] &= 0xBF; |
274 | output[8] |= 0x80; |
275 | } |
276 | |
277 | void Writer::writeBuildId() { |
278 | if (!out.buildIdSec->isNeeded()) |
279 | return; |
280 | if (config->buildId == BuildIdKind::Hexstring) { |
281 | out.buildIdSec->writeBuildId(buf: config->buildIdVector); |
282 | return; |
283 | } |
284 | |
285 | // Compute a hash of all sections of the output file. |
286 | size_t hashSize = out.buildIdSec->hashSize; |
287 | std::vector<uint8_t> buildId(hashSize); |
288 | llvm::ArrayRef<uint8_t> buf{buffer->getBufferStart(), size_t(fileSize)}; |
289 | |
290 | switch (config->buildId) { |
291 | case BuildIdKind::Fast: { |
292 | std::vector<uint8_t> fileHash(8); |
293 | computeHash(hashBuf: fileHash, data: buf, hashFn: [](uint8_t *dest, ArrayRef<uint8_t> arr) { |
294 | support::endian::write64le(P: dest, V: xxh3_64bits(data: arr)); |
295 | }); |
296 | makeUUID(version: 5, fileHash, output: buildId); |
297 | break; |
298 | } |
299 | case BuildIdKind::Sha1: |
300 | computeHash(hashBuf: buildId, data: buf, hashFn: [&](uint8_t *dest, ArrayRef<uint8_t> arr) { |
301 | memcpy(dest: dest, src: SHA1::hash(Data: arr).data(), n: hashSize); |
302 | }); |
303 | break; |
304 | case BuildIdKind::Uuid: |
305 | makeUUID(version: 4, fileHash: {}, output: buildId); |
306 | break; |
307 | default: |
308 | llvm_unreachable("unknown BuildIdKind" ); |
309 | } |
310 | out.buildIdSec->writeBuildId(buf: buildId); |
311 | } |
312 | |
313 | static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) { |
314 | LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr << "\n" ); |
315 | g->global->setPointerValue(memoryPtr); |
316 | } |
317 | |
318 | // Fix the memory layout of the output binary. This assigns memory offsets |
319 | // to each of the input data sections as well as the explicit stack region. |
320 | // The default memory layout is as follows, from low to high. |
321 | // |
322 | // - initialized data (starting at config->globalBase) |
323 | // - BSS data (not currently implemented in llvm) |
324 | // - explicit stack (config->ZStackSize) |
325 | // - heap start / unallocated |
326 | // |
327 | // The --stack-first option means that stack is placed before any static data. |
328 | // This can be useful since it means that stack overflow traps immediately |
329 | // rather than overwriting global data, but also increases code size since all |
330 | // static data loads and stores requires larger offsets. |
331 | void Writer::layoutMemory() { |
332 | uint64_t memoryPtr = 0; |
333 | |
334 | auto placeStack = [&]() { |
335 | if (config->relocatable || ctx.isPic) |
336 | return; |
337 | memoryPtr = alignTo(Value: memoryPtr, Align: stackAlignment); |
338 | if (WasmSym::stackLow) |
339 | WasmSym::stackLow->setVA(memoryPtr); |
340 | if (config->zStackSize != alignTo(Value: config->zStackSize, Align: stackAlignment)) |
341 | error(msg: "stack size must be " + Twine(stackAlignment) + "-byte aligned" ); |
342 | log(msg: "mem: stack size = " + Twine(config->zStackSize)); |
343 | log(msg: "mem: stack base = " + Twine(memoryPtr)); |
344 | memoryPtr += config->zStackSize; |
345 | setGlobalPtr(g: cast<DefinedGlobal>(Val: WasmSym::stackPointer), memoryPtr); |
346 | if (WasmSym::stackHigh) |
347 | WasmSym::stackHigh->setVA(memoryPtr); |
348 | log(msg: "mem: stack top = " + Twine(memoryPtr)); |
349 | }; |
350 | |
351 | if (config->stackFirst) { |
352 | placeStack(); |
353 | if (config->globalBase) { |
354 | if (config->globalBase < memoryPtr) { |
355 | error(msg: "--global-base cannot be less than stack size when --stack-first is used" ); |
356 | return; |
357 | } |
358 | memoryPtr = config->globalBase; |
359 | } |
360 | } else { |
361 | memoryPtr = config->globalBase; |
362 | } |
363 | |
364 | log(msg: "mem: global base = " + Twine(memoryPtr)); |
365 | if (WasmSym::globalBase) |
366 | WasmSym::globalBase->setVA(memoryPtr); |
367 | |
368 | uint64_t dataStart = memoryPtr; |
369 | |
370 | // Arbitrarily set __dso_handle handle to point to the start of the data |
371 | // segments. |
372 | if (WasmSym::dsoHandle) |
373 | WasmSym::dsoHandle->setVA(dataStart); |
374 | |
375 | out.dylinkSec->memAlign = 0; |
376 | for (OutputSegment *seg : segments) { |
377 | out.dylinkSec->memAlign = std::max(a: out.dylinkSec->memAlign, b: seg->alignment); |
378 | memoryPtr = alignTo(Value: memoryPtr, Align: 1ULL << seg->alignment); |
379 | seg->startVA = memoryPtr; |
380 | log(msg: formatv(Fmt: "mem: {0,-15} offset={1,-8} size={2,-8} align={3}" , Vals&: seg->name, |
381 | Vals&: memoryPtr, Vals&: seg->size, Vals&: seg->alignment)); |
382 | |
383 | if (!config->relocatable && seg->isTLS()) { |
384 | if (WasmSym::tlsSize) { |
385 | auto *tlsSize = cast<DefinedGlobal>(Val: WasmSym::tlsSize); |
386 | setGlobalPtr(g: tlsSize, memoryPtr: seg->size); |
387 | } |
388 | if (WasmSym::tlsAlign) { |
389 | auto *tlsAlign = cast<DefinedGlobal>(Val: WasmSym::tlsAlign); |
390 | setGlobalPtr(g: tlsAlign, memoryPtr: int64_t{1} << seg->alignment); |
391 | } |
392 | if (!config->sharedMemory && WasmSym::tlsBase) { |
393 | auto *tlsBase = cast<DefinedGlobal>(Val: WasmSym::tlsBase); |
394 | setGlobalPtr(g: tlsBase, memoryPtr); |
395 | } |
396 | } |
397 | |
398 | memoryPtr += seg->size; |
399 | } |
400 | |
401 | // Make space for the memory initialization flag |
402 | if (config->sharedMemory && hasPassiveInitializedSegments()) { |
403 | memoryPtr = alignTo(Value: memoryPtr, Align: 4); |
404 | WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol( |
405 | name: "__wasm_init_memory_flag" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN); |
406 | WasmSym::initMemoryFlag->markLive(); |
407 | WasmSym::initMemoryFlag->setVA(memoryPtr); |
408 | log(msg: formatv(Fmt: "mem: {0,-15} offset={1,-8} size={2,-8} align={3}" , |
409 | Vals: "__wasm_init_memory_flag" , Vals&: memoryPtr, Vals: 4, Vals: 4)); |
410 | memoryPtr += 4; |
411 | } |
412 | |
413 | if (WasmSym::dataEnd) |
414 | WasmSym::dataEnd->setVA(memoryPtr); |
415 | |
416 | uint64_t staticDataSize = memoryPtr - dataStart; |
417 | log(msg: "mem: static data = " + Twine(staticDataSize)); |
418 | if (ctx.isPic) |
419 | out.dylinkSec->memSize = staticDataSize; |
420 | |
421 | if (!config->stackFirst) |
422 | placeStack(); |
423 | |
424 | if (WasmSym::heapBase) { |
425 | // Set `__heap_base` to follow the end of the stack or global data. The |
426 | // fact that this comes last means that a malloc/brk implementation can |
427 | // grow the heap at runtime. |
428 | // We'll align the heap base here because memory allocators might expect |
429 | // __heap_base to be aligned already. |
430 | memoryPtr = alignTo(Value: memoryPtr, Align: heapAlignment); |
431 | log(msg: "mem: heap base = " + Twine(memoryPtr)); |
432 | WasmSym::heapBase->setVA(memoryPtr); |
433 | } |
434 | |
435 | uint64_t maxMemorySetting = 1ULL << 32; |
436 | if (config->is64.value_or(u: false)) { |
437 | // TODO: Update once we decide on a reasonable limit here: |
438 | // https://github.com/WebAssembly/memory64/issues/33 |
439 | maxMemorySetting = 1ULL << 34; |
440 | } |
441 | |
442 | if (config->initialHeap != 0) { |
443 | if (config->initialHeap != alignTo(Value: config->initialHeap, Align: WasmPageSize)) |
444 | error(msg: "initial heap must be " + Twine(WasmPageSize) + "-byte aligned" ); |
445 | uint64_t maxInitialHeap = maxMemorySetting - memoryPtr; |
446 | if (config->initialHeap > maxInitialHeap) |
447 | error(msg: "initial heap too large, cannot be greater than " + |
448 | Twine(maxInitialHeap)); |
449 | memoryPtr += config->initialHeap; |
450 | } |
451 | |
452 | if (config->initialMemory != 0) { |
453 | if (config->initialMemory != alignTo(Value: config->initialMemory, Align: WasmPageSize)) |
454 | error(msg: "initial memory must be " + Twine(WasmPageSize) + "-byte aligned" ); |
455 | if (memoryPtr > config->initialMemory) |
456 | error(msg: "initial memory too small, " + Twine(memoryPtr) + " bytes needed" ); |
457 | if (config->initialMemory > maxMemorySetting) |
458 | error(msg: "initial memory too large, cannot be greater than " + |
459 | Twine(maxMemorySetting)); |
460 | memoryPtr = config->initialMemory; |
461 | } |
462 | |
463 | memoryPtr = alignTo(Value: memoryPtr, Align: WasmPageSize); |
464 | |
465 | out.memorySec->numMemoryPages = memoryPtr / WasmPageSize; |
466 | log(msg: "mem: total pages = " + Twine(out.memorySec->numMemoryPages)); |
467 | |
468 | if (WasmSym::heapEnd) { |
469 | // Set `__heap_end` to follow the end of the statically allocated linear |
470 | // memory. The fact that this comes last means that a malloc/brk |
471 | // implementation can grow the heap at runtime. |
472 | log(msg: "mem: heap end = " + Twine(memoryPtr)); |
473 | WasmSym::heapEnd->setVA(memoryPtr); |
474 | } |
475 | |
476 | uint64_t maxMemory = 0; |
477 | if (config->maxMemory != 0) { |
478 | if (config->maxMemory != alignTo(Value: config->maxMemory, Align: WasmPageSize)) |
479 | error(msg: "maximum memory must be " + Twine(WasmPageSize) + "-byte aligned" ); |
480 | if (memoryPtr > config->maxMemory) |
481 | error(msg: "maximum memory too small, " + Twine(memoryPtr) + " bytes needed" ); |
482 | if (config->maxMemory > maxMemorySetting) |
483 | error(msg: "maximum memory too large, cannot be greater than " + |
484 | Twine(maxMemorySetting)); |
485 | |
486 | maxMemory = config->maxMemory; |
487 | } else if (config->noGrowableMemory) { |
488 | maxMemory = memoryPtr; |
489 | } |
490 | |
491 | // If no maxMemory config was supplied but we are building with |
492 | // shared memory, we need to pick a sensible upper limit. |
493 | if (config->sharedMemory && maxMemory == 0) { |
494 | if (ctx.isPic) |
495 | maxMemory = maxMemorySetting; |
496 | else |
497 | maxMemory = memoryPtr; |
498 | } |
499 | |
500 | if (maxMemory != 0) { |
501 | out.memorySec->maxMemoryPages = maxMemory / WasmPageSize; |
502 | log(msg: "mem: max pages = " + Twine(out.memorySec->maxMemoryPages)); |
503 | } |
504 | } |
505 | |
506 | void Writer::addSection(OutputSection *sec) { |
507 | if (!sec->isNeeded()) |
508 | return; |
509 | log(msg: "addSection: " + toString(section: *sec)); |
510 | sec->sectionIndex = outputSections.size(); |
511 | outputSections.push_back(x: sec); |
512 | } |
513 | |
514 | // If a section name is valid as a C identifier (which is rare because of |
515 | // the leading '.'), linkers are expected to define __start_<secname> and |
516 | // __stop_<secname> symbols. They are at beginning and end of the section, |
517 | // respectively. This is not requested by the ELF standard, but GNU ld and |
518 | // gold provide the feature, and used by many programs. |
519 | static void addStartStopSymbols(const OutputSegment *seg) { |
520 | StringRef name = seg->name; |
521 | if (!isValidCIdentifier(s: name)) |
522 | return; |
523 | LLVM_DEBUG(dbgs() << "addStartStopSymbols: " << name << "\n" ); |
524 | uint64_t start = seg->startVA; |
525 | uint64_t stop = start + seg->size; |
526 | symtab->addOptionalDataSymbol(name: saver().save(S: "__start_" + name), value: start); |
527 | symtab->addOptionalDataSymbol(name: saver().save(S: "__stop_" + name), value: stop); |
528 | } |
529 | |
530 | void Writer::addSections() { |
531 | addSection(sec: out.dylinkSec); |
532 | addSection(sec: out.typeSec); |
533 | addSection(sec: out.importSec); |
534 | addSection(sec: out.functionSec); |
535 | addSection(sec: out.tableSec); |
536 | addSection(sec: out.memorySec); |
537 | addSection(sec: out.tagSec); |
538 | addSection(sec: out.globalSec); |
539 | addSection(sec: out.exportSec); |
540 | addSection(sec: out.startSec); |
541 | addSection(sec: out.elemSec); |
542 | addSection(sec: out.dataCountSec); |
543 | |
544 | addSection(sec: make<CodeSection>(args&: out.functionSec->inputFunctions)); |
545 | addSection(sec: make<DataSection>(args&: segments)); |
546 | |
547 | createCustomSections(); |
548 | |
549 | addSection(sec: out.linkingSec); |
550 | if (config->emitRelocs || config->relocatable) { |
551 | createRelocSections(); |
552 | } |
553 | |
554 | addSection(sec: out.nameSec); |
555 | addSection(sec: out.producersSec); |
556 | addSection(sec: out.targetFeaturesSec); |
557 | addSection(sec: out.buildIdSec); |
558 | } |
559 | |
560 | void Writer::finalizeSections() { |
561 | for (OutputSection *s : outputSections) { |
562 | s->setOffset(fileSize); |
563 | s->finalizeContents(); |
564 | fileSize += s->getSize(); |
565 | } |
566 | } |
567 | |
568 | void Writer::populateTargetFeatures() { |
569 | StringMap<std::string> used; |
570 | StringMap<std::string> required; |
571 | StringMap<std::string> disallowed; |
572 | SmallSet<std::string, 8> &allowed = out.targetFeaturesSec->features; |
573 | bool tlsUsed = false; |
574 | |
575 | if (ctx.isPic) { |
576 | // This should not be necessary because all PIC objects should |
577 | // contain the mutable-globals feature. |
578 | // TODO (https://github.com/llvm/llvm-project/issues/51681) |
579 | allowed.insert(V: "mutable-globals" ); |
580 | } |
581 | |
582 | if (config->extraFeatures.has_value()) { |
583 | auto & = *config->extraFeatures; |
584 | allowed.insert(I: extraFeatures.begin(), E: extraFeatures.end()); |
585 | } |
586 | |
587 | // Only infer used features if user did not specify features |
588 | bool inferFeatures = !config->features.has_value(); |
589 | |
590 | if (!inferFeatures) { |
591 | auto &explicitFeatures = *config->features; |
592 | allowed.insert(I: explicitFeatures.begin(), E: explicitFeatures.end()); |
593 | if (!config->checkFeatures) |
594 | goto done; |
595 | } |
596 | |
597 | // Find the sets of used, required, and disallowed features |
598 | for (ObjFile *file : ctx.objectFiles) { |
599 | StringRef fileName(file->getName()); |
600 | for (auto &feature : file->getWasmObj()->getTargetFeatures()) { |
601 | switch (feature.Prefix) { |
602 | case WASM_FEATURE_PREFIX_USED: |
603 | used.insert(KV: {feature.Name, std::string(fileName)}); |
604 | break; |
605 | case WASM_FEATURE_PREFIX_REQUIRED: |
606 | used.insert(KV: {feature.Name, std::string(fileName)}); |
607 | required.insert(KV: {feature.Name, std::string(fileName)}); |
608 | break; |
609 | case WASM_FEATURE_PREFIX_DISALLOWED: |
610 | disallowed.insert(KV: {feature.Name, std::string(fileName)}); |
611 | break; |
612 | default: |
613 | error(msg: "Unrecognized feature policy prefix " + |
614 | std::to_string(val: feature.Prefix)); |
615 | } |
616 | } |
617 | |
618 | // Find TLS data segments |
619 | auto isTLS = [](InputChunk *segment) { |
620 | return segment->live && segment->isTLS(); |
621 | }; |
622 | tlsUsed = tlsUsed || llvm::any_of(Range&: file->segments, P: isTLS); |
623 | } |
624 | |
625 | if (inferFeatures) |
626 | for (const auto &key : used.keys()) |
627 | allowed.insert(V: std::string(key)); |
628 | |
629 | if (!config->checkFeatures) |
630 | goto done; |
631 | |
632 | if (config->sharedMemory) { |
633 | if (disallowed.count(Key: "shared-mem" )) |
634 | error(msg: "--shared-memory is disallowed by " + disallowed["shared-mem" ] + |
635 | " because it was not compiled with 'atomics' or 'bulk-memory' " |
636 | "features." ); |
637 | |
638 | for (auto feature : {"atomics" , "bulk-memory" }) |
639 | if (!allowed.count(V: feature)) |
640 | error(msg: StringRef("'" ) + feature + |
641 | "' feature must be used in order to use shared memory" ); |
642 | } |
643 | |
644 | if (tlsUsed) { |
645 | for (auto feature : {"atomics" , "bulk-memory" }) |
646 | if (!allowed.count(V: feature)) |
647 | error(msg: StringRef("'" ) + feature + |
648 | "' feature must be used in order to use thread-local storage" ); |
649 | } |
650 | |
651 | // Validate that used features are allowed in output |
652 | if (!inferFeatures) { |
653 | for (const auto &feature : used.keys()) { |
654 | if (!allowed.count(V: std::string(feature))) |
655 | error(msg: Twine("Target feature '" ) + feature + "' used by " + |
656 | used[feature] + " is not allowed." ); |
657 | } |
658 | } |
659 | |
660 | // Validate the required and disallowed constraints for each file |
661 | for (ObjFile *file : ctx.objectFiles) { |
662 | StringRef fileName(file->getName()); |
663 | SmallSet<std::string, 8> objectFeatures; |
664 | for (const auto &feature : file->getWasmObj()->getTargetFeatures()) { |
665 | if (feature.Prefix == WASM_FEATURE_PREFIX_DISALLOWED) |
666 | continue; |
667 | objectFeatures.insert(V: feature.Name); |
668 | if (disallowed.count(Key: feature.Name)) |
669 | error(msg: Twine("Target feature '" ) + feature.Name + "' used in " + |
670 | fileName + " is disallowed by " + disallowed[feature.Name] + |
671 | ". Use --no-check-features to suppress." ); |
672 | } |
673 | for (const auto &feature : required.keys()) { |
674 | if (!objectFeatures.count(V: std::string(feature))) |
675 | error(msg: Twine("Missing target feature '" ) + feature + "' in " + fileName + |
676 | ", required by " + required[feature] + |
677 | ". Use --no-check-features to suppress." ); |
678 | } |
679 | } |
680 | |
681 | done: |
682 | // Normally we don't include bss segments in the binary. In particular if |
683 | // memory is not being imported then we can assume its zero initialized. |
684 | // In the case the memory is imported, and we can use the memory.fill |
685 | // instruction, then we can also avoid including the segments. |
686 | // Finally, if we are emitting relocations, they may refer to locations within |
687 | // the bss segments, so these segments need to exist in the binary. |
688 | if (config->emitRelocs || |
689 | (config->memoryImport.has_value() && !allowed.count(V: "bulk-memory" ))) |
690 | ctx.emitBssSegments = true; |
691 | |
692 | if (allowed.count(V: "extended-const" )) |
693 | config->extendedConst = true; |
694 | |
695 | for (auto &feature : allowed) |
696 | log(msg: "Allowed feature: " + feature); |
697 | } |
698 | |
699 | void Writer::checkImportExportTargetFeatures() { |
700 | if (config->relocatable || !config->checkFeatures) |
701 | return; |
702 | |
703 | if (out.targetFeaturesSec->features.count(V: "mutable-globals" ) == 0) { |
704 | for (const Symbol *sym : out.importSec->importedSymbols) { |
705 | if (auto *global = dyn_cast<GlobalSymbol>(Val: sym)) { |
706 | if (global->getGlobalType()->Mutable) { |
707 | error(msg: Twine("mutable global imported but 'mutable-globals' feature " |
708 | "not present in inputs: `" ) + |
709 | toString(sym: *sym) + "`. Use --no-check-features to suppress." ); |
710 | } |
711 | } |
712 | } |
713 | for (const Symbol *sym : out.exportSec->exportedSymbols) { |
714 | if (isa<GlobalSymbol>(Val: sym)) { |
715 | error(msg: Twine("mutable global exported but 'mutable-globals' feature " |
716 | "not present in inputs: `" ) + |
717 | toString(sym: *sym) + "`. Use --no-check-features to suppress." ); |
718 | } |
719 | } |
720 | } |
721 | } |
722 | |
723 | static bool shouldImport(Symbol *sym) { |
724 | // We don't generate imports for data symbols. They however can be imported |
725 | // as GOT entries. |
726 | if (isa<DataSymbol>(Val: sym)) |
727 | return false; |
728 | if (!sym->isLive()) |
729 | return false; |
730 | if (!sym->isUsedInRegularObj) |
731 | return false; |
732 | |
733 | // When a symbol is weakly defined in a shared library we need to allow |
734 | // it to be overridden by another module so need to both import |
735 | // and export the symbol. |
736 | if (config->shared && sym->isWeak() && !sym->isUndefined() && |
737 | !sym->isHidden()) |
738 | return true; |
739 | if (!sym->isUndefined()) |
740 | return false; |
741 | if (sym->isWeak() && !config->relocatable && !ctx.isPic) |
742 | return false; |
743 | |
744 | // In PIC mode we only need to import functions when they are called directly. |
745 | // Indirect usage all goes via GOT imports. |
746 | if (ctx.isPic) { |
747 | if (auto *f = dyn_cast<UndefinedFunction>(Val: sym)) |
748 | if (!f->isCalledDirectly) |
749 | return false; |
750 | } |
751 | |
752 | if (ctx.isPic || config->relocatable || config->importUndefined || |
753 | config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) |
754 | return true; |
755 | if (config->allowUndefinedSymbols.count(Key: sym->getName()) != 0) |
756 | return true; |
757 | |
758 | return sym->isImported(); |
759 | } |
760 | |
761 | void Writer::calculateImports() { |
762 | // Some inputs require that the indirect function table be assigned to table |
763 | // number 0, so if it is present and is an import, allocate it before any |
764 | // other tables. |
765 | if (WasmSym::indirectFunctionTable && |
766 | shouldImport(sym: WasmSym::indirectFunctionTable)) |
767 | out.importSec->addImport(sym: WasmSym::indirectFunctionTable); |
768 | |
769 | for (Symbol *sym : symtab->symbols()) { |
770 | if (!shouldImport(sym)) |
771 | continue; |
772 | if (sym == WasmSym::indirectFunctionTable) |
773 | continue; |
774 | LLVM_DEBUG(dbgs() << "import: " << sym->getName() << "\n" ); |
775 | out.importSec->addImport(sym); |
776 | } |
777 | } |
778 | |
779 | void Writer::calculateExports() { |
780 | if (config->relocatable) |
781 | return; |
782 | |
783 | if (!config->relocatable && config->memoryExport.has_value()) { |
784 | out.exportSec->exports.push_back( |
785 | x: WasmExport{.Name: *config->memoryExport, .Kind: WASM_EXTERNAL_MEMORY, .Index: 0}); |
786 | } |
787 | |
788 | unsigned globalIndex = |
789 | out.importSec->getNumImportedGlobals() + out.globalSec->numGlobals(); |
790 | |
791 | for (Symbol *sym : symtab->symbols()) { |
792 | if (!sym->isExported()) |
793 | continue; |
794 | if (!sym->isLive()) |
795 | continue; |
796 | |
797 | StringRef name = sym->getName(); |
798 | WasmExport export_; |
799 | if (auto *f = dyn_cast<DefinedFunction>(Val: sym)) { |
800 | if (std::optional<StringRef> exportName = f->function->getExportName()) { |
801 | name = *exportName; |
802 | } |
803 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_FUNCTION, .Index: f->getExportedFunctionIndex()}; |
804 | } else if (auto *g = dyn_cast<DefinedGlobal>(Val: sym)) { |
805 | if (g->getGlobalType()->Mutable && !g->getFile() && !g->forceExport) { |
806 | // Avoid exporting mutable globals are linker synthesized (e.g. |
807 | // __stack_pointer or __tls_base) unless they are explicitly exported |
808 | // from the command line. |
809 | // Without this check `--export-all` would cause any program using the |
810 | // stack pointer to export a mutable global even if none of the input |
811 | // files were built with the `mutable-globals` feature. |
812 | continue; |
813 | } |
814 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_GLOBAL, .Index: g->getGlobalIndex()}; |
815 | } else if (auto *t = dyn_cast<DefinedTag>(Val: sym)) { |
816 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_TAG, .Index: t->getTagIndex()}; |
817 | } else if (auto *d = dyn_cast<DefinedData>(Val: sym)) { |
818 | out.globalSec->dataAddressGlobals.push_back(x: d); |
819 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_GLOBAL, .Index: globalIndex++}; |
820 | } else { |
821 | auto *t = cast<DefinedTable>(Val: sym); |
822 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_TABLE, .Index: t->getTableNumber()}; |
823 | } |
824 | |
825 | LLVM_DEBUG(dbgs() << "Export: " << name << "\n" ); |
826 | out.exportSec->exports.push_back(x: export_); |
827 | out.exportSec->exportedSymbols.push_back(x: sym); |
828 | } |
829 | } |
830 | |
831 | void Writer::populateSymtab() { |
832 | if (!config->relocatable && !config->emitRelocs) |
833 | return; |
834 | |
835 | for (Symbol *sym : symtab->symbols()) |
836 | if (sym->isUsedInRegularObj && sym->isLive()) |
837 | out.linkingSec->addToSymtab(sym); |
838 | |
839 | for (ObjFile *file : ctx.objectFiles) { |
840 | LLVM_DEBUG(dbgs() << "Local symtab entries: " << file->getName() << "\n" ); |
841 | for (Symbol *sym : file->getSymbols()) |
842 | if (sym->isLocal() && !isa<SectionSymbol>(Val: sym) && sym->isLive()) |
843 | out.linkingSec->addToSymtab(sym); |
844 | } |
845 | } |
846 | |
847 | void Writer::calculateTypes() { |
848 | // The output type section is the union of the following sets: |
849 | // 1. Any signature used in the TYPE relocation |
850 | // 2. The signatures of all imported functions |
851 | // 3. The signatures of all defined functions |
852 | // 4. The signatures of all imported tags |
853 | // 5. The signatures of all defined tags |
854 | |
855 | for (ObjFile *file : ctx.objectFiles) { |
856 | ArrayRef<WasmSignature> types = file->getWasmObj()->types(); |
857 | for (uint32_t i = 0; i < types.size(); i++) |
858 | if (file->typeIsUsed[i]) |
859 | file->typeMap[i] = out.typeSec->registerType(sig: types[i]); |
860 | } |
861 | |
862 | for (const Symbol *sym : out.importSec->importedSymbols) { |
863 | if (auto *f = dyn_cast<FunctionSymbol>(Val: sym)) |
864 | out.typeSec->registerType(sig: *f->signature); |
865 | else if (auto *t = dyn_cast<TagSymbol>(Val: sym)) |
866 | out.typeSec->registerType(sig: *t->signature); |
867 | } |
868 | |
869 | for (const InputFunction *f : out.functionSec->inputFunctions) |
870 | out.typeSec->registerType(sig: f->signature); |
871 | |
872 | for (const InputTag *t : out.tagSec->inputTags) |
873 | out.typeSec->registerType(sig: t->signature); |
874 | } |
875 | |
876 | // In a command-style link, create a wrapper for each exported symbol |
877 | // which calls the constructors and destructors. |
878 | void Writer::createCommandExportWrappers() { |
879 | // This logic doesn't currently support Emscripten-style PIC mode. |
880 | assert(!ctx.isPic); |
881 | |
882 | // If there are no ctors and there's no libc `__wasm_call_dtors` to |
883 | // call, don't wrap the exports. |
884 | if (initFunctions.empty() && WasmSym::callDtors == nullptr) |
885 | return; |
886 | |
887 | std::vector<DefinedFunction *> toWrap; |
888 | |
889 | for (Symbol *sym : symtab->symbols()) |
890 | if (sym->isExported()) |
891 | if (auto *f = dyn_cast<DefinedFunction>(Val: sym)) |
892 | toWrap.push_back(x: f); |
893 | |
894 | for (auto *f : toWrap) { |
895 | auto funcNameStr = (f->getName() + ".command_export" ).str(); |
896 | commandExportWrapperNames.push_back(x: funcNameStr); |
897 | const std::string &funcName = commandExportWrapperNames.back(); |
898 | |
899 | auto func = make<SyntheticFunction>(args: *f->getSignature(), args: funcName); |
900 | if (f->function->getExportName()) |
901 | func->setExportName(f->function->getExportName()->str()); |
902 | else |
903 | func->setExportName(f->getName().str()); |
904 | |
905 | DefinedFunction *def = |
906 | symtab->addSyntheticFunction(name: funcName, flags: f->flags, function: func); |
907 | def->markLive(); |
908 | |
909 | def->flags |= WASM_SYMBOL_EXPORTED; |
910 | def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN; |
911 | def->forceExport = f->forceExport; |
912 | |
913 | f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN; |
914 | f->flags &= ~WASM_SYMBOL_EXPORTED; |
915 | f->forceExport = false; |
916 | |
917 | out.functionSec->addFunction(func); |
918 | |
919 | createCommandExportWrapper(functionIndex: f->getFunctionIndex(), f: def); |
920 | } |
921 | } |
922 | |
923 | static void finalizeIndirectFunctionTable() { |
924 | if (!WasmSym::indirectFunctionTable) |
925 | return; |
926 | |
927 | if (shouldImport(sym: WasmSym::indirectFunctionTable) && |
928 | !WasmSym::indirectFunctionTable->hasTableNumber()) { |
929 | // Processing -Bsymbolic relocations resulted in a late requirement that the |
930 | // indirect function table be present, and we are running in --import-table |
931 | // mode. Add the table now to the imports section. Otherwise it will be |
932 | // added to the tables section later in assignIndexes. |
933 | out.importSec->addImport(sym: WasmSym::indirectFunctionTable); |
934 | } |
935 | |
936 | uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); |
937 | WasmLimits limits = {.Flags: 0, .Minimum: tableSize, .Maximum: 0}; |
938 | if (WasmSym::indirectFunctionTable->isDefined() && !config->growableTable) { |
939 | limits.Flags |= WASM_LIMITS_FLAG_HAS_MAX; |
940 | limits.Maximum = limits.Minimum; |
941 | } |
942 | WasmSym::indirectFunctionTable->setLimits(limits); |
943 | } |
944 | |
945 | static void scanRelocations() { |
946 | for (ObjFile *file : ctx.objectFiles) { |
947 | LLVM_DEBUG(dbgs() << "scanRelocations: " << file->getName() << "\n" ); |
948 | for (InputChunk *chunk : file->functions) |
949 | scanRelocations(chunk); |
950 | for (InputChunk *chunk : file->segments) |
951 | scanRelocations(chunk); |
952 | for (auto &p : file->customSections) |
953 | scanRelocations(chunk: p); |
954 | } |
955 | } |
956 | |
957 | void Writer::assignIndexes() { |
958 | // Seal the import section, since other index spaces such as function and |
959 | // global are effected by the number of imports. |
960 | out.importSec->seal(); |
961 | |
962 | for (InputFunction *func : ctx.syntheticFunctions) |
963 | out.functionSec->addFunction(func); |
964 | |
965 | for (ObjFile *file : ctx.objectFiles) { |
966 | LLVM_DEBUG(dbgs() << "Functions: " << file->getName() << "\n" ); |
967 | for (InputFunction *func : file->functions) |
968 | out.functionSec->addFunction(func); |
969 | } |
970 | |
971 | for (InputGlobal *global : ctx.syntheticGlobals) |
972 | out.globalSec->addGlobal(global); |
973 | |
974 | for (ObjFile *file : ctx.objectFiles) { |
975 | LLVM_DEBUG(dbgs() << "Globals: " << file->getName() << "\n" ); |
976 | for (InputGlobal *global : file->globals) |
977 | out.globalSec->addGlobal(global); |
978 | } |
979 | |
980 | for (ObjFile *file : ctx.objectFiles) { |
981 | LLVM_DEBUG(dbgs() << "Tags: " << file->getName() << "\n" ); |
982 | for (InputTag *tag : file->tags) |
983 | out.tagSec->addTag(tag); |
984 | } |
985 | |
986 | for (ObjFile *file : ctx.objectFiles) { |
987 | LLVM_DEBUG(dbgs() << "Tables: " << file->getName() << "\n" ); |
988 | for (InputTable *table : file->tables) |
989 | out.tableSec->addTable(table); |
990 | } |
991 | |
992 | for (InputTable *table : ctx.syntheticTables) |
993 | out.tableSec->addTable(table); |
994 | |
995 | out.globalSec->assignIndexes(); |
996 | out.tableSec->assignIndexes(); |
997 | } |
998 | |
999 | static StringRef getOutputDataSegmentName(const InputChunk &seg) { |
1000 | // We always merge .tbss and .tdata into a single TLS segment so all TLS |
1001 | // symbols are be relative to single __tls_base. |
1002 | if (seg.isTLS()) |
1003 | return ".tdata" ; |
1004 | if (!config->mergeDataSegments) |
1005 | return seg.name; |
1006 | if (seg.name.starts_with(Prefix: ".text." )) |
1007 | return ".text" ; |
1008 | if (seg.name.starts_with(Prefix: ".data." )) |
1009 | return ".data" ; |
1010 | if (seg.name.starts_with(Prefix: ".bss." )) |
1011 | return ".bss" ; |
1012 | if (seg.name.starts_with(Prefix: ".rodata." )) |
1013 | return ".rodata" ; |
1014 | return seg.name; |
1015 | } |
1016 | |
1017 | OutputSegment *Writer::createOutputSegment(StringRef name) { |
1018 | LLVM_DEBUG(dbgs() << "new segment: " << name << "\n" ); |
1019 | OutputSegment *s = make<OutputSegment>(args&: name); |
1020 | if (config->sharedMemory) |
1021 | s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE; |
1022 | if (!config->relocatable && name.starts_with(Prefix: ".bss" )) |
1023 | s->isBss = true; |
1024 | segments.push_back(x: s); |
1025 | return s; |
1026 | } |
1027 | |
1028 | void Writer::createOutputSegments() { |
1029 | for (ObjFile *file : ctx.objectFiles) { |
1030 | for (InputChunk *segment : file->segments) { |
1031 | if (!segment->live) |
1032 | continue; |
1033 | StringRef name = getOutputDataSegmentName(seg: *segment); |
1034 | OutputSegment *s = nullptr; |
1035 | // When running in relocatable mode we can't merge segments that are part |
1036 | // of comdat groups since the ultimate linker needs to be able exclude or |
1037 | // include them individually. |
1038 | if (config->relocatable && !segment->getComdatName().empty()) { |
1039 | s = createOutputSegment(name); |
1040 | } else { |
1041 | if (segmentMap.count(Val: name) == 0) |
1042 | segmentMap[name] = createOutputSegment(name); |
1043 | s = segmentMap[name]; |
1044 | } |
1045 | s->addInputSegment(inSeg: segment); |
1046 | } |
1047 | } |
1048 | |
1049 | // Sort segments by type, placing .bss last |
1050 | std::stable_sort(first: segments.begin(), last: segments.end(), |
1051 | comp: [](const OutputSegment *a, const OutputSegment *b) { |
1052 | auto order = [](StringRef name) { |
1053 | return StringSwitch<int>(name) |
1054 | .StartsWith(S: ".tdata" , Value: 0) |
1055 | .StartsWith(S: ".rodata" , Value: 1) |
1056 | .StartsWith(S: ".data" , Value: 2) |
1057 | .StartsWith(S: ".bss" , Value: 4) |
1058 | .Default(Value: 3); |
1059 | }; |
1060 | return order(a->name) < order(b->name); |
1061 | }); |
1062 | |
1063 | for (size_t i = 0; i < segments.size(); ++i) |
1064 | segments[i]->index = i; |
1065 | |
1066 | // Merge MergeInputSections into a single MergeSyntheticSection. |
1067 | LLVM_DEBUG(dbgs() << "-- finalize input semgments\n" ); |
1068 | for (OutputSegment *seg : segments) |
1069 | seg->finalizeInputSegments(); |
1070 | } |
1071 | |
1072 | void Writer::combineOutputSegments() { |
1073 | // With PIC code we currently only support a single active data segment since |
1074 | // we only have a single __memory_base to use as our base address. This pass |
1075 | // combines all data segments into a single .data segment. |
1076 | // This restriction does not apply when the extended const extension is |
1077 | // available: https://github.com/WebAssembly/extended-const |
1078 | assert(!config->extendedConst); |
1079 | assert(ctx.isPic && !config->sharedMemory); |
1080 | if (segments.size() <= 1) |
1081 | return; |
1082 | OutputSegment *combined = make<OutputSegment>(args: ".data" ); |
1083 | combined->startVA = segments[0]->startVA; |
1084 | for (OutputSegment *s : segments) { |
1085 | bool first = true; |
1086 | for (InputChunk *inSeg : s->inputSegments) { |
1087 | if (first) |
1088 | inSeg->alignment = std::max(a: inSeg->alignment, b: s->alignment); |
1089 | first = false; |
1090 | #ifndef NDEBUG |
1091 | uint64_t oldVA = inSeg->getVA(); |
1092 | #endif |
1093 | combined->addInputSegment(inSeg); |
1094 | #ifndef NDEBUG |
1095 | uint64_t newVA = inSeg->getVA(); |
1096 | LLVM_DEBUG(dbgs() << "added input segment. name=" << inSeg->name |
1097 | << " oldVA=" << oldVA << " newVA=" << newVA << "\n" ); |
1098 | assert(oldVA == newVA); |
1099 | #endif |
1100 | } |
1101 | } |
1102 | |
1103 | segments = {combined}; |
1104 | } |
1105 | |
1106 | static void createFunction(DefinedFunction *func, StringRef bodyContent) { |
1107 | std::string functionBody; |
1108 | { |
1109 | raw_string_ostream os(functionBody); |
1110 | writeUleb128(os, number: bodyContent.size(), msg: "function size" ); |
1111 | os << bodyContent; |
1112 | } |
1113 | ArrayRef<uint8_t> body = arrayRefFromStringRef(Input: saver().save(S: functionBody)); |
1114 | cast<SyntheticFunction>(Val: func->function)->setBody(body); |
1115 | } |
1116 | |
1117 | bool Writer::needsPassiveInitialization(const OutputSegment *segment) { |
1118 | // If bulk memory features is supported then we can perform bss initialization |
1119 | // (via memory.fill) during `__wasm_init_memory`. |
1120 | if (config->memoryImport.has_value() && !segment->requiredInBinary()) |
1121 | return true; |
1122 | return segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE; |
1123 | } |
1124 | |
1125 | bool Writer::hasPassiveInitializedSegments() { |
1126 | return llvm::any_of(Range&: segments, P: [this](const OutputSegment *s) { |
1127 | return this->needsPassiveInitialization(segment: s); |
1128 | }); |
1129 | } |
1130 | |
1131 | void Writer::createSyntheticInitFunctions() { |
1132 | if (config->relocatable) |
1133 | return; |
1134 | |
1135 | static WasmSignature nullSignature = {{}, {}}; |
1136 | |
1137 | // Passive segments are used to avoid memory being reinitialized on each |
1138 | // thread's instantiation. These passive segments are initialized and |
1139 | // dropped in __wasm_init_memory, which is registered as the start function |
1140 | // We also initialize bss segments (using memory.fill) as part of this |
1141 | // function. |
1142 | if (hasPassiveInitializedSegments()) { |
1143 | WasmSym::initMemory = symtab->addSyntheticFunction( |
1144 | name: "__wasm_init_memory" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1145 | function: make<SyntheticFunction>(args&: nullSignature, args: "__wasm_init_memory" )); |
1146 | WasmSym::initMemory->markLive(); |
1147 | if (config->sharedMemory) { |
1148 | // This global is assigned during __wasm_init_memory in the shared memory |
1149 | // case. |
1150 | WasmSym::tlsBase->markLive(); |
1151 | } |
1152 | } |
1153 | |
1154 | if (config->sharedMemory) { |
1155 | if (out.globalSec->needsTLSRelocations()) { |
1156 | WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction( |
1157 | name: "__wasm_apply_global_tls_relocs" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1158 | function: make<SyntheticFunction>(args&: nullSignature, |
1159 | args: "__wasm_apply_global_tls_relocs" )); |
1160 | WasmSym::applyGlobalTLSRelocs->markLive(); |
1161 | // TLS relocations depend on the __tls_base symbols |
1162 | WasmSym::tlsBase->markLive(); |
1163 | } |
1164 | |
1165 | auto hasTLSRelocs = [](const OutputSegment *segment) { |
1166 | if (segment->isTLS()) |
1167 | for (const auto* is: segment->inputSegments) |
1168 | if (is->getRelocations().size()) |
1169 | return true; |
1170 | return false; |
1171 | }; |
1172 | if (llvm::any_of(Range&: segments, P: hasTLSRelocs)) { |
1173 | WasmSym::applyTLSRelocs = symtab->addSyntheticFunction( |
1174 | name: "__wasm_apply_tls_relocs" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1175 | function: make<SyntheticFunction>(args&: nullSignature, |
1176 | args: "__wasm_apply_tls_relocs" )); |
1177 | WasmSym::applyTLSRelocs->markLive(); |
1178 | } |
1179 | } |
1180 | |
1181 | if (ctx.isPic && out.globalSec->needsRelocations()) { |
1182 | WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction( |
1183 | name: "__wasm_apply_global_relocs" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1184 | function: make<SyntheticFunction>(args&: nullSignature, args: "__wasm_apply_global_relocs" )); |
1185 | WasmSym::applyGlobalRelocs->markLive(); |
1186 | } |
1187 | |
1188 | // If there is only one start function we can just use that function |
1189 | // itself as the Wasm start function, otherwise we need to synthesize |
1190 | // a new function to call them in sequence. |
1191 | if (WasmSym::applyGlobalRelocs && WasmSym::initMemory) { |
1192 | WasmSym::startFunction = symtab->addSyntheticFunction( |
1193 | name: "__wasm_start" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1194 | function: make<SyntheticFunction>(args&: nullSignature, args: "__wasm_start" )); |
1195 | WasmSym::startFunction->markLive(); |
1196 | } |
1197 | } |
1198 | |
1199 | void Writer::createInitMemoryFunction() { |
1200 | LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n" ); |
1201 | assert(WasmSym::initMemory); |
1202 | assert(hasPassiveInitializedSegments()); |
1203 | uint64_t flagAddress; |
1204 | if (config->sharedMemory) { |
1205 | assert(WasmSym::initMemoryFlag); |
1206 | flagAddress = WasmSym::initMemoryFlag->getVA(); |
1207 | } |
1208 | bool is64 = config->is64.value_or(u: false); |
1209 | std::string bodyContent; |
1210 | { |
1211 | raw_string_ostream os(bodyContent); |
1212 | // Initialize memory in a thread-safe manner. The thread that successfully |
1213 | // increments the flag from 0 to 1 is responsible for performing the memory |
1214 | // initialization. Other threads go sleep on the flag until the first thread |
1215 | // finishing initializing memory, increments the flag to 2, and wakes all |
1216 | // the other threads. Once the flag has been set to 2, subsequently started |
1217 | // threads will skip the sleep. All threads unconditionally drop their |
1218 | // passive data segments once memory has been initialized. The generated |
1219 | // code is as follows: |
1220 | // |
1221 | // (func $__wasm_init_memory |
1222 | // (block $drop |
1223 | // (block $wait |
1224 | // (block $init |
1225 | // (br_table $init $wait $drop |
1226 | // (i32.atomic.rmw.cmpxchg align=2 offset=0 |
1227 | // (i32.const $__init_memory_flag) |
1228 | // (i32.const 0) |
1229 | // (i32.const 1) |
1230 | // ) |
1231 | // ) |
1232 | // ) ;; $init |
1233 | // ( ... initialize data segments ... ) |
1234 | // (i32.atomic.store align=2 offset=0 |
1235 | // (i32.const $__init_memory_flag) |
1236 | // (i32.const 2) |
1237 | // ) |
1238 | // (drop |
1239 | // (i32.atomic.notify align=2 offset=0 |
1240 | // (i32.const $__init_memory_flag) |
1241 | // (i32.const -1u) |
1242 | // ) |
1243 | // ) |
1244 | // (br $drop) |
1245 | // ) ;; $wait |
1246 | // (drop |
1247 | // (i32.atomic.wait align=2 offset=0 |
1248 | // (i32.const $__init_memory_flag) |
1249 | // (i32.const 1) |
1250 | // (i32.const -1) |
1251 | // ) |
1252 | // ) |
1253 | // ) ;; $drop |
1254 | // ( ... drop data segments ... ) |
1255 | // ) |
1256 | // |
1257 | // When we are building with PIC, calculate the flag location using: |
1258 | // |
1259 | // (global.get $__memory_base) |
1260 | // (i32.const $__init_memory_flag) |
1261 | // (i32.const 1) |
1262 | |
1263 | auto writeGetFlagAddress = [&]() { |
1264 | if (ctx.isPic) { |
1265 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.get" ); |
1266 | writeUleb128(os, number: 0, msg: "local 0" ); |
1267 | } else { |
1268 | writePtrConst(os, number: flagAddress, is64, msg: "flag address" ); |
1269 | } |
1270 | }; |
1271 | |
1272 | if (config->sharedMemory) { |
1273 | // With PIC code we cache the flag address in local 0 |
1274 | if (ctx.isPic) { |
1275 | writeUleb128(os, number: 1, msg: "num local decls" ); |
1276 | writeUleb128(os, number: 2, msg: "local count" ); |
1277 | writeU8(os, byte: is64 ? WASM_TYPE_I64 : WASM_TYPE_I32, msg: "address type" ); |
1278 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET" ); |
1279 | writeUleb128(os, number: WasmSym::memoryBase->getGlobalIndex(), msg: "memory_base" ); |
1280 | writePtrConst(os, number: flagAddress, is64, msg: "flag address" ); |
1281 | writeU8(os, byte: is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, msg: "add" ); |
1282 | writeU8(os, byte: WASM_OPCODE_LOCAL_SET, msg: "local.set" ); |
1283 | writeUleb128(os, number: 0, msg: "local 0" ); |
1284 | } else { |
1285 | writeUleb128(os, number: 0, msg: "num locals" ); |
1286 | } |
1287 | |
1288 | // Set up destination blocks |
1289 | writeU8(os, byte: WASM_OPCODE_BLOCK, msg: "block $drop" ); |
1290 | writeU8(os, byte: WASM_TYPE_NORESULT, msg: "block type" ); |
1291 | writeU8(os, byte: WASM_OPCODE_BLOCK, msg: "block $wait" ); |
1292 | writeU8(os, byte: WASM_TYPE_NORESULT, msg: "block type" ); |
1293 | writeU8(os, byte: WASM_OPCODE_BLOCK, msg: "block $init" ); |
1294 | writeU8(os, byte: WASM_TYPE_NORESULT, msg: "block type" ); |
1295 | |
1296 | // Atomically check whether we win the race. |
1297 | writeGetFlagAddress(); |
1298 | writeI32Const(os, number: 0, msg: "expected flag value" ); |
1299 | writeI32Const(os, number: 1, msg: "new flag value" ); |
1300 | writeU8(os, byte: WASM_OPCODE_ATOMICS_PREFIX, msg: "atomics prefix" ); |
1301 | writeUleb128(os, number: WASM_OPCODE_I32_RMW_CMPXCHG, msg: "i32.atomic.rmw.cmpxchg" ); |
1302 | writeMemArg(os, alignment: 2, offset: 0); |
1303 | |
1304 | // Based on the value, decide what to do next. |
1305 | writeU8(os, byte: WASM_OPCODE_BR_TABLE, msg: "br_table" ); |
1306 | writeUleb128(os, number: 2, msg: "label vector length" ); |
1307 | writeUleb128(os, number: 0, msg: "label $init" ); |
1308 | writeUleb128(os, number: 1, msg: "label $wait" ); |
1309 | writeUleb128(os, number: 2, msg: "default label $drop" ); |
1310 | |
1311 | // Initialize passive data segments |
1312 | writeU8(os, byte: WASM_OPCODE_END, msg: "end $init" ); |
1313 | } else { |
1314 | writeUleb128(os, number: 0, msg: "num local decls" ); |
1315 | } |
1316 | |
1317 | for (const OutputSegment *s : segments) { |
1318 | if (needsPassiveInitialization(segment: s)) { |
1319 | // For passive BSS segments we can simple issue a memory.fill(0). |
1320 | // For non-BSS segments we do a memory.init. Both these |
1321 | // instructions take as their first argument the destination |
1322 | // address. |
1323 | writePtrConst(os, number: s->startVA, is64, msg: "destination address" ); |
1324 | if (ctx.isPic) { |
1325 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET" ); |
1326 | writeUleb128(os, number: WasmSym::memoryBase->getGlobalIndex(), |
1327 | msg: "__memory_base" ); |
1328 | writeU8(os, byte: is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, |
1329 | msg: "i32.add" ); |
1330 | } |
1331 | |
1332 | // When we initialize the TLS segment we also set the `__tls_base` |
1333 | // global. This allows the runtime to use this static copy of the |
1334 | // TLS data for the first/main thread. |
1335 | if (config->sharedMemory && s->isTLS()) { |
1336 | if (ctx.isPic) { |
1337 | // Cache the result of the addionion in local 0 |
1338 | writeU8(os, byte: WASM_OPCODE_LOCAL_TEE, msg: "local.tee" ); |
1339 | writeUleb128(os, number: 1, msg: "local 1" ); |
1340 | } else { |
1341 | writePtrConst(os, number: s->startVA, is64, msg: "destination address" ); |
1342 | } |
1343 | writeU8(os, byte: WASM_OPCODE_GLOBAL_SET, msg: "GLOBAL_SET" ); |
1344 | writeUleb128(os, number: WasmSym::tlsBase->getGlobalIndex(), |
1345 | msg: "__tls_base" ); |
1346 | if (ctx.isPic) { |
1347 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.tee" ); |
1348 | writeUleb128(os, number: 1, msg: "local 1" ); |
1349 | } |
1350 | } |
1351 | |
1352 | if (s->isBss) { |
1353 | writeI32Const(os, number: 0, msg: "fill value" ); |
1354 | writePtrConst(os, number: s->size, is64, msg: "memory region size" ); |
1355 | writeU8(os, byte: WASM_OPCODE_MISC_PREFIX, msg: "bulk-memory prefix" ); |
1356 | writeUleb128(os, number: WASM_OPCODE_MEMORY_FILL, msg: "memory.fill" ); |
1357 | writeU8(os, byte: 0, msg: "memory index immediate" ); |
1358 | } else { |
1359 | writeI32Const(os, number: 0, msg: "source segment offset" ); |
1360 | writeI32Const(os, number: s->size, msg: "memory region size" ); |
1361 | writeU8(os, byte: WASM_OPCODE_MISC_PREFIX, msg: "bulk-memory prefix" ); |
1362 | writeUleb128(os, number: WASM_OPCODE_MEMORY_INIT, msg: "memory.init" ); |
1363 | writeUleb128(os, number: s->index, msg: "segment index immediate" ); |
1364 | writeU8(os, byte: 0, msg: "memory index immediate" ); |
1365 | } |
1366 | } |
1367 | } |
1368 | |
1369 | if (config->sharedMemory) { |
1370 | // Set flag to 2 to mark end of initialization |
1371 | writeGetFlagAddress(); |
1372 | writeI32Const(os, number: 2, msg: "flag value" ); |
1373 | writeU8(os, byte: WASM_OPCODE_ATOMICS_PREFIX, msg: "atomics prefix" ); |
1374 | writeUleb128(os, number: WASM_OPCODE_I32_ATOMIC_STORE, msg: "i32.atomic.store" ); |
1375 | writeMemArg(os, alignment: 2, offset: 0); |
1376 | |
1377 | // Notify any waiters that memory initialization is complete |
1378 | writeGetFlagAddress(); |
1379 | writeI32Const(os, number: -1, msg: "number of waiters" ); |
1380 | writeU8(os, byte: WASM_OPCODE_ATOMICS_PREFIX, msg: "atomics prefix" ); |
1381 | writeUleb128(os, number: WASM_OPCODE_ATOMIC_NOTIFY, msg: "atomic.notify" ); |
1382 | writeMemArg(os, alignment: 2, offset: 0); |
1383 | writeU8(os, byte: WASM_OPCODE_DROP, msg: "drop" ); |
1384 | |
1385 | // Branch to drop the segments |
1386 | writeU8(os, byte: WASM_OPCODE_BR, msg: "br" ); |
1387 | writeUleb128(os, number: 1, msg: "label $drop" ); |
1388 | |
1389 | // Wait for the winning thread to initialize memory |
1390 | writeU8(os, byte: WASM_OPCODE_END, msg: "end $wait" ); |
1391 | writeGetFlagAddress(); |
1392 | writeI32Const(os, number: 1, msg: "expected flag value" ); |
1393 | writeI64Const(os, number: -1, msg: "timeout" ); |
1394 | |
1395 | writeU8(os, byte: WASM_OPCODE_ATOMICS_PREFIX, msg: "atomics prefix" ); |
1396 | writeUleb128(os, number: WASM_OPCODE_I32_ATOMIC_WAIT, msg: "i32.atomic.wait" ); |
1397 | writeMemArg(os, alignment: 2, offset: 0); |
1398 | writeU8(os, byte: WASM_OPCODE_DROP, msg: "drop" ); |
1399 | |
1400 | // Unconditionally drop passive data segments |
1401 | writeU8(os, byte: WASM_OPCODE_END, msg: "end $drop" ); |
1402 | } |
1403 | |
1404 | for (const OutputSegment *s : segments) { |
1405 | if (needsPassiveInitialization(segment: s) && !s->isBss) { |
1406 | // The TLS region should not be dropped since its is needed |
1407 | // during the initialization of each thread (__wasm_init_tls). |
1408 | if (config->sharedMemory && s->isTLS()) |
1409 | continue; |
1410 | // data.drop instruction |
1411 | writeU8(os, byte: WASM_OPCODE_MISC_PREFIX, msg: "bulk-memory prefix" ); |
1412 | writeUleb128(os, number: WASM_OPCODE_DATA_DROP, msg: "data.drop" ); |
1413 | writeUleb128(os, number: s->index, msg: "segment index immediate" ); |
1414 | } |
1415 | } |
1416 | |
1417 | // End the function |
1418 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1419 | } |
1420 | |
1421 | createFunction(func: WasmSym::initMemory, bodyContent); |
1422 | } |
1423 | |
1424 | void Writer::createStartFunction() { |
1425 | // If the start function exists when we have more than one function to call. |
1426 | if (WasmSym::initMemory && WasmSym::applyGlobalRelocs) { |
1427 | assert(WasmSym::startFunction); |
1428 | std::string bodyContent; |
1429 | { |
1430 | raw_string_ostream os(bodyContent); |
1431 | writeUleb128(os, number: 0, msg: "num locals" ); |
1432 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1433 | writeUleb128(os, number: WasmSym::applyGlobalRelocs->getFunctionIndex(), |
1434 | msg: "function index" ); |
1435 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1436 | writeUleb128(os, number: WasmSym::initMemory->getFunctionIndex(), |
1437 | msg: "function index" ); |
1438 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1439 | } |
1440 | createFunction(func: WasmSym::startFunction, bodyContent); |
1441 | } else if (WasmSym::initMemory) { |
1442 | WasmSym::startFunction = WasmSym::initMemory; |
1443 | } else if (WasmSym::applyGlobalRelocs) { |
1444 | WasmSym::startFunction = WasmSym::applyGlobalRelocs; |
1445 | } |
1446 | } |
1447 | |
1448 | // For -shared (PIC) output, we create create a synthetic function which will |
1449 | // apply any relocations to the data segments on startup. This function is |
1450 | // called `__wasm_apply_data_relocs` and is expected to be called before |
1451 | // any user code (i.e. before `__wasm_call_ctors`). |
1452 | void Writer::createApplyDataRelocationsFunction() { |
1453 | LLVM_DEBUG(dbgs() << "createApplyDataRelocationsFunction\n" ); |
1454 | // First write the body's contents to a string. |
1455 | std::string bodyContent; |
1456 | { |
1457 | raw_string_ostream os(bodyContent); |
1458 | writeUleb128(os, number: 0, msg: "num locals" ); |
1459 | for (const OutputSegment *seg : segments) |
1460 | if (!config->sharedMemory || !seg->isTLS()) |
1461 | for (const InputChunk *inSeg : seg->inputSegments) |
1462 | inSeg->generateRelocationCode(os); |
1463 | |
1464 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1465 | } |
1466 | |
1467 | createFunction(func: WasmSym::applyDataRelocs, bodyContent); |
1468 | } |
1469 | |
1470 | void Writer::createApplyTLSRelocationsFunction() { |
1471 | LLVM_DEBUG(dbgs() << "createApplyTLSRelocationsFunction\n" ); |
1472 | std::string bodyContent; |
1473 | { |
1474 | raw_string_ostream os(bodyContent); |
1475 | writeUleb128(os, number: 0, msg: "num locals" ); |
1476 | for (const OutputSegment *seg : segments) |
1477 | if (seg->isTLS()) |
1478 | for (const InputChunk *inSeg : seg->inputSegments) |
1479 | inSeg->generateRelocationCode(os); |
1480 | |
1481 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1482 | } |
1483 | |
1484 | createFunction(func: WasmSym::applyTLSRelocs, bodyContent); |
1485 | } |
1486 | |
1487 | // Similar to createApplyDataRelocationsFunction but generates relocation code |
1488 | // for WebAssembly globals. Because these globals are not shared between threads |
1489 | // these relocation need to run on every thread. |
1490 | void Writer::createApplyGlobalRelocationsFunction() { |
1491 | // First write the body's contents to a string. |
1492 | std::string bodyContent; |
1493 | { |
1494 | raw_string_ostream os(bodyContent); |
1495 | writeUleb128(os, number: 0, msg: "num locals" ); |
1496 | out.globalSec->generateRelocationCode(os, TLS: false); |
1497 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1498 | } |
1499 | |
1500 | createFunction(func: WasmSym::applyGlobalRelocs, bodyContent); |
1501 | } |
1502 | |
1503 | // Similar to createApplyGlobalRelocationsFunction but for |
1504 | // TLS symbols. This cannot be run during the start function |
1505 | // but must be delayed until __wasm_init_tls is called. |
1506 | void Writer::createApplyGlobalTLSRelocationsFunction() { |
1507 | // First write the body's contents to a string. |
1508 | std::string bodyContent; |
1509 | { |
1510 | raw_string_ostream os(bodyContent); |
1511 | writeUleb128(os, number: 0, msg: "num locals" ); |
1512 | out.globalSec->generateRelocationCode(os, TLS: true); |
1513 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1514 | } |
1515 | |
1516 | createFunction(func: WasmSym::applyGlobalTLSRelocs, bodyContent); |
1517 | } |
1518 | |
1519 | // Create synthetic "__wasm_call_ctors" function based on ctor functions |
1520 | // in input object. |
1521 | void Writer::createCallCtorsFunction() { |
1522 | // If __wasm_call_ctors isn't referenced, there aren't any ctors, don't |
1523 | // define the `__wasm_call_ctors` function. |
1524 | if (!WasmSym::callCtors->isLive() && initFunctions.empty()) |
1525 | return; |
1526 | |
1527 | // First write the body's contents to a string. |
1528 | std::string bodyContent; |
1529 | { |
1530 | raw_string_ostream os(bodyContent); |
1531 | writeUleb128(os, number: 0, msg: "num locals" ); |
1532 | |
1533 | // Call constructors |
1534 | for (const WasmInitEntry &f : initFunctions) { |
1535 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1536 | writeUleb128(os, number: f.sym->getFunctionIndex(), msg: "function index" ); |
1537 | for (size_t i = 0; i < f.sym->signature->Returns.size(); i++) { |
1538 | writeU8(os, byte: WASM_OPCODE_DROP, msg: "DROP" ); |
1539 | } |
1540 | } |
1541 | |
1542 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1543 | } |
1544 | |
1545 | createFunction(func: WasmSym::callCtors, bodyContent); |
1546 | } |
1547 | |
1548 | // Create a wrapper around a function export which calls the |
1549 | // static constructors and destructors. |
1550 | void Writer::createCommandExportWrapper(uint32_t functionIndex, |
1551 | DefinedFunction *f) { |
1552 | // First write the body's contents to a string. |
1553 | std::string bodyContent; |
1554 | { |
1555 | raw_string_ostream os(bodyContent); |
1556 | writeUleb128(os, number: 0, msg: "num locals" ); |
1557 | |
1558 | // Call `__wasm_call_ctors` which call static constructors (and |
1559 | // applies any runtime relocations in Emscripten-style PIC mode) |
1560 | if (WasmSym::callCtors->isLive()) { |
1561 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1562 | writeUleb128(os, number: WasmSym::callCtors->getFunctionIndex(), |
1563 | msg: "function index" ); |
1564 | } |
1565 | |
1566 | // Call the user's code, leaving any return values on the operand stack. |
1567 | for (size_t i = 0; i < f->signature->Params.size(); ++i) { |
1568 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.get" ); |
1569 | writeUleb128(os, number: i, msg: "local index" ); |
1570 | } |
1571 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1572 | writeUleb128(os, number: functionIndex, msg: "function index" ); |
1573 | |
1574 | // Call the function that calls the destructors. |
1575 | if (DefinedFunction *callDtors = WasmSym::callDtors) { |
1576 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1577 | writeUleb128(os, number: callDtors->getFunctionIndex(), msg: "function index" ); |
1578 | } |
1579 | |
1580 | // End the function, returning the return values from the user's code. |
1581 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1582 | } |
1583 | |
1584 | createFunction(func: f, bodyContent); |
1585 | } |
1586 | |
1587 | void Writer::createInitTLSFunction() { |
1588 | std::string bodyContent; |
1589 | { |
1590 | raw_string_ostream os(bodyContent); |
1591 | |
1592 | OutputSegment *tlsSeg = nullptr; |
1593 | for (auto *seg : segments) { |
1594 | if (seg->name == ".tdata" ) { |
1595 | tlsSeg = seg; |
1596 | break; |
1597 | } |
1598 | } |
1599 | |
1600 | writeUleb128(os, number: 0, msg: "num locals" ); |
1601 | if (tlsSeg) { |
1602 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.get" ); |
1603 | writeUleb128(os, number: 0, msg: "local index" ); |
1604 | |
1605 | writeU8(os, byte: WASM_OPCODE_GLOBAL_SET, msg: "global.set" ); |
1606 | writeUleb128(os, number: WasmSym::tlsBase->getGlobalIndex(), msg: "global index" ); |
1607 | |
1608 | // FIXME(wvo): this local needs to be I64 in wasm64, or we need an extend op. |
1609 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.get" ); |
1610 | writeUleb128(os, number: 0, msg: "local index" ); |
1611 | |
1612 | writeI32Const(os, number: 0, msg: "segment offset" ); |
1613 | |
1614 | writeI32Const(os, number: tlsSeg->size, msg: "memory region size" ); |
1615 | |
1616 | writeU8(os, byte: WASM_OPCODE_MISC_PREFIX, msg: "bulk-memory prefix" ); |
1617 | writeUleb128(os, number: WASM_OPCODE_MEMORY_INIT, msg: "MEMORY.INIT" ); |
1618 | writeUleb128(os, number: tlsSeg->index, msg: "segment index immediate" ); |
1619 | writeU8(os, byte: 0, msg: "memory index immediate" ); |
1620 | } |
1621 | |
1622 | if (WasmSym::applyTLSRelocs) { |
1623 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1624 | writeUleb128(os, number: WasmSym::applyTLSRelocs->getFunctionIndex(), |
1625 | msg: "function index" ); |
1626 | } |
1627 | |
1628 | if (WasmSym::applyGlobalTLSRelocs) { |
1629 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1630 | writeUleb128(os, number: WasmSym::applyGlobalTLSRelocs->getFunctionIndex(), |
1631 | msg: "function index" ); |
1632 | } |
1633 | writeU8(os, byte: WASM_OPCODE_END, msg: "end function" ); |
1634 | } |
1635 | |
1636 | createFunction(func: WasmSym::initTLS, bodyContent); |
1637 | } |
1638 | |
1639 | // Populate InitFunctions vector with init functions from all input objects. |
1640 | // This is then used either when creating the output linking section or to |
1641 | // synthesize the "__wasm_call_ctors" function. |
1642 | void Writer::calculateInitFunctions() { |
1643 | if (!config->relocatable && !WasmSym::callCtors->isLive()) |
1644 | return; |
1645 | |
1646 | for (ObjFile *file : ctx.objectFiles) { |
1647 | const WasmLinkingData &l = file->getWasmObj()->linkingData(); |
1648 | for (const WasmInitFunc &f : l.InitFunctions) { |
1649 | FunctionSymbol *sym = file->getFunctionSymbol(index: f.Symbol); |
1650 | // comdat exclusions can cause init functions be discarded. |
1651 | if (sym->isDiscarded() || !sym->isLive()) |
1652 | continue; |
1653 | if (sym->signature->Params.size() != 0) |
1654 | error(msg: "constructor functions cannot take arguments: " + toString(sym: *sym)); |
1655 | LLVM_DEBUG(dbgs() << "initFunctions: " << toString(*sym) << "\n" ); |
1656 | initFunctions.emplace_back(args: WasmInitEntry{.sym: sym, .priority: f.Priority}); |
1657 | } |
1658 | } |
1659 | |
1660 | // Sort in order of priority (lowest first) so that they are called |
1661 | // in the correct order. |
1662 | llvm::stable_sort(Range&: initFunctions, |
1663 | C: [](const WasmInitEntry &l, const WasmInitEntry &r) { |
1664 | return l.priority < r.priority; |
1665 | }); |
1666 | } |
1667 | |
1668 | void Writer::createSyntheticSections() { |
1669 | out.dylinkSec = make<DylinkSection>(); |
1670 | out.typeSec = make<TypeSection>(); |
1671 | out.importSec = make<ImportSection>(); |
1672 | out.functionSec = make<FunctionSection>(); |
1673 | out.tableSec = make<TableSection>(); |
1674 | out.memorySec = make<MemorySection>(); |
1675 | out.tagSec = make<TagSection>(); |
1676 | out.globalSec = make<GlobalSection>(); |
1677 | out.exportSec = make<ExportSection>(); |
1678 | out.startSec = make<StartSection>(); |
1679 | out.elemSec = make<ElemSection>(); |
1680 | out.producersSec = make<ProducersSection>(); |
1681 | out.targetFeaturesSec = make<TargetFeaturesSection>(); |
1682 | out.buildIdSec = make<BuildIdSection>(); |
1683 | } |
1684 | |
1685 | void Writer::createSyntheticSectionsPostLayout() { |
1686 | out.dataCountSec = make<DataCountSection>(args&: segments); |
1687 | out.linkingSec = make<LinkingSection>(args&: initFunctions, args&: segments); |
1688 | out.nameSec = make<NameSection>(args&: segments); |
1689 | } |
1690 | |
1691 | void Writer::run() { |
1692 | // For PIC code the table base is assigned dynamically by the loader. |
1693 | // For non-PIC, we start at 1 so that accessing table index 0 always traps. |
1694 | if (!ctx.isPic) { |
1695 | if (WasmSym::definedTableBase) |
1696 | WasmSym::definedTableBase->setVA(config->tableBase); |
1697 | if (WasmSym::definedTableBase32) |
1698 | WasmSym::definedTableBase32->setVA(config->tableBase); |
1699 | } |
1700 | |
1701 | log(msg: "-- createOutputSegments" ); |
1702 | createOutputSegments(); |
1703 | log(msg: "-- createSyntheticSections" ); |
1704 | createSyntheticSections(); |
1705 | log(msg: "-- layoutMemory" ); |
1706 | layoutMemory(); |
1707 | |
1708 | if (!config->relocatable) { |
1709 | // Create linker synthesized __start_SECNAME/__stop_SECNAME symbols |
1710 | // This has to be done after memory layout is performed. |
1711 | for (const OutputSegment *seg : segments) { |
1712 | addStartStopSymbols(seg); |
1713 | } |
1714 | } |
1715 | |
1716 | for (auto &pair : config->exportedSymbols) { |
1717 | Symbol *sym = symtab->find(name: pair.first()); |
1718 | if (sym && sym->isDefined()) |
1719 | sym->forceExport = true; |
1720 | } |
1721 | |
1722 | // Delay reporting errors about explicit exports until after |
1723 | // addStartStopSymbols which can create optional symbols. |
1724 | for (auto &name : config->requiredExports) { |
1725 | Symbol *sym = symtab->find(name); |
1726 | if (!sym || !sym->isDefined()) { |
1727 | if (config->unresolvedSymbols == UnresolvedPolicy::ReportError) |
1728 | error(msg: Twine("symbol exported via --export not found: " ) + name); |
1729 | if (config->unresolvedSymbols == UnresolvedPolicy::Warn) |
1730 | warn(msg: Twine("symbol exported via --export not found: " ) + name); |
1731 | } |
1732 | } |
1733 | |
1734 | log(msg: "-- populateTargetFeatures" ); |
1735 | populateTargetFeatures(); |
1736 | |
1737 | // When outputting PIC code each segment lives at at fixes offset from the |
1738 | // `__memory_base` import. Unless we support the extended const expression we |
1739 | // can't do addition inside the constant expression, so we much combine the |
1740 | // segments into a single one that can live at `__memory_base`. |
1741 | if (ctx.isPic && !config->extendedConst && !config->sharedMemory) { |
1742 | // In shared memory mode all data segments are passive and initialized |
1743 | // via __wasm_init_memory. |
1744 | log(msg: "-- combineOutputSegments" ); |
1745 | combineOutputSegments(); |
1746 | } |
1747 | |
1748 | log(msg: "-- createSyntheticSectionsPostLayout" ); |
1749 | createSyntheticSectionsPostLayout(); |
1750 | log(msg: "-- populateProducers" ); |
1751 | populateProducers(); |
1752 | log(msg: "-- calculateImports" ); |
1753 | calculateImports(); |
1754 | log(msg: "-- scanRelocations" ); |
1755 | scanRelocations(); |
1756 | log(msg: "-- finalizeIndirectFunctionTable" ); |
1757 | finalizeIndirectFunctionTable(); |
1758 | log(msg: "-- createSyntheticInitFunctions" ); |
1759 | createSyntheticInitFunctions(); |
1760 | log(msg: "-- assignIndexes" ); |
1761 | assignIndexes(); |
1762 | log(msg: "-- calculateInitFunctions" ); |
1763 | calculateInitFunctions(); |
1764 | |
1765 | if (!config->relocatable) { |
1766 | // Create linker synthesized functions |
1767 | if (WasmSym::applyDataRelocs) |
1768 | createApplyDataRelocationsFunction(); |
1769 | if (WasmSym::applyGlobalRelocs) |
1770 | createApplyGlobalRelocationsFunction(); |
1771 | if (WasmSym::applyTLSRelocs) |
1772 | createApplyTLSRelocationsFunction(); |
1773 | if (WasmSym::applyGlobalTLSRelocs) |
1774 | createApplyGlobalTLSRelocationsFunction(); |
1775 | if (WasmSym::initMemory) |
1776 | createInitMemoryFunction(); |
1777 | createStartFunction(); |
1778 | |
1779 | createCallCtorsFunction(); |
1780 | |
1781 | // Create export wrappers for commands if needed. |
1782 | // |
1783 | // If the input contains a call to `__wasm_call_ctors`, either in one of |
1784 | // the input objects or an explicit export from the command-line, we |
1785 | // assume ctors and dtors are taken care of already. |
1786 | if (!config->relocatable && !ctx.isPic && |
1787 | !WasmSym::callCtors->isUsedInRegularObj && |
1788 | !WasmSym::callCtors->isExported()) { |
1789 | log(msg: "-- createCommandExportWrappers" ); |
1790 | createCommandExportWrappers(); |
1791 | } |
1792 | } |
1793 | |
1794 | if (WasmSym::initTLS && WasmSym::initTLS->isLive()) { |
1795 | log(msg: "-- createInitTLSFunction" ); |
1796 | createInitTLSFunction(); |
1797 | } |
1798 | |
1799 | if (errorCount()) |
1800 | return; |
1801 | |
1802 | log(msg: "-- calculateTypes" ); |
1803 | calculateTypes(); |
1804 | log(msg: "-- calculateExports" ); |
1805 | calculateExports(); |
1806 | log(msg: "-- calculateCustomSections" ); |
1807 | calculateCustomSections(); |
1808 | log(msg: "-- populateSymtab" ); |
1809 | populateSymtab(); |
1810 | log(msg: "-- checkImportExportTargetFeatures" ); |
1811 | checkImportExportTargetFeatures(); |
1812 | log(msg: "-- addSections" ); |
1813 | addSections(); |
1814 | |
1815 | if (errorHandler().verbose) { |
1816 | log(msg: "Defined Functions: " + Twine(out.functionSec->inputFunctions.size())); |
1817 | log(msg: "Defined Globals : " + Twine(out.globalSec->numGlobals())); |
1818 | log(msg: "Defined Tags : " + Twine(out.tagSec->inputTags.size())); |
1819 | log(msg: "Defined Tables : " + Twine(out.tableSec->inputTables.size())); |
1820 | log(msg: "Function Imports : " + |
1821 | Twine(out.importSec->getNumImportedFunctions())); |
1822 | log(msg: "Global Imports : " + Twine(out.importSec->getNumImportedGlobals())); |
1823 | log(msg: "Tag Imports : " + Twine(out.importSec->getNumImportedTags())); |
1824 | log(msg: "Table Imports : " + Twine(out.importSec->getNumImportedTables())); |
1825 | } |
1826 | |
1827 | createHeader(); |
1828 | log(msg: "-- finalizeSections" ); |
1829 | finalizeSections(); |
1830 | |
1831 | log(msg: "-- writeMapFile" ); |
1832 | writeMapFile(outputSections); |
1833 | |
1834 | log(msg: "-- openFile" ); |
1835 | openFile(); |
1836 | if (errorCount()) |
1837 | return; |
1838 | |
1839 | writeHeader(); |
1840 | |
1841 | log(msg: "-- writeSections" ); |
1842 | writeSections(); |
1843 | writeBuildId(); |
1844 | if (errorCount()) |
1845 | return; |
1846 | |
1847 | if (Error e = buffer->commit()) |
1848 | fatal(msg: "failed to write output '" + buffer->getPath() + |
1849 | "': " + toString(E: std::move(e))); |
1850 | } |
1851 | |
1852 | // Open a result file. |
1853 | void Writer::openFile() { |
1854 | log(msg: "writing: " + config->outputFile); |
1855 | |
1856 | Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr = |
1857 | FileOutputBuffer::create(FilePath: config->outputFile, Size: fileSize, |
1858 | Flags: FileOutputBuffer::F_executable); |
1859 | |
1860 | if (!bufferOrErr) |
1861 | error(msg: "failed to open " + config->outputFile + ": " + |
1862 | toString(E: bufferOrErr.takeError())); |
1863 | else |
1864 | buffer = std::move(*bufferOrErr); |
1865 | } |
1866 | |
1867 | void Writer::() { |
1868 | raw_string_ostream os(header); |
1869 | writeBytes(os, bytes: WasmMagic, count: sizeof(WasmMagic), msg: "wasm magic" ); |
1870 | writeU32(os, number: WasmVersion, msg: "wasm version" ); |
1871 | os.flush(); |
1872 | fileSize += header.size(); |
1873 | } |
1874 | |
1875 | void writeResult() { Writer().run(); } |
1876 | |
1877 | } // namespace wasm::lld |
1878 | |