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 write64le(P: loc, V: value);
164 break;
165 default:
166 llvm_unreachable("unknown relocation type");
167 }
168 }
169}
170
171static 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
176size_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.
185void 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
205uint64_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
213void InputFunction::setFunctionIndex(uint32_t index) {
214 LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << name << " -> "
215 << index << "\n");
216 assert(!hasFunctionIndex());
217 functionIndex = index;
218}
219
220void 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.
229static 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
267static 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
304static 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.
319void 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.
357void 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
389uint64_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
401uint64_t InputChunk::getOffset(uint64_t offset) const {
402 return outSecOff + getChunkOffset(offset);
403}
404
405uint64_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.
412bool 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.
497void 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).
520void 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
528SectionPiece *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.
543uint64_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
551void 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
571uint64_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