1 | //===- InputChunks.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 "InputChunks.h" |
10 | #include "Config.h" |
11 | #include "OutputSegment.h" |
12 | #include "WriterUtils.h" |
13 | #include "lld/Common/ErrorHandler.h" |
14 | #include "lld/Common/LLVM.h" |
15 | #include "llvm/Support/LEB128.h" |
16 | #include "llvm/Support/xxhash.h" |
17 | #include <algorithm> |
18 | |
19 | #define DEBUG_TYPE "lld" |
20 | |
21 | using namespace llvm; |
22 | using namespace llvm::wasm; |
23 | using namespace llvm::support::endian; |
24 | |
25 | namespace lld { |
26 | StringRef relocTypeToString(uint8_t relocType) { |
27 | switch (relocType) { |
28 | #define WASM_RELOC(NAME, REL) \ |
29 | case REL: \ |
30 | return #NAME; |
31 | #include "llvm/BinaryFormat/WasmRelocs.def" |
32 | #undef WASM_RELOC |
33 | } |
34 | llvm_unreachable("unknown reloc type" ); |
35 | } |
36 | |
37 | bool relocIs64(uint8_t relocType) { |
38 | switch (relocType) { |
39 | case R_WASM_MEMORY_ADDR_LEB64: |
40 | case R_WASM_MEMORY_ADDR_SLEB64: |
41 | case R_WASM_MEMORY_ADDR_REL_SLEB64: |
42 | case R_WASM_MEMORY_ADDR_I64: |
43 | case R_WASM_TABLE_INDEX_SLEB64: |
44 | case R_WASM_TABLE_INDEX_I64: |
45 | case R_WASM_FUNCTION_OFFSET_I64: |
46 | case R_WASM_TABLE_INDEX_REL_SLEB64: |
47 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: |
48 | return true; |
49 | default: |
50 | return false; |
51 | } |
52 | } |
53 | |
54 | std::string toString(const wasm::InputChunk *c) { |
55 | return (toString(file: c->file) + ":(" + c->name + ")" ).str(); |
56 | } |
57 | |
58 | namespace wasm { |
59 | StringRef InputChunk::getComdatName() const { |
60 | uint32_t index = getComdat(); |
61 | if (index == UINT32_MAX) |
62 | return StringRef(); |
63 | return file->getWasmObj()->linkingData().Comdats[index]; |
64 | } |
65 | |
66 | uint32_t InputChunk::getSize() const { |
67 | if (const auto *ms = dyn_cast<SyntheticMergedChunk>(Val: this)) |
68 | return ms->builder.getSize(); |
69 | |
70 | if (const auto *f = dyn_cast<InputFunction>(Val: this)) { |
71 | if (ctx.arg.compressRelocations && f->file) { |
72 | return f->getCompressedSize(); |
73 | } |
74 | } |
75 | |
76 | return data().size(); |
77 | } |
78 | |
79 | uint32_t InputChunk::getInputSize() const { |
80 | if (const auto *f = dyn_cast<InputFunction>(Val: this)) |
81 | return f->function->Size; |
82 | return getSize(); |
83 | } |
84 | |
85 | // Copy this input chunk to an mmap'ed output file and apply relocations. |
86 | void InputChunk::writeTo(uint8_t *buf) const { |
87 | if (const auto *f = dyn_cast<InputFunction>(Val: this)) { |
88 | if (file && ctx.arg.compressRelocations) |
89 | return f->writeCompressed(buf); |
90 | } else if (const auto *ms = dyn_cast<SyntheticMergedChunk>(Val: this)) { |
91 | ms->builder.write(Buf: buf + outSecOff); |
92 | // Apply relocations |
93 | ms->relocate(buf: buf + outSecOff); |
94 | return; |
95 | } |
96 | |
97 | // Copy contents |
98 | memcpy(dest: buf + outSecOff, src: data().data(), n: data().size()); |
99 | |
100 | // Apply relocations |
101 | relocate(buf: buf + outSecOff); |
102 | } |
103 | |
104 | void InputChunk::relocate(uint8_t *buf) const { |
105 | if (relocations.empty()) |
106 | return; |
107 | |
108 | LLVM_DEBUG(dbgs() << "applying relocations: " << toString(this) |
109 | << " count=" << relocations.size() << "\n" ); |
110 | int32_t inputSectionOffset = getInputSectionOffset(); |
111 | uint64_t tombstone = getTombstone(); |
112 | |
113 | for (const WasmRelocation &rel : relocations) { |
114 | uint8_t *loc = buf + rel.Offset - inputSectionOffset; |
115 | LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type)); |
116 | if (rel.Type != R_WASM_TYPE_INDEX_LEB) |
117 | LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName()); |
118 | LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index |
119 | << " offset=" << rel.Offset << "\n" ); |
120 | // TODO(sbc): Check that the value is within the range of the |
121 | // relocation type below. Most likely we must error out here |
122 | // if its not with range. |
123 | uint64_t value = file->calcNewValue(reloc: rel, tombstone, chunk: this); |
124 | |
125 | switch (rel.Type) { |
126 | case R_WASM_TYPE_INDEX_LEB: |
127 | case R_WASM_FUNCTION_INDEX_LEB: |
128 | case R_WASM_GLOBAL_INDEX_LEB: |
129 | case R_WASM_TAG_INDEX_LEB: |
130 | case R_WASM_MEMORY_ADDR_LEB: |
131 | case R_WASM_TABLE_NUMBER_LEB: |
132 | encodeULEB128(Value: static_cast<uint32_t>(value), p: loc, PadTo: 5); |
133 | break; |
134 | case R_WASM_MEMORY_ADDR_LEB64: |
135 | encodeULEB128(Value: value, p: loc, PadTo: 10); |
136 | break; |
137 | case R_WASM_TABLE_INDEX_SLEB: |
138 | case R_WASM_TABLE_INDEX_REL_SLEB: |
139 | case R_WASM_MEMORY_ADDR_SLEB: |
140 | case R_WASM_MEMORY_ADDR_REL_SLEB: |
141 | case R_WASM_MEMORY_ADDR_TLS_SLEB: |
142 | encodeSLEB128(Value: static_cast<int32_t>(value), p: loc, PadTo: 5); |
143 | break; |
144 | case R_WASM_TABLE_INDEX_SLEB64: |
145 | case R_WASM_TABLE_INDEX_REL_SLEB64: |
146 | case R_WASM_MEMORY_ADDR_SLEB64: |
147 | case R_WASM_MEMORY_ADDR_REL_SLEB64: |
148 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: |
149 | encodeSLEB128(Value: static_cast<int64_t>(value), p: loc, PadTo: 10); |
150 | break; |
151 | case R_WASM_TABLE_INDEX_I32: |
152 | case R_WASM_MEMORY_ADDR_I32: |
153 | case R_WASM_FUNCTION_OFFSET_I32: |
154 | case R_WASM_FUNCTION_INDEX_I32: |
155 | case R_WASM_SECTION_OFFSET_I32: |
156 | case R_WASM_GLOBAL_INDEX_I32: |
157 | case R_WASM_MEMORY_ADDR_LOCREL_I32: |
158 | write32le(P: loc, V: value); |
159 | break; |
160 | case R_WASM_TABLE_INDEX_I64: |
161 | case R_WASM_MEMORY_ADDR_I64: |
162 | case R_WASM_FUNCTION_OFFSET_I64: |
163 | write64le(P: loc, V: value); |
164 | break; |
165 | default: |
166 | llvm_unreachable("unknown relocation type" ); |
167 | } |
168 | } |
169 | } |
170 | |
171 | static bool relocIsLive(const WasmRelocation &rel, ObjFile *file) { |
172 | return rel.Type == R_WASM_TYPE_INDEX_LEB || |
173 | file->getSymbol(index: rel.Index)->isLive(); |
174 | } |
175 | |
176 | size_t InputChunk::getNumLiveRelocations() const { |
177 | return llvm::count_if(Range: relocations, P: [this](const WasmRelocation &rel) { |
178 | return relocIsLive(rel, file); |
179 | }); |
180 | } |
181 | |
182 | // Copy relocation entries to a given output stream. |
183 | // This function is used only when a user passes "-r". For a regular link, |
184 | // we consume relocations instead of copying them to an output file. |
185 | void InputChunk::writeRelocations(raw_ostream &os) const { |
186 | if (relocations.empty()) |
187 | return; |
188 | |
189 | int32_t off = outSecOff - getInputSectionOffset(); |
190 | LLVM_DEBUG(dbgs() << "writeRelocations: " << file->getName() |
191 | << " offset=" << Twine(off) << "\n" ); |
192 | |
193 | for (const WasmRelocation &rel : relocations) { |
194 | if (!relocIsLive(rel, file)) |
195 | continue; |
196 | writeUleb128(os, number: rel.Type, msg: "reloc type" ); |
197 | writeUleb128(os, number: rel.Offset + off, msg: "reloc offset" ); |
198 | writeUleb128(os, number: file->calcNewIndex(reloc: rel), msg: "reloc index" ); |
199 | |
200 | if (relocTypeHasAddend(type: rel.Type)) |
201 | writeSleb128(os, number: file->calcNewAddend(reloc: rel), msg: "reloc addend" ); |
202 | } |
203 | } |
204 | |
205 | uint64_t InputChunk::getTombstone() const { |
206 | if (const auto *s = dyn_cast<InputSection>(Val: this)) { |
207 | return s->tombstoneValue; |
208 | } |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | void InputFunction::setFunctionIndex(uint32_t index) { |
214 | LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << name << " -> " |
215 | << index << "\n" ); |
216 | assert(!hasFunctionIndex()); |
217 | functionIndex = index; |
218 | } |
219 | |
220 | void InputFunction::setTableIndex(uint32_t index) { |
221 | LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << name << " -> " |
222 | << index << "\n" ); |
223 | assert(!hasTableIndex()); |
224 | tableIndex = index; |
225 | } |
226 | |
227 | // Write a relocation value without padding and return the number of bytes |
228 | // witten. |
229 | static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel, |
230 | uint64_t value) { |
231 | switch (rel.getType()) { |
232 | case R_WASM_TYPE_INDEX_LEB: |
233 | case R_WASM_FUNCTION_INDEX_LEB: |
234 | case R_WASM_GLOBAL_INDEX_LEB: |
235 | case R_WASM_TAG_INDEX_LEB: |
236 | case R_WASM_MEMORY_ADDR_LEB: |
237 | case R_WASM_MEMORY_ADDR_LEB64: |
238 | case R_WASM_TABLE_NUMBER_LEB: |
239 | return encodeULEB128(Value: value, p: buf); |
240 | case R_WASM_TABLE_INDEX_SLEB: |
241 | case R_WASM_TABLE_INDEX_SLEB64: |
242 | case R_WASM_TABLE_INDEX_REL_SLEB64: |
243 | case R_WASM_MEMORY_ADDR_SLEB: |
244 | case R_WASM_MEMORY_ADDR_SLEB64: |
245 | case R_WASM_MEMORY_ADDR_REL_SLEB: |
246 | case R_WASM_MEMORY_ADDR_REL_SLEB64: |
247 | case R_WASM_MEMORY_ADDR_TLS_SLEB: |
248 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: |
249 | case R_WASM_TABLE_INDEX_REL_SLEB: |
250 | return encodeSLEB128(Value: static_cast<int64_t>(value), p: buf); |
251 | case R_WASM_TABLE_INDEX_I32: |
252 | case R_WASM_MEMORY_ADDR_I32: |
253 | case R_WASM_FUNCTION_OFFSET_I32: |
254 | case R_WASM_SECTION_OFFSET_I32: |
255 | case R_WASM_GLOBAL_INDEX_I32: |
256 | case R_WASM_MEMORY_ADDR_I64: |
257 | case R_WASM_TABLE_INDEX_I64: |
258 | case R_WASM_FUNCTION_OFFSET_I64: |
259 | case R_WASM_MEMORY_ADDR_LOCREL_I32: |
260 | case R_WASM_FUNCTION_INDEX_I32: |
261 | fatal(msg: "relocation compression not supported for " + |
262 | relocTypeToString(relocType: rel.Type)); |
263 | } |
264 | llvm_unreachable("unhandled relocation type" ); |
265 | } |
266 | |
267 | static unsigned getRelocWidthPadded(const WasmRelocation &rel) { |
268 | switch (rel.getType()) { |
269 | case R_WASM_TYPE_INDEX_LEB: |
270 | case R_WASM_FUNCTION_INDEX_LEB: |
271 | case R_WASM_GLOBAL_INDEX_LEB: |
272 | case R_WASM_TAG_INDEX_LEB: |
273 | case R_WASM_MEMORY_ADDR_LEB: |
274 | case R_WASM_TABLE_NUMBER_LEB: |
275 | case R_WASM_TABLE_INDEX_SLEB: |
276 | case R_WASM_TABLE_INDEX_REL_SLEB: |
277 | case R_WASM_MEMORY_ADDR_SLEB: |
278 | case R_WASM_MEMORY_ADDR_REL_SLEB: |
279 | case R_WASM_MEMORY_ADDR_TLS_SLEB: |
280 | return 5; |
281 | case R_WASM_TABLE_INDEX_SLEB64: |
282 | case R_WASM_TABLE_INDEX_REL_SLEB64: |
283 | case R_WASM_MEMORY_ADDR_LEB64: |
284 | case R_WASM_MEMORY_ADDR_SLEB64: |
285 | case R_WASM_MEMORY_ADDR_REL_SLEB64: |
286 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: |
287 | return 10; |
288 | case R_WASM_TABLE_INDEX_I32: |
289 | case R_WASM_MEMORY_ADDR_I32: |
290 | case R_WASM_FUNCTION_OFFSET_I32: |
291 | case R_WASM_SECTION_OFFSET_I32: |
292 | case R_WASM_GLOBAL_INDEX_I32: |
293 | case R_WASM_MEMORY_ADDR_I64: |
294 | case R_WASM_TABLE_INDEX_I64: |
295 | case R_WASM_FUNCTION_OFFSET_I64: |
296 | case R_WASM_MEMORY_ADDR_LOCREL_I32: |
297 | case R_WASM_FUNCTION_INDEX_I32: |
298 | fatal(msg: "relocation compression not supported for " + |
299 | relocTypeToString(relocType: rel.Type)); |
300 | } |
301 | llvm_unreachable("unhandled relocation type" ); |
302 | } |
303 | |
304 | static unsigned getRelocWidth(const WasmRelocation &rel, uint64_t value) { |
305 | uint8_t buf[10]; |
306 | return writeCompressedReloc(buf, rel, value); |
307 | } |
308 | |
309 | // Relocations of type LEB and SLEB in the code section are padded to 5 bytes |
310 | // so that a fast linker can blindly overwrite them without needing to worry |
311 | // about the number of bytes needed to encode the values. |
312 | // However, for optimal output the code section can be compressed to remove |
313 | // the padding then outputting non-relocatable files. |
314 | // In this case we need to perform a size calculation based on the value at each |
315 | // relocation. At best we end up saving 4 bytes for each relocation entry. |
316 | // |
317 | // This function only computes the final output size. It must be called |
318 | // before getSize() is used to calculate of layout of the code section. |
319 | void InputFunction::calculateSize() { |
320 | if (!file || !ctx.arg.compressRelocations) |
321 | return; |
322 | |
323 | LLVM_DEBUG(dbgs() << "calculateSize: " << name << "\n" ); |
324 | |
325 | const uint8_t *secStart = file->codeSection->Content.data(); |
326 | const uint8_t *funcStart = secStart + getInputSectionOffset(); |
327 | uint32_t functionSizeLength; |
328 | decodeULEB128(p: funcStart, n: &functionSizeLength); |
329 | |
330 | uint32_t start = getInputSectionOffset(); |
331 | uint32_t end = start + function->Size; |
332 | |
333 | uint64_t tombstone = getTombstone(); |
334 | |
335 | uint32_t lastRelocEnd = start + functionSizeLength; |
336 | for (const WasmRelocation &rel : relocations) { |
337 | LLVM_DEBUG(dbgs() << " region: " << (rel.Offset - lastRelocEnd) << "\n" ); |
338 | compressedFuncSize += rel.Offset - lastRelocEnd; |
339 | compressedFuncSize += |
340 | getRelocWidth(rel, value: file->calcNewValue(reloc: rel, tombstone, chunk: this)); |
341 | lastRelocEnd = rel.Offset + getRelocWidthPadded(rel); |
342 | } |
343 | LLVM_DEBUG(dbgs() << " final region: " << (end - lastRelocEnd) << "\n" ); |
344 | compressedFuncSize += end - lastRelocEnd; |
345 | |
346 | // Now we know how long the resulting function is we can add the encoding |
347 | // of its length |
348 | uint8_t buf[5]; |
349 | compressedSize = compressedFuncSize + encodeULEB128(Value: compressedFuncSize, p: buf); |
350 | |
351 | LLVM_DEBUG(dbgs() << " calculateSize orig: " << function->Size << "\n" ); |
352 | LLVM_DEBUG(dbgs() << " calculateSize new: " << compressedSize << "\n" ); |
353 | } |
354 | |
355 | // Override the default writeTo method so that we can (optionally) write the |
356 | // compressed version of the function. |
357 | void InputFunction::writeCompressed(uint8_t *buf) const { |
358 | buf += outSecOff; |
359 | uint8_t *orig = buf; |
360 | (void)orig; |
361 | |
362 | const uint8_t *secStart = file->codeSection->Content.data(); |
363 | const uint8_t *funcStart = secStart + getInputSectionOffset(); |
364 | const uint8_t *end = funcStart + function->Size; |
365 | uint64_t tombstone = getTombstone(); |
366 | uint32_t count; |
367 | decodeULEB128(p: funcStart, n: &count); |
368 | funcStart += count; |
369 | |
370 | LLVM_DEBUG(dbgs() << "write func: " << name << "\n" ); |
371 | buf += encodeULEB128(Value: compressedFuncSize, p: buf); |
372 | const uint8_t *lastRelocEnd = funcStart; |
373 | for (const WasmRelocation &rel : relocations) { |
374 | unsigned chunkSize = (secStart + rel.Offset) - lastRelocEnd; |
375 | LLVM_DEBUG(dbgs() << " write chunk: " << chunkSize << "\n" ); |
376 | memcpy(dest: buf, src: lastRelocEnd, n: chunkSize); |
377 | buf += chunkSize; |
378 | buf += writeCompressedReloc(buf, rel, |
379 | value: file->calcNewValue(reloc: rel, tombstone, chunk: this)); |
380 | lastRelocEnd = secStart + rel.Offset + getRelocWidthPadded(rel); |
381 | } |
382 | |
383 | unsigned chunkSize = end - lastRelocEnd; |
384 | LLVM_DEBUG(dbgs() << " write final chunk: " << chunkSize << "\n" ); |
385 | memcpy(dest: buf, src: lastRelocEnd, n: chunkSize); |
386 | LLVM_DEBUG(dbgs() << " total: " << (buf + chunkSize - orig) << "\n" ); |
387 | } |
388 | |
389 | uint64_t InputChunk::getChunkOffset(uint64_t offset) const { |
390 | if (const auto *ms = dyn_cast<MergeInputChunk>(Val: this)) { |
391 | LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n" ); |
392 | LLVM_DEBUG(dbgs() << "offset: " << offset << "\n" ); |
393 | LLVM_DEBUG(dbgs() << "parentOffset: " << ms->getParentOffset(offset) |
394 | << "\n" ); |
395 | assert(ms->parent); |
396 | return ms->parent->getChunkOffset(offset: ms->getParentOffset(offset)); |
397 | } |
398 | return outputSegmentOffset + offset; |
399 | } |
400 | |
401 | uint64_t InputChunk::getOffset(uint64_t offset) const { |
402 | return outSecOff + getChunkOffset(offset); |
403 | } |
404 | |
405 | uint64_t InputChunk::getVA(uint64_t offset) const { |
406 | return (outputSeg ? outputSeg->startVA : 0) + getChunkOffset(offset); |
407 | } |
408 | |
409 | // Generate code to apply relocations to the data section at runtime. |
410 | // This is only called when generating shared libraries (PIC) where address are |
411 | // not known at static link time. |
412 | bool InputChunk::generateRelocationCode(raw_ostream &os) const { |
413 | LLVM_DEBUG(dbgs() << "generating runtime relocations: " << name |
414 | << " count=" << relocations.size() << "\n" ); |
415 | |
416 | bool is64 = ctx.arg.is64.value_or(u: false); |
417 | bool generated = false; |
418 | unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST |
419 | : WASM_OPCODE_I32_CONST; |
420 | unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD |
421 | : WASM_OPCODE_I32_ADD; |
422 | |
423 | uint64_t tombstone = getTombstone(); |
424 | // TODO(sbc): Encode the relocations in the data section and write a loop |
425 | // here to apply them. |
426 | for (const WasmRelocation &rel : relocations) { |
427 | uint64_t offset = getVA(offset: rel.Offset) - getInputSectionOffset(); |
428 | |
429 | Symbol *sym = file->getSymbol(reloc: rel); |
430 | // Runtime relocations are needed when we don't know the address of |
431 | // a symbol statically. |
432 | bool requiresRuntimeReloc = ctx.isPic || sym->hasGOTIndex(); |
433 | if (!requiresRuntimeReloc) |
434 | continue; |
435 | |
436 | LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type) |
437 | << " addend=" << rel.Addend << " index=" << rel.Index |
438 | << " output offset=" << offset << "\n" ); |
439 | |
440 | // Calculate the address at which to apply the relocation |
441 | writeU8(os, byte: opcode_ptr_const, msg: "CONST" ); |
442 | writeSleb128(os, number: offset, msg: "offset" ); |
443 | |
444 | // In PIC mode we need to add the __memory_base |
445 | if (ctx.isPic) { |
446 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET" ); |
447 | if (isTLS()) |
448 | writeUleb128(os, number: ctx.sym.tlsBase->getGlobalIndex(), msg: "tls_base" ); |
449 | else |
450 | writeUleb128(os, number: ctx.sym.memoryBase->getGlobalIndex(), msg: "memory_base" ); |
451 | writeU8(os, byte: opcode_ptr_add, msg: "ADD" ); |
452 | } |
453 | |
454 | // Now figure out what we want to store at this location |
455 | bool is64 = relocIs64(relocType: rel.Type); |
456 | unsigned opcode_reloc_const = |
457 | is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST; |
458 | unsigned opcode_reloc_add = |
459 | is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD; |
460 | unsigned opcode_reloc_store = |
461 | is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE; |
462 | |
463 | if (sym->hasGOTIndex()) { |
464 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET" ); |
465 | writeUleb128(os, number: sym->getGOTIndex(), msg: "global index" ); |
466 | if (rel.Addend) { |
467 | writeU8(os, byte: opcode_reloc_const, msg: "CONST" ); |
468 | writeSleb128(os, number: rel.Addend, msg: "addend" ); |
469 | writeU8(os, byte: opcode_reloc_add, msg: "ADD" ); |
470 | } |
471 | } else { |
472 | assert(ctx.isPic); |
473 | const GlobalSymbol *baseSymbol = ctx.sym.memoryBase; |
474 | if (rel.Type == R_WASM_TABLE_INDEX_I32 || |
475 | rel.Type == R_WASM_TABLE_INDEX_I64) |
476 | baseSymbol = ctx.sym.tableBase; |
477 | else if (sym->isTLS()) |
478 | baseSymbol = ctx.sym.tlsBase; |
479 | writeU8(os, byte: WASM_OPCODE_GLOBAL_GET, msg: "GLOBAL_GET" ); |
480 | writeUleb128(os, number: baseSymbol->getGlobalIndex(), msg: "base" ); |
481 | writeU8(os, byte: opcode_reloc_const, msg: "CONST" ); |
482 | writeSleb128(os, number: file->calcNewValue(reloc: rel, tombstone, chunk: this), msg: "offset" ); |
483 | writeU8(os, byte: opcode_reloc_add, msg: "ADD" ); |
484 | } |
485 | |
486 | // Store that value at the virtual address |
487 | writeU8(os, byte: opcode_reloc_store, msg: "I32_STORE" ); |
488 | writeUleb128(os, number: 2, msg: "align" ); |
489 | writeUleb128(os, number: 0, msg: "offset" ); |
490 | generated = true; |
491 | } |
492 | return generated; |
493 | } |
494 | |
495 | // Split WASM_SEG_FLAG_STRINGS section. Such a section is a sequence of |
496 | // null-terminated strings. |
497 | void MergeInputChunk::splitStrings(ArrayRef<uint8_t> data) { |
498 | LLVM_DEBUG(llvm::dbgs() << "splitStrings\n" ); |
499 | size_t off = 0; |
500 | StringRef s = toStringRef(Input: data); |
501 | |
502 | while (!s.empty()) { |
503 | size_t end = s.find(C: 0); |
504 | if (end == StringRef::npos) |
505 | fatal(msg: toString(c: this) + ": string is not null terminated" ); |
506 | size_t size = end + 1; |
507 | |
508 | pieces.emplace_back(args&: off, args: xxh3_64bits(data: s.substr(Start: 0, N: size)), args: true); |
509 | s = s.substr(Start: size); |
510 | off += size; |
511 | } |
512 | } |
513 | |
514 | // This function is called after we obtain a complete list of input sections |
515 | // that need to be linked. This is responsible to split section contents |
516 | // into small chunks for further processing. |
517 | // |
518 | // Note that this function is called from parallelForEach. This must be |
519 | // thread-safe (i.e. no memory allocation from the pools). |
520 | void MergeInputChunk::splitIntoPieces() { |
521 | assert(pieces.empty()); |
522 | // As of now we only support WASM_SEG_FLAG_STRINGS but in the future we |
523 | // could add other types of splitting (see ELF's splitIntoPieces). |
524 | assert(flags & WASM_SEG_FLAG_STRINGS); |
525 | splitStrings(data: data()); |
526 | } |
527 | |
528 | SectionPiece *MergeInputChunk::getSectionPiece(uint64_t offset) { |
529 | if (this->data().size() <= offset) |
530 | fatal(msg: toString(c: this) + ": offset is outside the section" ); |
531 | |
532 | // If Offset is not at beginning of a section piece, it is not in the map. |
533 | // In that case we need to do a binary search of the original section piece |
534 | // vector. |
535 | auto it = partition_point( |
536 | Range&: pieces, P: [=](SectionPiece p) { return p.inputOff <= offset; }); |
537 | return &it[-1]; |
538 | } |
539 | |
540 | // Returns the offset in an output section for a given input offset. |
541 | // Because contents of a mergeable section is not contiguous in output, |
542 | // it is not just an addition to a base output offset. |
543 | uint64_t MergeInputChunk::getParentOffset(uint64_t offset) const { |
544 | // If Offset is not at beginning of a section piece, it is not in the map. |
545 | // In that case we need to search from the original section piece vector. |
546 | const SectionPiece *piece = getSectionPiece(offset); |
547 | uint64_t addend = offset - piece->inputOff; |
548 | return piece->outputOff + addend; |
549 | } |
550 | |
551 | void SyntheticMergedChunk::finalizeContents() { |
552 | // Add all string pieces to the string table builder to create section |
553 | // contents. |
554 | for (MergeInputChunk *sec : chunks) |
555 | for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) |
556 | if (sec->pieces[i].live) |
557 | builder.add(S: sec->getData(i)); |
558 | |
559 | // Fix the string table content. After this, the contents will never change. |
560 | builder.finalize(); |
561 | |
562 | // finalize() fixed tail-optimized strings, so we can now get |
563 | // offsets of strings. Get an offset for each string and save it |
564 | // to a corresponding SectionPiece for easy access. |
565 | for (MergeInputChunk *sec : chunks) |
566 | for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) |
567 | if (sec->pieces[i].live) |
568 | sec->pieces[i].outputOff = builder.getOffset(S: sec->getData(i)); |
569 | } |
570 | |
571 | uint64_t InputSection::getTombstoneForSection(StringRef name) { |
572 | // When a function is not live we need to update relocations referring to it. |
573 | // If they occur in DWARF debug symbols, we want to change the pc of the |
574 | // function to -1 to avoid overlapping with a valid range. However for the |
575 | // debug_ranges and debug_loc sections that would conflict with the existing |
576 | // meaning of -1 so we use -2. |
577 | if (name == ".debug_ranges" || name == ".debug_loc" ) |
578 | return UINT64_C(-2); |
579 | if (name.starts_with(Prefix: ".debug_" )) |
580 | return UINT64_C(-1); |
581 | // If the function occurs in an function attribute section change it to -1 since |
582 | // 0 is a valid function index. |
583 | if (name.starts_with(Prefix: "llvm.func_attr." )) |
584 | return UINT64_C(-1); |
585 | // Returning 0 means there is no tombstone value for this section, and relocation |
586 | // will just use the addend. |
587 | return 0; |
588 | } |
589 | |
590 | } // namespace wasm |
591 | } // namespace lld |
592 | |