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