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(s.data(), &s.data()[output.size()], 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->isShared()) |
740 | return true; |
741 | if (!sym->isUndefined()) |
742 | return false; |
743 | if (sym->isWeak() && !config->relocatable && !ctx.isPic) |
744 | return false; |
745 | |
746 | // In PIC mode we only need to import functions when they are called directly. |
747 | // Indirect usage all goes via GOT imports. |
748 | if (ctx.isPic) { |
749 | if (auto *f = dyn_cast<UndefinedFunction>(Val: sym)) |
750 | if (!f->isCalledDirectly) |
751 | return false; |
752 | } |
753 | |
754 | if (ctx.isPic || config->relocatable || config->importUndefined || |
755 | config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) |
756 | return true; |
757 | if (config->allowUndefinedSymbols.count(Key: sym->getName()) != 0) |
758 | return true; |
759 | |
760 | return sym->isImported(); |
761 | } |
762 | |
763 | void Writer::calculateImports() { |
764 | // Some inputs require that the indirect function table be assigned to table |
765 | // number 0, so if it is present and is an import, allocate it before any |
766 | // other tables. |
767 | if (WasmSym::indirectFunctionTable && |
768 | shouldImport(sym: WasmSym::indirectFunctionTable)) |
769 | out.importSec->addImport(sym: WasmSym::indirectFunctionTable); |
770 | |
771 | for (Symbol *sym : symtab->symbols()) { |
772 | if (!shouldImport(sym)) |
773 | continue; |
774 | if (sym == WasmSym::indirectFunctionTable) |
775 | continue; |
776 | LLVM_DEBUG(dbgs() << "import: " << sym->getName() << "\n" ); |
777 | out.importSec->addImport(sym); |
778 | } |
779 | } |
780 | |
781 | void Writer::calculateExports() { |
782 | if (config->relocatable) |
783 | return; |
784 | |
785 | if (!config->relocatable && config->memoryExport.has_value()) { |
786 | out.exportSec->exports.push_back( |
787 | x: WasmExport{.Name: *config->memoryExport, .Kind: WASM_EXTERNAL_MEMORY, .Index: 0}); |
788 | } |
789 | |
790 | unsigned globalIndex = |
791 | out.importSec->getNumImportedGlobals() + out.globalSec->numGlobals(); |
792 | |
793 | for (Symbol *sym : symtab->symbols()) { |
794 | if (!sym->isExported()) |
795 | continue; |
796 | if (!sym->isLive()) |
797 | continue; |
798 | if (isa<SharedFunctionSymbol>(Val: sym) || sym->isShared()) |
799 | continue; |
800 | |
801 | StringRef name = sym->getName(); |
802 | LLVM_DEBUG(dbgs() << "Export: " << name << "\n" ); |
803 | WasmExport export_; |
804 | if (auto *f = dyn_cast<DefinedFunction>(Val: sym)) { |
805 | if (std::optional<StringRef> exportName = f->function->getExportName()) { |
806 | name = *exportName; |
807 | } |
808 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_FUNCTION, .Index: f->getExportedFunctionIndex()}; |
809 | } else if (auto *g = dyn_cast<DefinedGlobal>(Val: sym)) { |
810 | if (g->getGlobalType()->Mutable && !g->getFile() && !g->forceExport) { |
811 | // Avoid exporting mutable globals are linker synthesized (e.g. |
812 | // __stack_pointer or __tls_base) unless they are explicitly exported |
813 | // from the command line. |
814 | // Without this check `--export-all` would cause any program using the |
815 | // stack pointer to export a mutable global even if none of the input |
816 | // files were built with the `mutable-globals` feature. |
817 | continue; |
818 | } |
819 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_GLOBAL, .Index: g->getGlobalIndex()}; |
820 | } else if (auto *t = dyn_cast<DefinedTag>(Val: sym)) { |
821 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_TAG, .Index: t->getTagIndex()}; |
822 | } else if (auto *d = dyn_cast<DefinedData>(Val: sym)) { |
823 | out.globalSec->dataAddressGlobals.push_back(x: d); |
824 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_GLOBAL, .Index: globalIndex++}; |
825 | } else { |
826 | auto *t = cast<DefinedTable>(Val: sym); |
827 | export_ = {.Name: name, .Kind: WASM_EXTERNAL_TABLE, .Index: t->getTableNumber()}; |
828 | } |
829 | |
830 | out.exportSec->exports.push_back(x: export_); |
831 | out.exportSec->exportedSymbols.push_back(x: sym); |
832 | } |
833 | } |
834 | |
835 | void Writer::populateSymtab() { |
836 | if (!config->relocatable && !config->emitRelocs) |
837 | return; |
838 | |
839 | for (Symbol *sym : symtab->symbols()) |
840 | if (sym->isUsedInRegularObj && sym->isLive() && !sym->isShared()) |
841 | out.linkingSec->addToSymtab(sym); |
842 | |
843 | for (ObjFile *file : ctx.objectFiles) { |
844 | LLVM_DEBUG(dbgs() << "Local symtab entries: " << file->getName() << "\n" ); |
845 | for (Symbol *sym : file->getSymbols()) |
846 | if (sym->isLocal() && !isa<SectionSymbol>(Val: sym) && sym->isLive()) |
847 | out.linkingSec->addToSymtab(sym); |
848 | } |
849 | } |
850 | |
851 | void Writer::calculateTypes() { |
852 | // The output type section is the union of the following sets: |
853 | // 1. Any signature used in the TYPE relocation |
854 | // 2. The signatures of all imported functions |
855 | // 3. The signatures of all defined functions |
856 | // 4. The signatures of all imported tags |
857 | // 5. The signatures of all defined tags |
858 | |
859 | for (ObjFile *file : ctx.objectFiles) { |
860 | ArrayRef<WasmSignature> types = file->getWasmObj()->types(); |
861 | for (uint32_t i = 0; i < types.size(); i++) |
862 | if (file->typeIsUsed[i]) |
863 | file->typeMap[i] = out.typeSec->registerType(sig: types[i]); |
864 | } |
865 | |
866 | for (const Symbol *sym : out.importSec->importedSymbols) { |
867 | if (auto *f = dyn_cast<FunctionSymbol>(Val: sym)) |
868 | out.typeSec->registerType(sig: *f->signature); |
869 | else if (auto *t = dyn_cast<TagSymbol>(Val: sym)) |
870 | out.typeSec->registerType(sig: *t->signature); |
871 | } |
872 | |
873 | for (const InputFunction *f : out.functionSec->inputFunctions) |
874 | out.typeSec->registerType(sig: f->signature); |
875 | |
876 | for (const InputTag *t : out.tagSec->inputTags) |
877 | out.typeSec->registerType(sig: t->signature); |
878 | } |
879 | |
880 | // In a command-style link, create a wrapper for each exported symbol |
881 | // which calls the constructors and destructors. |
882 | void Writer::createCommandExportWrappers() { |
883 | // This logic doesn't currently support Emscripten-style PIC mode. |
884 | assert(!ctx.isPic); |
885 | |
886 | // If there are no ctors and there's no libc `__wasm_call_dtors` to |
887 | // call, don't wrap the exports. |
888 | if (initFunctions.empty() && WasmSym::callDtors == nullptr) |
889 | return; |
890 | |
891 | std::vector<DefinedFunction *> toWrap; |
892 | |
893 | for (Symbol *sym : symtab->symbols()) |
894 | if (sym->isExported()) |
895 | if (auto *f = dyn_cast<DefinedFunction>(Val: sym)) |
896 | toWrap.push_back(x: f); |
897 | |
898 | for (auto *f : toWrap) { |
899 | auto funcNameStr = (f->getName() + ".command_export" ).str(); |
900 | commandExportWrapperNames.push_back(x: funcNameStr); |
901 | const std::string &funcName = commandExportWrapperNames.back(); |
902 | |
903 | auto func = make<SyntheticFunction>(args: *f->getSignature(), args: funcName); |
904 | if (f->function->getExportName()) |
905 | func->setExportName(f->function->getExportName()->str()); |
906 | else |
907 | func->setExportName(f->getName().str()); |
908 | |
909 | DefinedFunction *def = |
910 | symtab->addSyntheticFunction(name: funcName, flags: f->flags, function: func); |
911 | def->markLive(); |
912 | |
913 | def->flags |= WASM_SYMBOL_EXPORTED; |
914 | def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN; |
915 | def->forceExport = f->forceExport; |
916 | |
917 | f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN; |
918 | f->flags &= ~WASM_SYMBOL_EXPORTED; |
919 | f->forceExport = false; |
920 | |
921 | out.functionSec->addFunction(func); |
922 | |
923 | createCommandExportWrapper(functionIndex: f->getFunctionIndex(), f: def); |
924 | } |
925 | } |
926 | |
927 | static void finalizeIndirectFunctionTable() { |
928 | if (!WasmSym::indirectFunctionTable) |
929 | return; |
930 | |
931 | if (shouldImport(sym: WasmSym::indirectFunctionTable) && |
932 | !WasmSym::indirectFunctionTable->hasTableNumber()) { |
933 | // Processing -Bsymbolic relocations resulted in a late requirement that the |
934 | // indirect function table be present, and we are running in --import-table |
935 | // mode. Add the table now to the imports section. Otherwise it will be |
936 | // added to the tables section later in assignIndexes. |
937 | out.importSec->addImport(sym: WasmSym::indirectFunctionTable); |
938 | } |
939 | |
940 | uint32_t tableSize = config->tableBase + out.elemSec->numEntries(); |
941 | WasmLimits limits = {.Flags: 0, .Minimum: tableSize, .Maximum: 0}; |
942 | if (WasmSym::indirectFunctionTable->isDefined() && !config->growableTable) { |
943 | limits.Flags |= WASM_LIMITS_FLAG_HAS_MAX; |
944 | limits.Maximum = limits.Minimum; |
945 | } |
946 | if (config->is64.value_or(u: false)) |
947 | limits.Flags |= WASM_LIMITS_FLAG_IS_64; |
948 | WasmSym::indirectFunctionTable->setLimits(limits); |
949 | } |
950 | |
951 | static void scanRelocations() { |
952 | for (ObjFile *file : ctx.objectFiles) { |
953 | LLVM_DEBUG(dbgs() << "scanRelocations: " << file->getName() << "\n" ); |
954 | for (InputChunk *chunk : file->functions) |
955 | scanRelocations(chunk); |
956 | for (InputChunk *chunk : file->segments) |
957 | scanRelocations(chunk); |
958 | for (auto &p : file->customSections) |
959 | scanRelocations(chunk: p); |
960 | } |
961 | } |
962 | |
963 | void Writer::assignIndexes() { |
964 | // Seal the import section, since other index spaces such as function and |
965 | // global are effected by the number of imports. |
966 | out.importSec->seal(); |
967 | |
968 | for (InputFunction *func : ctx.syntheticFunctions) |
969 | out.functionSec->addFunction(func); |
970 | |
971 | for (ObjFile *file : ctx.objectFiles) { |
972 | LLVM_DEBUG(dbgs() << "Functions: " << file->getName() << "\n" ); |
973 | for (InputFunction *func : file->functions) |
974 | out.functionSec->addFunction(func); |
975 | } |
976 | |
977 | for (InputGlobal *global : ctx.syntheticGlobals) |
978 | out.globalSec->addGlobal(global); |
979 | |
980 | for (ObjFile *file : ctx.objectFiles) { |
981 | LLVM_DEBUG(dbgs() << "Globals: " << file->getName() << "\n" ); |
982 | for (InputGlobal *global : file->globals) |
983 | out.globalSec->addGlobal(global); |
984 | } |
985 | |
986 | for (ObjFile *file : ctx.objectFiles) { |
987 | LLVM_DEBUG(dbgs() << "Tags: " << file->getName() << "\n" ); |
988 | for (InputTag *tag : file->tags) |
989 | out.tagSec->addTag(tag); |
990 | } |
991 | |
992 | for (ObjFile *file : ctx.objectFiles) { |
993 | LLVM_DEBUG(dbgs() << "Tables: " << file->getName() << "\n" ); |
994 | for (InputTable *table : file->tables) |
995 | out.tableSec->addTable(table); |
996 | } |
997 | |
998 | for (InputTable *table : ctx.syntheticTables) |
999 | out.tableSec->addTable(table); |
1000 | |
1001 | out.globalSec->assignIndexes(); |
1002 | out.tableSec->assignIndexes(); |
1003 | } |
1004 | |
1005 | static StringRef getOutputDataSegmentName(const InputChunk &seg) { |
1006 | // We always merge .tbss and .tdata into a single TLS segment so all TLS |
1007 | // symbols are be relative to single __tls_base. |
1008 | if (seg.isTLS()) |
1009 | return ".tdata" ; |
1010 | if (!config->mergeDataSegments) |
1011 | return seg.name; |
1012 | if (seg.name.starts_with(Prefix: ".text." )) |
1013 | return ".text" ; |
1014 | if (seg.name.starts_with(Prefix: ".data." )) |
1015 | return ".data" ; |
1016 | if (seg.name.starts_with(Prefix: ".bss." )) |
1017 | return ".bss" ; |
1018 | if (seg.name.starts_with(Prefix: ".rodata." )) |
1019 | return ".rodata" ; |
1020 | return seg.name; |
1021 | } |
1022 | |
1023 | OutputSegment *Writer::createOutputSegment(StringRef name) { |
1024 | LLVM_DEBUG(dbgs() << "new segment: " << name << "\n" ); |
1025 | OutputSegment *s = make<OutputSegment>(args&: name); |
1026 | if (config->sharedMemory) |
1027 | s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE; |
1028 | if (!config->relocatable && name.starts_with(Prefix: ".bss" )) |
1029 | s->isBss = true; |
1030 | segments.push_back(x: s); |
1031 | return s; |
1032 | } |
1033 | |
1034 | void Writer::createOutputSegments() { |
1035 | for (ObjFile *file : ctx.objectFiles) { |
1036 | for (InputChunk *segment : file->segments) { |
1037 | if (!segment->live) |
1038 | continue; |
1039 | StringRef name = getOutputDataSegmentName(seg: *segment); |
1040 | OutputSegment *s = nullptr; |
1041 | // When running in relocatable mode we can't merge segments that are part |
1042 | // of comdat groups since the ultimate linker needs to be able exclude or |
1043 | // include them individually. |
1044 | if (config->relocatable && !segment->getComdatName().empty()) { |
1045 | s = createOutputSegment(name); |
1046 | } else { |
1047 | if (segmentMap.count(Val: name) == 0) |
1048 | segmentMap[name] = createOutputSegment(name); |
1049 | s = segmentMap[name]; |
1050 | } |
1051 | s->addInputSegment(inSeg: segment); |
1052 | } |
1053 | } |
1054 | |
1055 | // Sort segments by type, placing .bss last |
1056 | std::stable_sort(first: segments.begin(), last: segments.end(), |
1057 | comp: [](const OutputSegment *a, const OutputSegment *b) { |
1058 | auto order = [](StringRef name) { |
1059 | return StringSwitch<int>(name) |
1060 | .StartsWith(S: ".tdata" , Value: 0) |
1061 | .StartsWith(S: ".rodata" , Value: 1) |
1062 | .StartsWith(S: ".data" , Value: 2) |
1063 | .StartsWith(S: ".bss" , Value: 4) |
1064 | .Default(Value: 3); |
1065 | }; |
1066 | return order(a->name) < order(b->name); |
1067 | }); |
1068 | |
1069 | for (size_t i = 0; i < segments.size(); ++i) |
1070 | segments[i]->index = i; |
1071 | |
1072 | // Merge MergeInputSections into a single MergeSyntheticSection. |
1073 | LLVM_DEBUG(dbgs() << "-- finalize input semgments\n" ); |
1074 | for (OutputSegment *seg : segments) |
1075 | seg->finalizeInputSegments(); |
1076 | } |
1077 | |
1078 | void Writer::combineOutputSegments() { |
1079 | // With PIC code we currently only support a single active data segment since |
1080 | // we only have a single __memory_base to use as our base address. This pass |
1081 | // combines all data segments into a single .data segment. |
1082 | // This restriction does not apply when the extended const extension is |
1083 | // available: https://github.com/WebAssembly/extended-const |
1084 | assert(!config->extendedConst); |
1085 | assert(ctx.isPic && !config->sharedMemory); |
1086 | if (segments.size() <= 1) |
1087 | return; |
1088 | OutputSegment *combined = make<OutputSegment>(args: ".data" ); |
1089 | combined->startVA = segments[0]->startVA; |
1090 | for (OutputSegment *s : segments) { |
1091 | bool first = true; |
1092 | for (InputChunk *inSeg : s->inputSegments) { |
1093 | if (first) |
1094 | inSeg->alignment = std::max(a: inSeg->alignment, b: s->alignment); |
1095 | first = false; |
1096 | #ifndef NDEBUG |
1097 | uint64_t oldVA = inSeg->getVA(); |
1098 | #endif |
1099 | combined->addInputSegment(inSeg); |
1100 | #ifndef NDEBUG |
1101 | uint64_t newVA = inSeg->getVA(); |
1102 | LLVM_DEBUG(dbgs() << "added input segment. name=" << inSeg->name |
1103 | << " oldVA=" << oldVA << " newVA=" << newVA << "\n" ); |
1104 | assert(oldVA == newVA); |
1105 | #endif |
1106 | } |
1107 | } |
1108 | |
1109 | segments = {combined}; |
1110 | } |
1111 | |
1112 | static void createFunction(DefinedFunction *func, StringRef bodyContent) { |
1113 | std::string functionBody; |
1114 | { |
1115 | raw_string_ostream os(functionBody); |
1116 | writeUleb128(os, number: bodyContent.size(), msg: "function size" ); |
1117 | os << bodyContent; |
1118 | } |
1119 | ArrayRef<uint8_t> body = arrayRefFromStringRef(Input: saver().save(S: functionBody)); |
1120 | cast<SyntheticFunction>(Val: func->function)->setBody(body); |
1121 | } |
1122 | |
1123 | bool Writer::needsPassiveInitialization(const OutputSegment *segment) { |
1124 | // If bulk memory features is supported then we can perform bss initialization |
1125 | // (via memory.fill) during `__wasm_init_memory`. |
1126 | if (config->memoryImport.has_value() && !segment->requiredInBinary()) |
1127 | return true; |
1128 | return segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE; |
1129 | } |
1130 | |
1131 | bool Writer::hasPassiveInitializedSegments() { |
1132 | return llvm::any_of(Range&: segments, P: [this](const OutputSegment *s) { |
1133 | return this->needsPassiveInitialization(segment: s); |
1134 | }); |
1135 | } |
1136 | |
1137 | void Writer::createSyntheticInitFunctions() { |
1138 | if (config->relocatable) |
1139 | return; |
1140 | |
1141 | static WasmSignature nullSignature = {{}, {}}; |
1142 | |
1143 | // Passive segments are used to avoid memory being reinitialized on each |
1144 | // thread's instantiation. These passive segments are initialized and |
1145 | // dropped in __wasm_init_memory, which is registered as the start function |
1146 | // We also initialize bss segments (using memory.fill) as part of this |
1147 | // function. |
1148 | if (hasPassiveInitializedSegments()) { |
1149 | WasmSym::initMemory = symtab->addSyntheticFunction( |
1150 | name: "__wasm_init_memory" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1151 | function: make<SyntheticFunction>(args&: nullSignature, args: "__wasm_init_memory" )); |
1152 | WasmSym::initMemory->markLive(); |
1153 | if (config->sharedMemory) { |
1154 | // This global is assigned during __wasm_init_memory in the shared memory |
1155 | // case. |
1156 | WasmSym::tlsBase->markLive(); |
1157 | } |
1158 | } |
1159 | |
1160 | if (config->sharedMemory) { |
1161 | if (out.globalSec->needsTLSRelocations()) { |
1162 | WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction( |
1163 | name: "__wasm_apply_global_tls_relocs" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1164 | function: make<SyntheticFunction>(args&: nullSignature, |
1165 | args: "__wasm_apply_global_tls_relocs" )); |
1166 | WasmSym::applyGlobalTLSRelocs->markLive(); |
1167 | // TLS relocations depend on the __tls_base symbols |
1168 | WasmSym::tlsBase->markLive(); |
1169 | } |
1170 | |
1171 | auto hasTLSRelocs = [](const OutputSegment *segment) { |
1172 | if (segment->isTLS()) |
1173 | for (const auto* is: segment->inputSegments) |
1174 | if (is->getRelocations().size()) |
1175 | return true; |
1176 | return false; |
1177 | }; |
1178 | if (llvm::any_of(Range&: segments, P: hasTLSRelocs)) { |
1179 | WasmSym::applyTLSRelocs = symtab->addSyntheticFunction( |
1180 | name: "__wasm_apply_tls_relocs" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1181 | function: make<SyntheticFunction>(args&: nullSignature, |
1182 | args: "__wasm_apply_tls_relocs" )); |
1183 | WasmSym::applyTLSRelocs->markLive(); |
1184 | } |
1185 | } |
1186 | |
1187 | if (ctx.isPic && out.globalSec->needsRelocations()) { |
1188 | WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction( |
1189 | name: "__wasm_apply_global_relocs" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1190 | function: make<SyntheticFunction>(args&: nullSignature, args: "__wasm_apply_global_relocs" )); |
1191 | WasmSym::applyGlobalRelocs->markLive(); |
1192 | } |
1193 | |
1194 | // If there is only one start function we can just use that function |
1195 | // itself as the Wasm start function, otherwise we need to synthesize |
1196 | // a new function to call them in sequence. |
1197 | if (WasmSym::applyGlobalRelocs && WasmSym::initMemory) { |
1198 | WasmSym::startFunction = symtab->addSyntheticFunction( |
1199 | name: "__wasm_start" , flags: WASM_SYMBOL_VISIBILITY_HIDDEN, |
1200 | function: make<SyntheticFunction>(args&: nullSignature, args: "__wasm_start" )); |
1201 | WasmSym::startFunction->markLive(); |
1202 | } |
1203 | } |
1204 | |
1205 | void Writer::createInitMemoryFunction() { |
1206 | LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n" ); |
1207 | assert(WasmSym::initMemory); |
1208 | assert(hasPassiveInitializedSegments()); |
1209 | uint64_t flagAddress; |
1210 | if (config->sharedMemory) { |
1211 | assert(WasmSym::initMemoryFlag); |
1212 | flagAddress = WasmSym::initMemoryFlag->getVA(); |
1213 | } |
1214 | bool is64 = config->is64.value_or(u: false); |
1215 | std::string bodyContent; |
1216 | { |
1217 | raw_string_ostream os(bodyContent); |
1218 | // Initialize memory in a thread-safe manner. The thread that successfully |
1219 | // increments the flag from 0 to 1 is responsible for performing the memory |
1220 | // initialization. Other threads go sleep on the flag until the first thread |
1221 | // finishing initializing memory, increments the flag to 2, and wakes all |
1222 | // the other threads. Once the flag has been set to 2, subsequently started |
1223 | // threads will skip the sleep. All threads unconditionally drop their |
1224 | // passive data segments once memory has been initialized. The generated |
1225 | // code is as follows: |
1226 | // |
1227 | // (func $__wasm_init_memory |
1228 | // (block $drop |
1229 | // (block $wait |
1230 | // (block $init |
1231 | // (br_table $init $wait $drop |
1232 | // (i32.atomic.rmw.cmpxchg align=2 offset=0 |
1233 | // (i32.const $__init_memory_flag) |
1234 | // (i32.const 0) |
1235 | // (i32.const 1) |
1236 | // ) |
1237 | // ) |
1238 | // ) ;; $init |
1239 | // ( ... initialize data segments ... ) |
1240 | // (i32.atomic.store align=2 offset=0 |
1241 | // (i32.const $__init_memory_flag) |
1242 | // (i32.const 2) |
1243 | // ) |
1244 | // (drop |
1245 | // (i32.atomic.notify align=2 offset=0 |
1246 | // (i32.const $__init_memory_flag) |
1247 | // (i32.const -1u) |
1248 | // ) |
1249 | // ) |
1250 | // (br $drop) |
1251 | // ) ;; $wait |
1252 | // (drop |
1253 | // (i32.atomic.wait align=2 offset=0 |
1254 | // (i32.const $__init_memory_flag) |
1255 | // (i32.const 1) |
1256 | // (i32.const -1) |
1257 | // ) |
1258 | // ) |
1259 | // ) ;; $drop |
1260 | // ( ... drop data segments ... ) |
1261 | // ) |
1262 | // |
1263 | // When we are building with PIC, calculate the flag location using: |
1264 | // |
1265 | // (global.get $__memory_base) |
1266 | // (i32.const $__init_memory_flag) |
1267 | // (i32.const 1) |
1268 | |
1269 | auto writeGetFlagAddress = [&]() { |
1270 | if (ctx.isPic) { |
1271 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.get" ); |
1272 | writeUleb128(os, number: 0, msg: "local 0" ); |
1273 | } else { |
1274 | writePtrConst(os, number: flagAddress, is64, msg: "flag address" ); |
1275 | } |
1276 | }; |
1277 | |
1278 | if (config->sharedMemory) { |
1279 | // With PIC code we cache the flag address in local 0 |
1280 | if (ctx.isPic) { |
1281 | writeUleb128(os, number: 1, msg: "num local decls" ); |
1282 | writeUleb128(os, number: 2, msg: "local count" ); |
1283 | writeU8(os, byte: is64 ? WASM_TYPE_I64 : WASM_TYPE_I32, msg: "address type" ); |
1284 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET" ); |
1285 | writeUleb128(os, number: WasmSym::memoryBase->getGlobalIndex(), msg: "memory_base" ); |
1286 | writePtrConst(os, number: flagAddress, is64, msg: "flag address" ); |
1287 | writeU8(os, byte: is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, msg: "add" ); |
1288 | writeU8(os, byte: WASM_OPCODE_LOCAL_SET, msg: "local.set" ); |
1289 | writeUleb128(os, number: 0, msg: "local 0" ); |
1290 | } else { |
1291 | writeUleb128(os, number: 0, msg: "num locals" ); |
1292 | } |
1293 | |
1294 | // Set up destination blocks |
1295 | writeU8(os, byte: WASM_OPCODE_BLOCK, msg: "block $drop" ); |
1296 | writeU8(os, byte: WASM_TYPE_NORESULT, msg: "block type" ); |
1297 | writeU8(os, byte: WASM_OPCODE_BLOCK, msg: "block $wait" ); |
1298 | writeU8(os, byte: WASM_TYPE_NORESULT, msg: "block type" ); |
1299 | writeU8(os, byte: WASM_OPCODE_BLOCK, msg: "block $init" ); |
1300 | writeU8(os, byte: WASM_TYPE_NORESULT, msg: "block type" ); |
1301 | |
1302 | // Atomically check whether we win the race. |
1303 | writeGetFlagAddress(); |
1304 | writeI32Const(os, number: 0, msg: "expected flag value" ); |
1305 | writeI32Const(os, number: 1, msg: "new flag value" ); |
1306 | writeU8(os, byte: WASM_OPCODE_ATOMICS_PREFIX, msg: "atomics prefix" ); |
1307 | writeUleb128(os, number: WASM_OPCODE_I32_RMW_CMPXCHG, msg: "i32.atomic.rmw.cmpxchg" ); |
1308 | writeMemArg(os, alignment: 2, offset: 0); |
1309 | |
1310 | // Based on the value, decide what to do next. |
1311 | writeU8(os, byte: WASM_OPCODE_BR_TABLE, msg: "br_table" ); |
1312 | writeUleb128(os, number: 2, msg: "label vector length" ); |
1313 | writeUleb128(os, number: 0, msg: "label $init" ); |
1314 | writeUleb128(os, number: 1, msg: "label $wait" ); |
1315 | writeUleb128(os, number: 2, msg: "default label $drop" ); |
1316 | |
1317 | // Initialize passive data segments |
1318 | writeU8(os, byte: WASM_OPCODE_END, msg: "end $init" ); |
1319 | } else { |
1320 | writeUleb128(os, number: 0, msg: "num local decls" ); |
1321 | } |
1322 | |
1323 | for (const OutputSegment *s : segments) { |
1324 | if (needsPassiveInitialization(segment: s)) { |
1325 | // For passive BSS segments we can simple issue a memory.fill(0). |
1326 | // For non-BSS segments we do a memory.init. Both these |
1327 | // instructions take as their first argument the destination |
1328 | // address. |
1329 | writePtrConst(os, number: s->startVA, is64, msg: "destination address" ); |
1330 | if (ctx.isPic) { |
1331 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET" ); |
1332 | writeUleb128(os, number: WasmSym::memoryBase->getGlobalIndex(), |
1333 | msg: "__memory_base" ); |
1334 | writeU8(os, byte: is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, |
1335 | msg: "i32.add" ); |
1336 | } |
1337 | |
1338 | // When we initialize the TLS segment we also set the `__tls_base` |
1339 | // global. This allows the runtime to use this static copy of the |
1340 | // TLS data for the first/main thread. |
1341 | if (config->sharedMemory && s->isTLS()) { |
1342 | if (ctx.isPic) { |
1343 | // Cache the result of the addionion in local 0 |
1344 | writeU8(os, byte: WASM_OPCODE_LOCAL_TEE, msg: "local.tee" ); |
1345 | writeUleb128(os, number: 1, msg: "local 1" ); |
1346 | } else { |
1347 | writePtrConst(os, number: s->startVA, is64, msg: "destination address" ); |
1348 | } |
1349 | writeU8(os, byte: WASM_OPCODE_GLOBAL_SET, msg: "GLOBAL_SET" ); |
1350 | writeUleb128(os, number: WasmSym::tlsBase->getGlobalIndex(), |
1351 | msg: "__tls_base" ); |
1352 | if (ctx.isPic) { |
1353 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.tee" ); |
1354 | writeUleb128(os, number: 1, msg: "local 1" ); |
1355 | } |
1356 | } |
1357 | |
1358 | if (s->isBss) { |
1359 | writeI32Const(os, number: 0, msg: "fill value" ); |
1360 | writePtrConst(os, number: s->size, is64, msg: "memory region size" ); |
1361 | writeU8(os, byte: WASM_OPCODE_MISC_PREFIX, msg: "bulk-memory prefix" ); |
1362 | writeUleb128(os, number: WASM_OPCODE_MEMORY_FILL, msg: "memory.fill" ); |
1363 | writeU8(os, byte: 0, msg: "memory index immediate" ); |
1364 | } else { |
1365 | writeI32Const(os, number: 0, msg: "source segment offset" ); |
1366 | writeI32Const(os, number: s->size, msg: "memory region size" ); |
1367 | writeU8(os, byte: WASM_OPCODE_MISC_PREFIX, msg: "bulk-memory prefix" ); |
1368 | writeUleb128(os, number: WASM_OPCODE_MEMORY_INIT, msg: "memory.init" ); |
1369 | writeUleb128(os, number: s->index, msg: "segment index immediate" ); |
1370 | writeU8(os, byte: 0, msg: "memory index immediate" ); |
1371 | } |
1372 | } |
1373 | } |
1374 | |
1375 | if (config->sharedMemory) { |
1376 | // Set flag to 2 to mark end of initialization |
1377 | writeGetFlagAddress(); |
1378 | writeI32Const(os, number: 2, msg: "flag value" ); |
1379 | writeU8(os, byte: WASM_OPCODE_ATOMICS_PREFIX, msg: "atomics prefix" ); |
1380 | writeUleb128(os, number: WASM_OPCODE_I32_ATOMIC_STORE, msg: "i32.atomic.store" ); |
1381 | writeMemArg(os, alignment: 2, offset: 0); |
1382 | |
1383 | // Notify any waiters that memory initialization is complete |
1384 | writeGetFlagAddress(); |
1385 | writeI32Const(os, number: -1, msg: "number of waiters" ); |
1386 | writeU8(os, byte: WASM_OPCODE_ATOMICS_PREFIX, msg: "atomics prefix" ); |
1387 | writeUleb128(os, number: WASM_OPCODE_ATOMIC_NOTIFY, msg: "atomic.notify" ); |
1388 | writeMemArg(os, alignment: 2, offset: 0); |
1389 | writeU8(os, byte: WASM_OPCODE_DROP, msg: "drop" ); |
1390 | |
1391 | // Branch to drop the segments |
1392 | writeU8(os, byte: WASM_OPCODE_BR, msg: "br" ); |
1393 | writeUleb128(os, number: 1, msg: "label $drop" ); |
1394 | |
1395 | // Wait for the winning thread to initialize memory |
1396 | writeU8(os, byte: WASM_OPCODE_END, msg: "end $wait" ); |
1397 | writeGetFlagAddress(); |
1398 | writeI32Const(os, number: 1, msg: "expected flag value" ); |
1399 | writeI64Const(os, number: -1, msg: "timeout" ); |
1400 | |
1401 | writeU8(os, byte: WASM_OPCODE_ATOMICS_PREFIX, msg: "atomics prefix" ); |
1402 | writeUleb128(os, number: WASM_OPCODE_I32_ATOMIC_WAIT, msg: "i32.atomic.wait" ); |
1403 | writeMemArg(os, alignment: 2, offset: 0); |
1404 | writeU8(os, byte: WASM_OPCODE_DROP, msg: "drop" ); |
1405 | |
1406 | // Unconditionally drop passive data segments |
1407 | writeU8(os, byte: WASM_OPCODE_END, msg: "end $drop" ); |
1408 | } |
1409 | |
1410 | for (const OutputSegment *s : segments) { |
1411 | if (needsPassiveInitialization(segment: s) && !s->isBss) { |
1412 | // The TLS region should not be dropped since its is needed |
1413 | // during the initialization of each thread (__wasm_init_tls). |
1414 | if (config->sharedMemory && s->isTLS()) |
1415 | continue; |
1416 | // data.drop instruction |
1417 | writeU8(os, byte: WASM_OPCODE_MISC_PREFIX, msg: "bulk-memory prefix" ); |
1418 | writeUleb128(os, number: WASM_OPCODE_DATA_DROP, msg: "data.drop" ); |
1419 | writeUleb128(os, number: s->index, msg: "segment index immediate" ); |
1420 | } |
1421 | } |
1422 | |
1423 | // End the function |
1424 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1425 | } |
1426 | |
1427 | createFunction(func: WasmSym::initMemory, bodyContent); |
1428 | } |
1429 | |
1430 | void Writer::createStartFunction() { |
1431 | // If the start function exists when we have more than one function to call. |
1432 | if (WasmSym::initMemory && WasmSym::applyGlobalRelocs) { |
1433 | assert(WasmSym::startFunction); |
1434 | std::string bodyContent; |
1435 | { |
1436 | raw_string_ostream os(bodyContent); |
1437 | writeUleb128(os, number: 0, msg: "num locals" ); |
1438 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1439 | writeUleb128(os, number: WasmSym::applyGlobalRelocs->getFunctionIndex(), |
1440 | msg: "function index" ); |
1441 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1442 | writeUleb128(os, number: WasmSym::initMemory->getFunctionIndex(), |
1443 | msg: "function index" ); |
1444 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1445 | } |
1446 | createFunction(func: WasmSym::startFunction, bodyContent); |
1447 | } else if (WasmSym::initMemory) { |
1448 | WasmSym::startFunction = WasmSym::initMemory; |
1449 | } else if (WasmSym::applyGlobalRelocs) { |
1450 | WasmSym::startFunction = WasmSym::applyGlobalRelocs; |
1451 | } |
1452 | } |
1453 | |
1454 | // For -shared (PIC) output, we create create a synthetic function which will |
1455 | // apply any relocations to the data segments on startup. This function is |
1456 | // called `__wasm_apply_data_relocs` and is expected to be called before |
1457 | // any user code (i.e. before `__wasm_call_ctors`). |
1458 | void Writer::createApplyDataRelocationsFunction() { |
1459 | LLVM_DEBUG(dbgs() << "createApplyDataRelocationsFunction\n" ); |
1460 | // First write the body's contents to a string. |
1461 | std::string bodyContent; |
1462 | { |
1463 | raw_string_ostream os(bodyContent); |
1464 | writeUleb128(os, number: 0, msg: "num locals" ); |
1465 | for (const OutputSegment *seg : segments) |
1466 | if (!config->sharedMemory || !seg->isTLS()) |
1467 | for (const InputChunk *inSeg : seg->inputSegments) |
1468 | inSeg->generateRelocationCode(os); |
1469 | |
1470 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1471 | } |
1472 | |
1473 | createFunction(func: WasmSym::applyDataRelocs, bodyContent); |
1474 | } |
1475 | |
1476 | void Writer::createApplyTLSRelocationsFunction() { |
1477 | LLVM_DEBUG(dbgs() << "createApplyTLSRelocationsFunction\n" ); |
1478 | std::string bodyContent; |
1479 | { |
1480 | raw_string_ostream os(bodyContent); |
1481 | writeUleb128(os, number: 0, msg: "num locals" ); |
1482 | for (const OutputSegment *seg : segments) |
1483 | if (seg->isTLS()) |
1484 | for (const InputChunk *inSeg : seg->inputSegments) |
1485 | inSeg->generateRelocationCode(os); |
1486 | |
1487 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1488 | } |
1489 | |
1490 | createFunction(func: WasmSym::applyTLSRelocs, bodyContent); |
1491 | } |
1492 | |
1493 | // Similar to createApplyDataRelocationsFunction but generates relocation code |
1494 | // for WebAssembly globals. Because these globals are not shared between threads |
1495 | // these relocation need to run on every thread. |
1496 | void Writer::createApplyGlobalRelocationsFunction() { |
1497 | // First write the body's contents to a string. |
1498 | std::string bodyContent; |
1499 | { |
1500 | raw_string_ostream os(bodyContent); |
1501 | writeUleb128(os, number: 0, msg: "num locals" ); |
1502 | out.globalSec->generateRelocationCode(os, TLS: false); |
1503 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1504 | } |
1505 | |
1506 | createFunction(func: WasmSym::applyGlobalRelocs, bodyContent); |
1507 | } |
1508 | |
1509 | // Similar to createApplyGlobalRelocationsFunction but for |
1510 | // TLS symbols. This cannot be run during the start function |
1511 | // but must be delayed until __wasm_init_tls is called. |
1512 | void Writer::createApplyGlobalTLSRelocationsFunction() { |
1513 | // First write the body's contents to a string. |
1514 | std::string bodyContent; |
1515 | { |
1516 | raw_string_ostream os(bodyContent); |
1517 | writeUleb128(os, number: 0, msg: "num locals" ); |
1518 | out.globalSec->generateRelocationCode(os, TLS: true); |
1519 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1520 | } |
1521 | |
1522 | createFunction(func: WasmSym::applyGlobalTLSRelocs, bodyContent); |
1523 | } |
1524 | |
1525 | // Create synthetic "__wasm_call_ctors" function based on ctor functions |
1526 | // in input object. |
1527 | void Writer::createCallCtorsFunction() { |
1528 | // If __wasm_call_ctors isn't referenced, there aren't any ctors, don't |
1529 | // define the `__wasm_call_ctors` function. |
1530 | if (!WasmSym::callCtors->isLive() && initFunctions.empty()) |
1531 | return; |
1532 | |
1533 | // First write the body's contents to a string. |
1534 | std::string bodyContent; |
1535 | { |
1536 | raw_string_ostream os(bodyContent); |
1537 | writeUleb128(os, number: 0, msg: "num locals" ); |
1538 | |
1539 | // Call constructors |
1540 | for (const WasmInitEntry &f : initFunctions) { |
1541 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1542 | writeUleb128(os, number: f.sym->getFunctionIndex(), msg: "function index" ); |
1543 | for (size_t i = 0; i < f.sym->signature->Returns.size(); i++) { |
1544 | writeU8(os, byte: WASM_OPCODE_DROP, msg: "DROP" ); |
1545 | } |
1546 | } |
1547 | |
1548 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1549 | } |
1550 | |
1551 | createFunction(func: WasmSym::callCtors, bodyContent); |
1552 | } |
1553 | |
1554 | // Create a wrapper around a function export which calls the |
1555 | // static constructors and destructors. |
1556 | void Writer::createCommandExportWrapper(uint32_t functionIndex, |
1557 | DefinedFunction *f) { |
1558 | // First write the body's contents to a string. |
1559 | std::string bodyContent; |
1560 | { |
1561 | raw_string_ostream os(bodyContent); |
1562 | writeUleb128(os, number: 0, msg: "num locals" ); |
1563 | |
1564 | // Call `__wasm_call_ctors` which call static constructors (and |
1565 | // applies any runtime relocations in Emscripten-style PIC mode) |
1566 | if (WasmSym::callCtors->isLive()) { |
1567 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1568 | writeUleb128(os, number: WasmSym::callCtors->getFunctionIndex(), |
1569 | msg: "function index" ); |
1570 | } |
1571 | |
1572 | // Call the user's code, leaving any return values on the operand stack. |
1573 | for (size_t i = 0; i < f->signature->Params.size(); ++i) { |
1574 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.get" ); |
1575 | writeUleb128(os, number: i, msg: "local index" ); |
1576 | } |
1577 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1578 | writeUleb128(os, number: functionIndex, msg: "function index" ); |
1579 | |
1580 | // Call the function that calls the destructors. |
1581 | if (DefinedFunction *callDtors = WasmSym::callDtors) { |
1582 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1583 | writeUleb128(os, number: callDtors->getFunctionIndex(), msg: "function index" ); |
1584 | } |
1585 | |
1586 | // End the function, returning the return values from the user's code. |
1587 | writeU8(os, byte: WASM_OPCODE_END, msg: "END" ); |
1588 | } |
1589 | |
1590 | createFunction(func: f, bodyContent); |
1591 | } |
1592 | |
1593 | void Writer::createInitTLSFunction() { |
1594 | std::string bodyContent; |
1595 | { |
1596 | raw_string_ostream os(bodyContent); |
1597 | |
1598 | OutputSegment *tlsSeg = nullptr; |
1599 | for (auto *seg : segments) { |
1600 | if (seg->name == ".tdata" ) { |
1601 | tlsSeg = seg; |
1602 | break; |
1603 | } |
1604 | } |
1605 | |
1606 | writeUleb128(os, number: 0, msg: "num locals" ); |
1607 | if (tlsSeg) { |
1608 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.get" ); |
1609 | writeUleb128(os, number: 0, msg: "local index" ); |
1610 | |
1611 | writeU8(os, byte: WASM_OPCODE_GLOBAL_SET, msg: "global.set" ); |
1612 | writeUleb128(os, number: WasmSym::tlsBase->getGlobalIndex(), msg: "global index" ); |
1613 | |
1614 | // FIXME(wvo): this local needs to be I64 in wasm64, or we need an extend op. |
1615 | writeU8(os, byte: WASM_OPCODE_LOCAL_GET, msg: "local.get" ); |
1616 | writeUleb128(os, number: 0, msg: "local index" ); |
1617 | |
1618 | writeI32Const(os, number: 0, msg: "segment offset" ); |
1619 | |
1620 | writeI32Const(os, number: tlsSeg->size, msg: "memory region size" ); |
1621 | |
1622 | writeU8(os, byte: WASM_OPCODE_MISC_PREFIX, msg: "bulk-memory prefix" ); |
1623 | writeUleb128(os, number: WASM_OPCODE_MEMORY_INIT, msg: "MEMORY.INIT" ); |
1624 | writeUleb128(os, number: tlsSeg->index, msg: "segment index immediate" ); |
1625 | writeU8(os, byte: 0, msg: "memory index immediate" ); |
1626 | } |
1627 | |
1628 | if (WasmSym::applyTLSRelocs) { |
1629 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1630 | writeUleb128(os, number: WasmSym::applyTLSRelocs->getFunctionIndex(), |
1631 | msg: "function index" ); |
1632 | } |
1633 | |
1634 | if (WasmSym::applyGlobalTLSRelocs) { |
1635 | writeU8(os, byte: WASM_OPCODE_CALL, msg: "CALL" ); |
1636 | writeUleb128(os, number: WasmSym::applyGlobalTLSRelocs->getFunctionIndex(), |
1637 | msg: "function index" ); |
1638 | } |
1639 | writeU8(os, byte: WASM_OPCODE_END, msg: "end function" ); |
1640 | } |
1641 | |
1642 | createFunction(func: WasmSym::initTLS, bodyContent); |
1643 | } |
1644 | |
1645 | // Populate InitFunctions vector with init functions from all input objects. |
1646 | // This is then used either when creating the output linking section or to |
1647 | // synthesize the "__wasm_call_ctors" function. |
1648 | void Writer::calculateInitFunctions() { |
1649 | if (!config->relocatable && !WasmSym::callCtors->isLive()) |
1650 | return; |
1651 | |
1652 | for (ObjFile *file : ctx.objectFiles) { |
1653 | const WasmLinkingData &l = file->getWasmObj()->linkingData(); |
1654 | for (const WasmInitFunc &f : l.InitFunctions) { |
1655 | FunctionSymbol *sym = file->getFunctionSymbol(index: f.Symbol); |
1656 | // comdat exclusions can cause init functions be discarded. |
1657 | if (sym->isDiscarded() || !sym->isLive()) |
1658 | continue; |
1659 | if (sym->signature->Params.size() != 0) |
1660 | error(msg: "constructor functions cannot take arguments: " + toString(sym: *sym)); |
1661 | LLVM_DEBUG(dbgs() << "initFunctions: " << toString(*sym) << "\n" ); |
1662 | initFunctions.emplace_back(args: WasmInitEntry{.sym: sym, .priority: f.Priority}); |
1663 | } |
1664 | } |
1665 | |
1666 | // Sort in order of priority (lowest first) so that they are called |
1667 | // in the correct order. |
1668 | llvm::stable_sort(Range&: initFunctions, |
1669 | C: [](const WasmInitEntry &l, const WasmInitEntry &r) { |
1670 | return l.priority < r.priority; |
1671 | }); |
1672 | } |
1673 | |
1674 | void Writer::createSyntheticSections() { |
1675 | out.dylinkSec = make<DylinkSection>(); |
1676 | out.typeSec = make<TypeSection>(); |
1677 | out.importSec = make<ImportSection>(); |
1678 | out.functionSec = make<FunctionSection>(); |
1679 | out.tableSec = make<TableSection>(); |
1680 | out.memorySec = make<MemorySection>(); |
1681 | out.tagSec = make<TagSection>(); |
1682 | out.globalSec = make<GlobalSection>(); |
1683 | out.exportSec = make<ExportSection>(); |
1684 | out.startSec = make<StartSection>(); |
1685 | out.elemSec = make<ElemSection>(); |
1686 | out.producersSec = make<ProducersSection>(); |
1687 | out.targetFeaturesSec = make<TargetFeaturesSection>(); |
1688 | out.buildIdSec = make<BuildIdSection>(); |
1689 | } |
1690 | |
1691 | void Writer::createSyntheticSectionsPostLayout() { |
1692 | out.dataCountSec = make<DataCountSection>(args&: segments); |
1693 | out.linkingSec = make<LinkingSection>(args&: initFunctions, args&: segments); |
1694 | out.nameSec = make<NameSection>(args&: segments); |
1695 | } |
1696 | |
1697 | void Writer::run() { |
1698 | // For PIC code the table base is assigned dynamically by the loader. |
1699 | // For non-PIC, we start at 1 so that accessing table index 0 always traps. |
1700 | if (!ctx.isPic && WasmSym::definedTableBase) |
1701 | WasmSym::definedTableBase->setVA(config->tableBase); |
1702 | |
1703 | log(msg: "-- createOutputSegments" ); |
1704 | createOutputSegments(); |
1705 | log(msg: "-- createSyntheticSections" ); |
1706 | createSyntheticSections(); |
1707 | log(msg: "-- layoutMemory" ); |
1708 | layoutMemory(); |
1709 | |
1710 | if (!config->relocatable) { |
1711 | // Create linker synthesized __start_SECNAME/__stop_SECNAME symbols |
1712 | // This has to be done after memory layout is performed. |
1713 | for (const OutputSegment *seg : segments) { |
1714 | addStartStopSymbols(seg); |
1715 | } |
1716 | } |
1717 | |
1718 | for (auto &pair : config->exportedSymbols) { |
1719 | Symbol *sym = symtab->find(name: pair.first()); |
1720 | if (sym && sym->isDefined()) |
1721 | sym->forceExport = true; |
1722 | } |
1723 | |
1724 | // Delay reporting errors about explicit exports until after |
1725 | // addStartStopSymbols which can create optional symbols. |
1726 | for (auto &name : config->requiredExports) { |
1727 | Symbol *sym = symtab->find(name); |
1728 | if (!sym || !sym->isDefined()) { |
1729 | if (config->unresolvedSymbols == UnresolvedPolicy::ReportError) |
1730 | error(msg: Twine("symbol exported via --export not found: " ) + name); |
1731 | if (config->unresolvedSymbols == UnresolvedPolicy::Warn) |
1732 | warn(msg: Twine("symbol exported via --export not found: " ) + name); |
1733 | } |
1734 | } |
1735 | |
1736 | log(msg: "-- populateTargetFeatures" ); |
1737 | populateTargetFeatures(); |
1738 | |
1739 | // When outputting PIC code each segment lives at at fixes offset from the |
1740 | // `__memory_base` import. Unless we support the extended const expression we |
1741 | // can't do addition inside the constant expression, so we much combine the |
1742 | // segments into a single one that can live at `__memory_base`. |
1743 | if (ctx.isPic && !config->extendedConst && !config->sharedMemory) { |
1744 | // In shared memory mode all data segments are passive and initialized |
1745 | // via __wasm_init_memory. |
1746 | log(msg: "-- combineOutputSegments" ); |
1747 | combineOutputSegments(); |
1748 | } |
1749 | |
1750 | log(msg: "-- createSyntheticSectionsPostLayout" ); |
1751 | createSyntheticSectionsPostLayout(); |
1752 | log(msg: "-- populateProducers" ); |
1753 | populateProducers(); |
1754 | log(msg: "-- calculateImports" ); |
1755 | calculateImports(); |
1756 | log(msg: "-- scanRelocations" ); |
1757 | scanRelocations(); |
1758 | log(msg: "-- finalizeIndirectFunctionTable" ); |
1759 | finalizeIndirectFunctionTable(); |
1760 | log(msg: "-- createSyntheticInitFunctions" ); |
1761 | createSyntheticInitFunctions(); |
1762 | log(msg: "-- assignIndexes" ); |
1763 | assignIndexes(); |
1764 | log(msg: "-- calculateInitFunctions" ); |
1765 | calculateInitFunctions(); |
1766 | |
1767 | if (!config->relocatable) { |
1768 | // Create linker synthesized functions |
1769 | if (WasmSym::applyDataRelocs) |
1770 | createApplyDataRelocationsFunction(); |
1771 | if (WasmSym::applyGlobalRelocs) |
1772 | createApplyGlobalRelocationsFunction(); |
1773 | if (WasmSym::applyTLSRelocs) |
1774 | createApplyTLSRelocationsFunction(); |
1775 | if (WasmSym::applyGlobalTLSRelocs) |
1776 | createApplyGlobalTLSRelocationsFunction(); |
1777 | if (WasmSym::initMemory) |
1778 | createInitMemoryFunction(); |
1779 | createStartFunction(); |
1780 | |
1781 | createCallCtorsFunction(); |
1782 | |
1783 | // Create export wrappers for commands if needed. |
1784 | // |
1785 | // If the input contains a call to `__wasm_call_ctors`, either in one of |
1786 | // the input objects or an explicit export from the command-line, we |
1787 | // assume ctors and dtors are taken care of already. |
1788 | if (!config->relocatable && !ctx.isPic && |
1789 | !WasmSym::callCtors->isUsedInRegularObj && |
1790 | !WasmSym::callCtors->isExported()) { |
1791 | log(msg: "-- createCommandExportWrappers" ); |
1792 | createCommandExportWrappers(); |
1793 | } |
1794 | } |
1795 | |
1796 | if (WasmSym::initTLS && WasmSym::initTLS->isLive()) { |
1797 | log(msg: "-- createInitTLSFunction" ); |
1798 | createInitTLSFunction(); |
1799 | } |
1800 | |
1801 | if (errorCount()) |
1802 | return; |
1803 | |
1804 | log(msg: "-- calculateTypes" ); |
1805 | calculateTypes(); |
1806 | log(msg: "-- calculateExports" ); |
1807 | calculateExports(); |
1808 | log(msg: "-- calculateCustomSections" ); |
1809 | calculateCustomSections(); |
1810 | log(msg: "-- populateSymtab" ); |
1811 | populateSymtab(); |
1812 | log(msg: "-- checkImportExportTargetFeatures" ); |
1813 | checkImportExportTargetFeatures(); |
1814 | log(msg: "-- addSections" ); |
1815 | addSections(); |
1816 | |
1817 | if (errorHandler().verbose) { |
1818 | log(msg: "Defined Functions: " + Twine(out.functionSec->inputFunctions.size())); |
1819 | log(msg: "Defined Globals : " + Twine(out.globalSec->numGlobals())); |
1820 | log(msg: "Defined Tags : " + Twine(out.tagSec->inputTags.size())); |
1821 | log(msg: "Defined Tables : " + Twine(out.tableSec->inputTables.size())); |
1822 | log(msg: "Function Imports : " + |
1823 | Twine(out.importSec->getNumImportedFunctions())); |
1824 | log(msg: "Global Imports : " + Twine(out.importSec->getNumImportedGlobals())); |
1825 | log(msg: "Tag Imports : " + Twine(out.importSec->getNumImportedTags())); |
1826 | log(msg: "Table Imports : " + Twine(out.importSec->getNumImportedTables())); |
1827 | } |
1828 | |
1829 | createHeader(); |
1830 | log(msg: "-- finalizeSections" ); |
1831 | finalizeSections(); |
1832 | |
1833 | log(msg: "-- writeMapFile" ); |
1834 | writeMapFile(outputSections); |
1835 | |
1836 | log(msg: "-- openFile" ); |
1837 | openFile(); |
1838 | if (errorCount()) |
1839 | return; |
1840 | |
1841 | writeHeader(); |
1842 | |
1843 | log(msg: "-- writeSections" ); |
1844 | writeSections(); |
1845 | writeBuildId(); |
1846 | if (errorCount()) |
1847 | return; |
1848 | |
1849 | if (Error e = buffer->commit()) |
1850 | fatal(msg: "failed to write output '" + buffer->getPath() + |
1851 | "': " + toString(E: std::move(e))); |
1852 | } |
1853 | |
1854 | // Open a result file. |
1855 | void Writer::openFile() { |
1856 | log(msg: "writing: " + config->outputFile); |
1857 | |
1858 | Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr = |
1859 | FileOutputBuffer::create(FilePath: config->outputFile, Size: fileSize, |
1860 | Flags: FileOutputBuffer::F_executable); |
1861 | |
1862 | if (!bufferOrErr) |
1863 | error(msg: "failed to open " + config->outputFile + ": " + |
1864 | toString(E: bufferOrErr.takeError())); |
1865 | else |
1866 | buffer = std::move(*bufferOrErr); |
1867 | } |
1868 | |
1869 | void Writer::() { |
1870 | raw_string_ostream os(header); |
1871 | writeBytes(os, bytes: WasmMagic, count: sizeof(WasmMagic), msg: "wasm magic" ); |
1872 | writeU32(os, number: WasmVersion, msg: "wasm version" ); |
1873 | os.flush(); |
1874 | fileSize += header.size(); |
1875 | } |
1876 | |
1877 | void writeResult() { Writer().run(); } |
1878 | |
1879 | } // namespace wasm::lld |
1880 | |