xref: /openbsd-src/gnu/llvm/lld/wasm/InputChunks.cpp (revision dfe94b169149f14cc1aee2cf6dad58a8d9a1860c)
1ece8a530Spatrick //===- InputChunks.cpp ----------------------------------------------------===//
2ece8a530Spatrick //
3ece8a530Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ece8a530Spatrick // See https://llvm.org/LICENSE.txt for license information.
5ece8a530Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ece8a530Spatrick //
7ece8a530Spatrick //===----------------------------------------------------------------------===//
8ece8a530Spatrick 
9ece8a530Spatrick #include "InputChunks.h"
10ece8a530Spatrick #include "Config.h"
11ece8a530Spatrick #include "OutputSegment.h"
12ece8a530Spatrick #include "WriterUtils.h"
13ece8a530Spatrick #include "lld/Common/ErrorHandler.h"
14ece8a530Spatrick #include "lld/Common/LLVM.h"
15ece8a530Spatrick #include "llvm/Support/LEB128.h"
161cf9926bSpatrick #include "llvm/Support/xxhash.h"
17ece8a530Spatrick 
18ece8a530Spatrick #define DEBUG_TYPE "lld"
19ece8a530Spatrick 
20ece8a530Spatrick using namespace llvm;
21ece8a530Spatrick using namespace llvm::wasm;
22ece8a530Spatrick using namespace llvm::support::endian;
23ece8a530Spatrick 
24ece8a530Spatrick namespace lld {
relocTypeToString(uint8_t relocType)25ece8a530Spatrick StringRef relocTypeToString(uint8_t relocType) {
26ece8a530Spatrick   switch (relocType) {
27ece8a530Spatrick #define WASM_RELOC(NAME, REL)                                                  \
28ece8a530Spatrick   case REL:                                                                    \
29ece8a530Spatrick     return #NAME;
30ece8a530Spatrick #include "llvm/BinaryFormat/WasmRelocs.def"
31ece8a530Spatrick #undef WASM_RELOC
32ece8a530Spatrick   }
33ece8a530Spatrick   llvm_unreachable("unknown reloc type");
34ece8a530Spatrick }
35ece8a530Spatrick 
relocIs64(uint8_t relocType)36bb684c34Spatrick bool relocIs64(uint8_t relocType) {
37bb684c34Spatrick   switch (relocType) {
38bb684c34Spatrick   case R_WASM_MEMORY_ADDR_LEB64:
39bb684c34Spatrick   case R_WASM_MEMORY_ADDR_SLEB64:
40bb684c34Spatrick   case R_WASM_MEMORY_ADDR_REL_SLEB64:
41bb684c34Spatrick   case R_WASM_MEMORY_ADDR_I64:
421cf9926bSpatrick   case R_WASM_TABLE_INDEX_SLEB64:
431cf9926bSpatrick   case R_WASM_TABLE_INDEX_I64:
441cf9926bSpatrick   case R_WASM_FUNCTION_OFFSET_I64:
451cf9926bSpatrick   case R_WASM_TABLE_INDEX_REL_SLEB64:
461cf9926bSpatrick   case R_WASM_MEMORY_ADDR_TLS_SLEB64:
47bb684c34Spatrick     return true;
48bb684c34Spatrick   default:
49bb684c34Spatrick     return false;
50bb684c34Spatrick   }
51bb684c34Spatrick }
52bb684c34Spatrick 
toString(const wasm::InputChunk * c)53ece8a530Spatrick std::string toString(const wasm::InputChunk *c) {
54*dfe94b16Srobert   return (toString(c->file) + ":(" + c->name + ")").str();
55ece8a530Spatrick }
56ece8a530Spatrick 
57ece8a530Spatrick namespace wasm {
getComdatName() const58ece8a530Spatrick StringRef InputChunk::getComdatName() const {
59ece8a530Spatrick   uint32_t index = getComdat();
60ece8a530Spatrick   if (index == UINT32_MAX)
61ece8a530Spatrick     return StringRef();
62ece8a530Spatrick   return file->getWasmObj()->linkingData().Comdats[index];
63ece8a530Spatrick }
64ece8a530Spatrick 
getSize() const651cf9926bSpatrick uint32_t InputChunk::getSize() const {
661cf9926bSpatrick   if (const auto *ms = dyn_cast<SyntheticMergedChunk>(this))
671cf9926bSpatrick     return ms->builder.getSize();
681cf9926bSpatrick 
691cf9926bSpatrick   if (const auto *f = dyn_cast<InputFunction>(this)) {
701cf9926bSpatrick     if (config->compressRelocations && f->file) {
711cf9926bSpatrick       return f->getCompressedSize();
721cf9926bSpatrick     }
73ece8a530Spatrick   }
74ece8a530Spatrick 
751cf9926bSpatrick   return data().size();
761cf9926bSpatrick }
77ece8a530Spatrick 
getInputSize() const781cf9926bSpatrick uint32_t InputChunk::getInputSize() const {
791cf9926bSpatrick   if (const auto *f = dyn_cast<InputFunction>(this))
801cf9926bSpatrick     return f->function->Size;
811cf9926bSpatrick   return getSize();
82ece8a530Spatrick }
83ece8a530Spatrick 
84ece8a530Spatrick // Copy this input chunk to an mmap'ed output file and apply relocations.
writeTo(uint8_t * buf) const85ece8a530Spatrick void InputChunk::writeTo(uint8_t *buf) const {
861cf9926bSpatrick   if (const auto *f = dyn_cast<InputFunction>(this)) {
871cf9926bSpatrick     if (file && config->compressRelocations)
881cf9926bSpatrick       return f->writeCompressed(buf);
891cf9926bSpatrick   } else if (const auto *ms = dyn_cast<SyntheticMergedChunk>(this)) {
901cf9926bSpatrick     ms->builder.write(buf + outSecOff);
911cf9926bSpatrick     // Apply relocations
921cf9926bSpatrick     ms->relocate(buf + outSecOff);
931cf9926bSpatrick     return;
941cf9926bSpatrick   }
951cf9926bSpatrick 
96ece8a530Spatrick   // Copy contents
971cf9926bSpatrick   memcpy(buf + outSecOff, data().data(), data().size());
98ece8a530Spatrick 
99ece8a530Spatrick   // Apply relocations
1001cf9926bSpatrick   relocate(buf + outSecOff);
1011cf9926bSpatrick }
1021cf9926bSpatrick 
relocate(uint8_t * buf) const1031cf9926bSpatrick void InputChunk::relocate(uint8_t *buf) const {
104ece8a530Spatrick   if (relocations.empty())
105ece8a530Spatrick     return;
106ece8a530Spatrick 
107ece8a530Spatrick   LLVM_DEBUG(dbgs() << "applying relocations: " << toString(this)
108ece8a530Spatrick                     << " count=" << relocations.size() << "\n");
1091cf9926bSpatrick   int32_t inputSectionOffset = getInputSectionOffset();
1101cf9926bSpatrick   uint64_t tombstone = getTombstone();
111ece8a530Spatrick 
112ece8a530Spatrick   for (const WasmRelocation &rel : relocations) {
1131cf9926bSpatrick     uint8_t *loc = buf + rel.Offset - inputSectionOffset;
114ece8a530Spatrick     LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type));
115ece8a530Spatrick     if (rel.Type != R_WASM_TYPE_INDEX_LEB)
116ece8a530Spatrick       LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName());
117ece8a530Spatrick     LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index
1181cf9926bSpatrick                       << " offset=" << rel.Offset << "\n");
119*dfe94b16Srobert     // TODO(sbc): Check that the value is within the range of the
120*dfe94b16Srobert     // relocation type below.  Most likely we must error out here
121*dfe94b16Srobert     // if its not with range.
122*dfe94b16Srobert     uint64_t value = file->calcNewValue(rel, tombstone, this);
123ece8a530Spatrick 
124ece8a530Spatrick     switch (rel.Type) {
125ece8a530Spatrick     case R_WASM_TYPE_INDEX_LEB:
126ece8a530Spatrick     case R_WASM_FUNCTION_INDEX_LEB:
127ece8a530Spatrick     case R_WASM_GLOBAL_INDEX_LEB:
1281cf9926bSpatrick     case R_WASM_TAG_INDEX_LEB:
129ece8a530Spatrick     case R_WASM_MEMORY_ADDR_LEB:
1301cf9926bSpatrick     case R_WASM_TABLE_NUMBER_LEB:
131*dfe94b16Srobert       encodeULEB128(static_cast<uint32_t>(value), loc, 5);
132ece8a530Spatrick       break;
133bb684c34Spatrick     case R_WASM_MEMORY_ADDR_LEB64:
134bb684c34Spatrick       encodeULEB128(value, loc, 10);
135bb684c34Spatrick       break;
136ece8a530Spatrick     case R_WASM_TABLE_INDEX_SLEB:
137ece8a530Spatrick     case R_WASM_TABLE_INDEX_REL_SLEB:
138ece8a530Spatrick     case R_WASM_MEMORY_ADDR_SLEB:
139ece8a530Spatrick     case R_WASM_MEMORY_ADDR_REL_SLEB:
1401cf9926bSpatrick     case R_WASM_MEMORY_ADDR_TLS_SLEB:
141ece8a530Spatrick       encodeSLEB128(static_cast<int32_t>(value), loc, 5);
142ece8a530Spatrick       break;
1431cf9926bSpatrick     case R_WASM_TABLE_INDEX_SLEB64:
1441cf9926bSpatrick     case R_WASM_TABLE_INDEX_REL_SLEB64:
145bb684c34Spatrick     case R_WASM_MEMORY_ADDR_SLEB64:
146bb684c34Spatrick     case R_WASM_MEMORY_ADDR_REL_SLEB64:
1471cf9926bSpatrick     case R_WASM_MEMORY_ADDR_TLS_SLEB64:
148bb684c34Spatrick       encodeSLEB128(static_cast<int64_t>(value), loc, 10);
149bb684c34Spatrick       break;
150ece8a530Spatrick     case R_WASM_TABLE_INDEX_I32:
151ece8a530Spatrick     case R_WASM_MEMORY_ADDR_I32:
152ece8a530Spatrick     case R_WASM_FUNCTION_OFFSET_I32:
153ece8a530Spatrick     case R_WASM_SECTION_OFFSET_I32:
154bb684c34Spatrick     case R_WASM_GLOBAL_INDEX_I32:
1551cf9926bSpatrick     case R_WASM_MEMORY_ADDR_LOCREL_I32:
156ece8a530Spatrick       write32le(loc, value);
157ece8a530Spatrick       break;
1581cf9926bSpatrick     case R_WASM_TABLE_INDEX_I64:
159bb684c34Spatrick     case R_WASM_MEMORY_ADDR_I64:
1601cf9926bSpatrick     case R_WASM_FUNCTION_OFFSET_I64:
161bb684c34Spatrick       write64le(loc, value);
162bb684c34Spatrick       break;
163ece8a530Spatrick     default:
164ece8a530Spatrick       llvm_unreachable("unknown relocation type");
165ece8a530Spatrick     }
166ece8a530Spatrick   }
167ece8a530Spatrick }
168ece8a530Spatrick 
169ece8a530Spatrick // Copy relocation entries to a given output stream.
170ece8a530Spatrick // This function is used only when a user passes "-r". For a regular link,
171ece8a530Spatrick // we consume relocations instead of copying them to an output file.
writeRelocations(raw_ostream & os) const172ece8a530Spatrick void InputChunk::writeRelocations(raw_ostream &os) const {
173ece8a530Spatrick   if (relocations.empty())
174ece8a530Spatrick     return;
175ece8a530Spatrick 
1761cf9926bSpatrick   int32_t off = outSecOff - getInputSectionOffset();
177ece8a530Spatrick   LLVM_DEBUG(dbgs() << "writeRelocations: " << file->getName()
178ece8a530Spatrick                     << " offset=" << Twine(off) << "\n");
179ece8a530Spatrick 
180ece8a530Spatrick   for (const WasmRelocation &rel : relocations) {
181ece8a530Spatrick     writeUleb128(os, rel.Type, "reloc type");
182ece8a530Spatrick     writeUleb128(os, rel.Offset + off, "reloc offset");
183ece8a530Spatrick     writeUleb128(os, file->calcNewIndex(rel), "reloc index");
184ece8a530Spatrick 
185ece8a530Spatrick     if (relocTypeHasAddend(rel.Type))
186ece8a530Spatrick       writeSleb128(os, file->calcNewAddend(rel), "reloc addend");
187ece8a530Spatrick   }
188ece8a530Spatrick }
189ece8a530Spatrick 
getTombstone() const1901cf9926bSpatrick uint64_t InputChunk::getTombstone() const {
1911cf9926bSpatrick   if (const auto *s = dyn_cast<InputSection>(this)) {
1921cf9926bSpatrick     return s->tombstoneValue;
1931cf9926bSpatrick   }
1941cf9926bSpatrick 
1951cf9926bSpatrick   return 0;
1961cf9926bSpatrick }
1971cf9926bSpatrick 
setFunctionIndex(uint32_t index)198ece8a530Spatrick void InputFunction::setFunctionIndex(uint32_t index) {
199*dfe94b16Srobert   LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << name << " -> "
200*dfe94b16Srobert                     << index << "\n");
201ece8a530Spatrick   assert(!hasFunctionIndex());
202ece8a530Spatrick   functionIndex = index;
203ece8a530Spatrick }
204ece8a530Spatrick 
setTableIndex(uint32_t index)205ece8a530Spatrick void InputFunction::setTableIndex(uint32_t index) {
206*dfe94b16Srobert   LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << name << " -> "
207ece8a530Spatrick                     << index << "\n");
208ece8a530Spatrick   assert(!hasTableIndex());
209ece8a530Spatrick   tableIndex = index;
210ece8a530Spatrick }
211ece8a530Spatrick 
212ece8a530Spatrick // Write a relocation value without padding and return the number of bytes
213ece8a530Spatrick // witten.
writeCompressedReloc(uint8_t * buf,const WasmRelocation & rel,uint64_t value)214ece8a530Spatrick static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel,
215bb684c34Spatrick                                      uint64_t value) {
216ece8a530Spatrick   switch (rel.Type) {
217ece8a530Spatrick   case R_WASM_TYPE_INDEX_LEB:
218ece8a530Spatrick   case R_WASM_FUNCTION_INDEX_LEB:
219ece8a530Spatrick   case R_WASM_GLOBAL_INDEX_LEB:
2201cf9926bSpatrick   case R_WASM_TAG_INDEX_LEB:
221ece8a530Spatrick   case R_WASM_MEMORY_ADDR_LEB:
222bb684c34Spatrick   case R_WASM_MEMORY_ADDR_LEB64:
2231cf9926bSpatrick   case R_WASM_TABLE_NUMBER_LEB:
224ece8a530Spatrick     return encodeULEB128(value, buf);
225ece8a530Spatrick   case R_WASM_TABLE_INDEX_SLEB:
2261cf9926bSpatrick   case R_WASM_TABLE_INDEX_SLEB64:
227ece8a530Spatrick   case R_WASM_MEMORY_ADDR_SLEB:
228bb684c34Spatrick   case R_WASM_MEMORY_ADDR_SLEB64:
229bb684c34Spatrick     return encodeSLEB128(static_cast<int64_t>(value), buf);
230ece8a530Spatrick   default:
231ece8a530Spatrick     llvm_unreachable("unexpected relocation type");
232ece8a530Spatrick   }
233ece8a530Spatrick }
234ece8a530Spatrick 
getRelocWidthPadded(const WasmRelocation & rel)235ece8a530Spatrick static unsigned getRelocWidthPadded(const WasmRelocation &rel) {
236ece8a530Spatrick   switch (rel.Type) {
237ece8a530Spatrick   case R_WASM_TYPE_INDEX_LEB:
238ece8a530Spatrick   case R_WASM_FUNCTION_INDEX_LEB:
239ece8a530Spatrick   case R_WASM_GLOBAL_INDEX_LEB:
2401cf9926bSpatrick   case R_WASM_TAG_INDEX_LEB:
241ece8a530Spatrick   case R_WASM_MEMORY_ADDR_LEB:
2421cf9926bSpatrick   case R_WASM_TABLE_NUMBER_LEB:
243ece8a530Spatrick   case R_WASM_TABLE_INDEX_SLEB:
244ece8a530Spatrick   case R_WASM_MEMORY_ADDR_SLEB:
245ece8a530Spatrick     return 5;
2461cf9926bSpatrick   case R_WASM_TABLE_INDEX_SLEB64:
247bb684c34Spatrick   case R_WASM_MEMORY_ADDR_LEB64:
248bb684c34Spatrick   case R_WASM_MEMORY_ADDR_SLEB64:
249bb684c34Spatrick     return 10;
250ece8a530Spatrick   default:
251ece8a530Spatrick     llvm_unreachable("unexpected relocation type");
252ece8a530Spatrick   }
253ece8a530Spatrick }
254ece8a530Spatrick 
getRelocWidth(const WasmRelocation & rel,uint64_t value)255bb684c34Spatrick static unsigned getRelocWidth(const WasmRelocation &rel, uint64_t value) {
256bb684c34Spatrick   uint8_t buf[10];
257ece8a530Spatrick   return writeCompressedReloc(buf, rel, value);
258ece8a530Spatrick }
259ece8a530Spatrick 
260ece8a530Spatrick // Relocations of type LEB and SLEB in the code section are padded to 5 bytes
261ece8a530Spatrick // so that a fast linker can blindly overwrite them without needing to worry
262ece8a530Spatrick // about the number of bytes needed to encode the values.
263ece8a530Spatrick // However, for optimal output the code section can be compressed to remove
264ece8a530Spatrick // the padding then outputting non-relocatable files.
265ece8a530Spatrick // In this case we need to perform a size calculation based on the value at each
266ece8a530Spatrick // relocation.  At best we end up saving 4 bytes for each relocation entry.
267ece8a530Spatrick //
268ece8a530Spatrick // This function only computes the final output size.  It must be called
269ece8a530Spatrick // before getSize() is used to calculate of layout of the code section.
calculateSize()270ece8a530Spatrick void InputFunction::calculateSize() {
271ece8a530Spatrick   if (!file || !config->compressRelocations)
272ece8a530Spatrick     return;
273ece8a530Spatrick 
274*dfe94b16Srobert   LLVM_DEBUG(dbgs() << "calculateSize: " << name << "\n");
275ece8a530Spatrick 
276ece8a530Spatrick   const uint8_t *secStart = file->codeSection->Content.data();
277ece8a530Spatrick   const uint8_t *funcStart = secStart + getInputSectionOffset();
278ece8a530Spatrick   uint32_t functionSizeLength;
279ece8a530Spatrick   decodeULEB128(funcStart, &functionSizeLength);
280ece8a530Spatrick 
281ece8a530Spatrick   uint32_t start = getInputSectionOffset();
282ece8a530Spatrick   uint32_t end = start + function->Size;
283ece8a530Spatrick 
2841cf9926bSpatrick   uint64_t tombstone = getTombstone();
2851cf9926bSpatrick 
286ece8a530Spatrick   uint32_t lastRelocEnd = start + functionSizeLength;
287ece8a530Spatrick   for (const WasmRelocation &rel : relocations) {
288ece8a530Spatrick     LLVM_DEBUG(dbgs() << "  region: " << (rel.Offset - lastRelocEnd) << "\n");
289ece8a530Spatrick     compressedFuncSize += rel.Offset - lastRelocEnd;
2901cf9926bSpatrick     compressedFuncSize +=
2911cf9926bSpatrick         getRelocWidth(rel, file->calcNewValue(rel, tombstone, this));
292ece8a530Spatrick     lastRelocEnd = rel.Offset + getRelocWidthPadded(rel);
293ece8a530Spatrick   }
294ece8a530Spatrick   LLVM_DEBUG(dbgs() << "  final region: " << (end - lastRelocEnd) << "\n");
295ece8a530Spatrick   compressedFuncSize += end - lastRelocEnd;
296ece8a530Spatrick 
297ece8a530Spatrick   // Now we know how long the resulting function is we can add the encoding
298ece8a530Spatrick   // of its length
299ece8a530Spatrick   uint8_t buf[5];
300ece8a530Spatrick   compressedSize = compressedFuncSize + encodeULEB128(compressedFuncSize, buf);
301ece8a530Spatrick 
302ece8a530Spatrick   LLVM_DEBUG(dbgs() << "  calculateSize orig: " << function->Size << "\n");
303ece8a530Spatrick   LLVM_DEBUG(dbgs() << "  calculateSize  new: " << compressedSize << "\n");
304ece8a530Spatrick }
305ece8a530Spatrick 
306ece8a530Spatrick // Override the default writeTo method so that we can (optionally) write the
307ece8a530Spatrick // compressed version of the function.
writeCompressed(uint8_t * buf) const3081cf9926bSpatrick void InputFunction::writeCompressed(uint8_t *buf) const {
3091cf9926bSpatrick   buf += outSecOff;
310ece8a530Spatrick   uint8_t *orig = buf;
311ece8a530Spatrick   (void)orig;
312ece8a530Spatrick 
313ece8a530Spatrick   const uint8_t *secStart = file->codeSection->Content.data();
314ece8a530Spatrick   const uint8_t *funcStart = secStart + getInputSectionOffset();
315ece8a530Spatrick   const uint8_t *end = funcStart + function->Size;
3161cf9926bSpatrick   uint64_t tombstone = getTombstone();
317ece8a530Spatrick   uint32_t count;
318ece8a530Spatrick   decodeULEB128(funcStart, &count);
319ece8a530Spatrick   funcStart += count;
320ece8a530Spatrick 
321*dfe94b16Srobert   LLVM_DEBUG(dbgs() << "write func: " << name << "\n");
322ece8a530Spatrick   buf += encodeULEB128(compressedFuncSize, buf);
323ece8a530Spatrick   const uint8_t *lastRelocEnd = funcStart;
324ece8a530Spatrick   for (const WasmRelocation &rel : relocations) {
325ece8a530Spatrick     unsigned chunkSize = (secStart + rel.Offset) - lastRelocEnd;
326ece8a530Spatrick     LLVM_DEBUG(dbgs() << "  write chunk: " << chunkSize << "\n");
327ece8a530Spatrick     memcpy(buf, lastRelocEnd, chunkSize);
328ece8a530Spatrick     buf += chunkSize;
3291cf9926bSpatrick     buf += writeCompressedReloc(buf, rel,
3301cf9926bSpatrick                                 file->calcNewValue(rel, tombstone, this));
331ece8a530Spatrick     lastRelocEnd = secStart + rel.Offset + getRelocWidthPadded(rel);
332ece8a530Spatrick   }
333ece8a530Spatrick 
334ece8a530Spatrick   unsigned chunkSize = end - lastRelocEnd;
335ece8a530Spatrick   LLVM_DEBUG(dbgs() << "  write final chunk: " << chunkSize << "\n");
336ece8a530Spatrick   memcpy(buf, lastRelocEnd, chunkSize);
337ece8a530Spatrick   LLVM_DEBUG(dbgs() << "  total: " << (buf + chunkSize - orig) << "\n");
338ece8a530Spatrick }
339ece8a530Spatrick 
getChunkOffset(uint64_t offset) const3401cf9926bSpatrick uint64_t InputChunk::getChunkOffset(uint64_t offset) const {
3411cf9926bSpatrick   if (const auto *ms = dyn_cast<MergeInputChunk>(this)) {
342*dfe94b16Srobert     LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n");
3431cf9926bSpatrick     LLVM_DEBUG(dbgs() << "offset: " << offset << "\n");
3441cf9926bSpatrick     LLVM_DEBUG(dbgs() << "parentOffset: " << ms->getParentOffset(offset)
3451cf9926bSpatrick                       << "\n");
3461cf9926bSpatrick     assert(ms->parent);
3471cf9926bSpatrick     return ms->parent->getChunkOffset(ms->getParentOffset(offset));
3481cf9926bSpatrick   }
3491cf9926bSpatrick   return outputSegmentOffset + offset;
3501cf9926bSpatrick }
3511cf9926bSpatrick 
getOffset(uint64_t offset) const3521cf9926bSpatrick uint64_t InputChunk::getOffset(uint64_t offset) const {
3531cf9926bSpatrick   return outSecOff + getChunkOffset(offset);
3541cf9926bSpatrick }
3551cf9926bSpatrick 
getVA(uint64_t offset) const3561cf9926bSpatrick uint64_t InputChunk::getVA(uint64_t offset) const {
3571cf9926bSpatrick   return (outputSeg ? outputSeg->startVA : 0) + getChunkOffset(offset);
3581cf9926bSpatrick }
3591cf9926bSpatrick 
360ece8a530Spatrick // Generate code to apply relocations to the data section at runtime.
361*dfe94b16Srobert // This is only called when generating shared libraries (PIC) where address are
362ece8a530Spatrick // not known at static link time.
generateRelocationCode(raw_ostream & os) const3631cf9926bSpatrick void InputChunk::generateRelocationCode(raw_ostream &os) const {
364*dfe94b16Srobert   LLVM_DEBUG(dbgs() << "generating runtime relocations: " << name
365ece8a530Spatrick                     << " count=" << relocations.size() << "\n");
366ece8a530Spatrick 
367*dfe94b16Srobert   bool is64 = config->is64.value_or(false);
3681cf9926bSpatrick   unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST
3691cf9926bSpatrick                                    : WASM_OPCODE_I32_CONST;
3701cf9926bSpatrick   unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD
3711cf9926bSpatrick                                  : WASM_OPCODE_I32_ADD;
372bb684c34Spatrick 
3731cf9926bSpatrick   uint64_t tombstone = getTombstone();
374ece8a530Spatrick   // TODO(sbc): Encode the relocations in the data section and write a loop
375ece8a530Spatrick   // here to apply them.
376ece8a530Spatrick   for (const WasmRelocation &rel : relocations) {
3771cf9926bSpatrick     uint64_t offset = getVA(rel.Offset) - getInputSectionOffset();
378ece8a530Spatrick 
379*dfe94b16Srobert     Symbol *sym = file->getSymbol(rel);
380*dfe94b16Srobert     if (!config->isPic && sym->isDefined())
381*dfe94b16Srobert       continue;
382*dfe94b16Srobert 
383ece8a530Spatrick     LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)
384ece8a530Spatrick                       << " addend=" << rel.Addend << " index=" << rel.Index
3851cf9926bSpatrick                       << " output offset=" << offset << "\n");
386ece8a530Spatrick 
387*dfe94b16Srobert     // Calculate the address at which to apply the relocations
388bb684c34Spatrick     writeU8(os, opcode_ptr_const, "CONST");
3891cf9926bSpatrick     writeSleb128(os, offset, "offset");
390bb684c34Spatrick 
391*dfe94b16Srobert     // In PIC mode we need to add the __memory_base
392*dfe94b16Srobert     if (config->isPic) {
393*dfe94b16Srobert       writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
394*dfe94b16Srobert       writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
395*dfe94b16Srobert       writeU8(os, opcode_ptr_add, "ADD");
396*dfe94b16Srobert     }
397*dfe94b16Srobert 
398*dfe94b16Srobert     // Now figure out what we want to store at this location
399bb684c34Spatrick     bool is64 = relocIs64(rel.Type);
400bb684c34Spatrick     unsigned opcode_reloc_const =
401bb684c34Spatrick         is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST;
402bb684c34Spatrick     unsigned opcode_reloc_add =
403bb684c34Spatrick         is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD;
404bb684c34Spatrick     unsigned opcode_reloc_store =
405bb684c34Spatrick         is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE;
406ece8a530Spatrick 
407ece8a530Spatrick     if (sym->hasGOTIndex()) {
408ece8a530Spatrick       writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
409ece8a530Spatrick       writeUleb128(os, sym->getGOTIndex(), "global index");
410ece8a530Spatrick       if (rel.Addend) {
411bb684c34Spatrick         writeU8(os, opcode_reloc_const, "CONST");
412ece8a530Spatrick         writeSleb128(os, rel.Addend, "addend");
413bb684c34Spatrick         writeU8(os, opcode_reloc_add, "ADD");
414ece8a530Spatrick       }
415ece8a530Spatrick     } else {
416*dfe94b16Srobert       assert(config->isPic);
417ece8a530Spatrick       const GlobalSymbol* baseSymbol = WasmSym::memoryBase;
4181cf9926bSpatrick       if (rel.Type == R_WASM_TABLE_INDEX_I32 ||
4191cf9926bSpatrick           rel.Type == R_WASM_TABLE_INDEX_I64)
420ece8a530Spatrick         baseSymbol = WasmSym::tableBase;
421ece8a530Spatrick       writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
422ece8a530Spatrick       writeUleb128(os, baseSymbol->getGlobalIndex(), "base");
423bb684c34Spatrick       writeU8(os, opcode_reloc_const, "CONST");
4241cf9926bSpatrick       writeSleb128(os, file->calcNewValue(rel, tombstone, this), "offset");
425bb684c34Spatrick       writeU8(os, opcode_reloc_add, "ADD");
426ece8a530Spatrick     }
427ece8a530Spatrick 
428ece8a530Spatrick     // Store that value at the virtual address
429bb684c34Spatrick     writeU8(os, opcode_reloc_store, "I32_STORE");
430ece8a530Spatrick     writeUleb128(os, 2, "align");
431ece8a530Spatrick     writeUleb128(os, 0, "offset");
432ece8a530Spatrick   }
433ece8a530Spatrick }
434ece8a530Spatrick 
4351cf9926bSpatrick // Split WASM_SEG_FLAG_STRINGS section. Such a section is a sequence of
4361cf9926bSpatrick // null-terminated strings.
splitStrings(ArrayRef<uint8_t> data)4371cf9926bSpatrick void MergeInputChunk::splitStrings(ArrayRef<uint8_t> data) {
4381cf9926bSpatrick   LLVM_DEBUG(llvm::dbgs() << "splitStrings\n");
4391cf9926bSpatrick   size_t off = 0;
4401cf9926bSpatrick   StringRef s = toStringRef(data);
4411cf9926bSpatrick 
4421cf9926bSpatrick   while (!s.empty()) {
4431cf9926bSpatrick     size_t end = s.find(0);
4441cf9926bSpatrick     if (end == StringRef::npos)
4451cf9926bSpatrick       fatal(toString(this) + ": string is not null terminated");
4461cf9926bSpatrick     size_t size = end + 1;
4471cf9926bSpatrick 
4481cf9926bSpatrick     pieces.emplace_back(off, xxHash64(s.substr(0, size)), true);
4491cf9926bSpatrick     s = s.substr(size);
4501cf9926bSpatrick     off += size;
4511cf9926bSpatrick   }
4521cf9926bSpatrick }
4531cf9926bSpatrick 
4541cf9926bSpatrick // This function is called after we obtain a complete list of input sections
4551cf9926bSpatrick // that need to be linked. This is responsible to split section contents
4561cf9926bSpatrick // into small chunks for further processing.
4571cf9926bSpatrick //
4581cf9926bSpatrick // Note that this function is called from parallelForEach. This must be
4591cf9926bSpatrick // thread-safe (i.e. no memory allocation from the pools).
splitIntoPieces()4601cf9926bSpatrick void MergeInputChunk::splitIntoPieces() {
4611cf9926bSpatrick   assert(pieces.empty());
4621cf9926bSpatrick   // As of now we only support WASM_SEG_FLAG_STRINGS but in the future we
4631cf9926bSpatrick   // could add other types of splitting (see ELF's splitIntoPieces).
4641cf9926bSpatrick   assert(flags & WASM_SEG_FLAG_STRINGS);
4651cf9926bSpatrick   splitStrings(data());
4661cf9926bSpatrick }
4671cf9926bSpatrick 
getSectionPiece(uint64_t offset)4681cf9926bSpatrick SectionPiece *MergeInputChunk::getSectionPiece(uint64_t offset) {
4691cf9926bSpatrick   if (this->data().size() <= offset)
4701cf9926bSpatrick     fatal(toString(this) + ": offset is outside the section");
4711cf9926bSpatrick 
4721cf9926bSpatrick   // If Offset is not at beginning of a section piece, it is not in the map.
4731cf9926bSpatrick   // In that case we need to  do a binary search of the original section piece
4741cf9926bSpatrick   // vector.
4751cf9926bSpatrick   auto it = partition_point(
4761cf9926bSpatrick       pieces, [=](SectionPiece p) { return p.inputOff <= offset; });
4771cf9926bSpatrick   return &it[-1];
4781cf9926bSpatrick }
4791cf9926bSpatrick 
4801cf9926bSpatrick // Returns the offset in an output section for a given input offset.
4811cf9926bSpatrick // Because contents of a mergeable section is not contiguous in output,
4821cf9926bSpatrick // it is not just an addition to a base output offset.
getParentOffset(uint64_t offset) const4831cf9926bSpatrick uint64_t MergeInputChunk::getParentOffset(uint64_t offset) const {
4841cf9926bSpatrick   // If Offset is not at beginning of a section piece, it is not in the map.
4851cf9926bSpatrick   // In that case we need to search from the original section piece vector.
4861cf9926bSpatrick   const SectionPiece *piece = getSectionPiece(offset);
4871cf9926bSpatrick   uint64_t addend = offset - piece->inputOff;
4881cf9926bSpatrick   return piece->outputOff + addend;
4891cf9926bSpatrick }
4901cf9926bSpatrick 
finalizeContents()4911cf9926bSpatrick void SyntheticMergedChunk::finalizeContents() {
4921cf9926bSpatrick   // Add all string pieces to the string table builder to create section
4931cf9926bSpatrick   // contents.
4941cf9926bSpatrick   for (MergeInputChunk *sec : chunks)
4951cf9926bSpatrick     for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
4961cf9926bSpatrick       if (sec->pieces[i].live)
4971cf9926bSpatrick         builder.add(sec->getData(i));
4981cf9926bSpatrick 
4991cf9926bSpatrick   // Fix the string table content. After this, the contents will never change.
5001cf9926bSpatrick   builder.finalize();
5011cf9926bSpatrick 
5021cf9926bSpatrick   // finalize() fixed tail-optimized strings, so we can now get
5031cf9926bSpatrick   // offsets of strings. Get an offset for each string and save it
5041cf9926bSpatrick   // to a corresponding SectionPiece for easy access.
5051cf9926bSpatrick   for (MergeInputChunk *sec : chunks)
5061cf9926bSpatrick     for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)
5071cf9926bSpatrick       if (sec->pieces[i].live)
5081cf9926bSpatrick         sec->pieces[i].outputOff = builder.getOffset(sec->getData(i));
5091cf9926bSpatrick }
5101cf9926bSpatrick 
getTombstoneForSection(StringRef name)5111cf9926bSpatrick uint64_t InputSection::getTombstoneForSection(StringRef name) {
5121cf9926bSpatrick   // When a function is not live we need to update relocations referring to it.
5131cf9926bSpatrick   // If they occur in DWARF debug symbols, we want to change the pc of the
5141cf9926bSpatrick   // function to -1 to avoid overlapping with a valid range. However for the
5151cf9926bSpatrick   // debug_ranges and debug_loc sections that would conflict with the existing
5161cf9926bSpatrick   // meaning of -1 so we use -2.
5171cf9926bSpatrick   // Returning 0 means there is no tombstone value for this section, and relocation
5181cf9926bSpatrick   // will just use the addend.
5191cf9926bSpatrick   if (!name.startswith(".debug_"))
5201cf9926bSpatrick     return 0;
5211cf9926bSpatrick   if (name.equals(".debug_ranges") || name.equals(".debug_loc"))
5221cf9926bSpatrick     return UINT64_C(-2);
5231cf9926bSpatrick   return UINT64_C(-1);
5241cf9926bSpatrick }
5251cf9926bSpatrick 
526ece8a530Spatrick } // namespace wasm
527ece8a530Spatrick } // namespace lld
528