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