723 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "InputChunks.h"10#include "Config.h"11#include "OutputSegment.h"12#include "Symbols.h"13#include "WriterUtils.h"14#include "lld/Common/ErrorHandler.h"15#include "lld/Common/LLVM.h"16#include "llvm/Support/LEB128.h"17#include "llvm/Support/xxhash.h"18#include <algorithm>19 20#define DEBUG_TYPE "lld"21 22using namespace llvm;23using namespace llvm::wasm;24using namespace llvm::support::endian;25 26namespace lld {27StringRef relocTypeToString(uint8_t relocType) {28 switch (relocType) {29#define WASM_RELOC(NAME, REL) \30 case REL: \31 return #NAME;32#include "llvm/BinaryFormat/WasmRelocs.def"33#undef WASM_RELOC34 }35 llvm_unreachable("unknown reloc type");36}37 38bool relocIs64(uint8_t relocType) {39 switch (relocType) {40 case R_WASM_MEMORY_ADDR_LEB64:41 case R_WASM_MEMORY_ADDR_SLEB64:42 case R_WASM_MEMORY_ADDR_REL_SLEB64:43 case R_WASM_MEMORY_ADDR_I64:44 case R_WASM_TABLE_INDEX_SLEB64:45 case R_WASM_TABLE_INDEX_I64:46 case R_WASM_FUNCTION_OFFSET_I64:47 case R_WASM_TABLE_INDEX_REL_SLEB64:48 case R_WASM_MEMORY_ADDR_TLS_SLEB64:49 return true;50 default:51 return false;52 }53}54 55std::string toString(const wasm::InputChunk *c) {56 return (toString(c->file) + ":(" + c->name + ")").str();57}58 59// True for the memory-address (data) relocation kinds, which the wasm format60// requires to reference DATA symbols.61static bool relocTypeIsMemoryAddr(uint8_t type) {62 switch (type) {63 case R_WASM_MEMORY_ADDR_LEB:64 case R_WASM_MEMORY_ADDR_LEB64:65 case R_WASM_MEMORY_ADDR_SLEB:66 case R_WASM_MEMORY_ADDR_SLEB64:67 case R_WASM_MEMORY_ADDR_REL_SLEB:68 case R_WASM_MEMORY_ADDR_REL_SLEB64:69 case R_WASM_MEMORY_ADDR_I32:70 case R_WASM_MEMORY_ADDR_I64:71 case R_WASM_MEMORY_ADDR_TLS_SLEB:72 case R_WASM_MEMORY_ADDR_TLS_SLEB64:73 case R_WASM_MEMORY_ADDR_LOCREL_I32:74 return true;75 default:76 return false;77 }78}79 80// glibc/wasm32 (HWJS-B-1): a memory-address (data) relocation may, after symbol81// resolution, target a FUNCTION (lld commit 12776b692 lets a strong82// undefined-DATA reference bind to a defined function of the same name). Taking83// a function's address on wasm yields a function pointer == its index in84// __indirect_function_table, so when such a relocation is *re-emitted*85// (relocatable / --emit-relocs output) it must be written as the corresponding86// table-index relocation: the wasm object format requires memory-address87// relocations to reference data symbols, and a function pointer is genuinely a88// table index, not a memory address. The provisional in-place bytes that89// InputChunk::relocate() already wrote (the table index, via90// ObjFile::calcNewValue) are byte-compatible with the table-index form for each91// case below. Returns the table-index equivalent, or 0 if this memory-address92// form cannot represent a function pointer (handled by a loud diagnostic).93static uint8_t getFunctionPointerRelocType(uint8_t memAddrType) {94 switch (memAddrType) {95 case R_WASM_MEMORY_ADDR_I32:96 return R_WASM_TABLE_INDEX_I32;97 case R_WASM_MEMORY_ADDR_I64:98 return R_WASM_TABLE_INDEX_I64;99 // The LEB/SLEB forms (e.g. an `i32.const <addr>` operand in code, as glibc's100 // _nl_make_l10nflist emits taking `&__GI_strcpy`) carry the table index in a101 // 5/10-byte padded LEB slot. A table index is a small non-negative integer102 // whose padded unsigned-LEB and signed-LEB encodings are identical, and the103 // table-index relocation kinds are signed (there is no R_WASM_TABLE_INDEX_LEB104 // unsigned form), so both map to the SLEB table-index relocation. (The105 // provisional in-place value is in any case recomputed at the final link.)106 case R_WASM_MEMORY_ADDR_LEB:107 case R_WASM_MEMORY_ADDR_SLEB:108 return R_WASM_TABLE_INDEX_SLEB;109 case R_WASM_MEMORY_ADDR_LEB64:110 case R_WASM_MEMORY_ADDR_SLEB64:111 return R_WASM_TABLE_INDEX_SLEB64;112 default:113 return 0;114 }115}116 117namespace wasm {118StringRef InputChunk::getComdatName() const {119 uint32_t index = getComdat();120 if (index == UINT32_MAX)121 return StringRef();122 return file->getWasmObj()->linkingData().Comdats[index];123}124 125uint32_t InputChunk::getSize() const {126 if (const auto *ms = dyn_cast<SyntheticMergedChunk>(this))127 return ms->builder.getSize();128 129 if (const auto *f = dyn_cast<InputFunction>(this)) {130 if (ctx.arg.compressRelocations && f->file) {131 return f->getCompressedSize();132 }133 }134 135 return data().size();136}137 138uint32_t InputChunk::getInputSize() const {139 if (const auto *f = dyn_cast<InputFunction>(this))140 return f->function->Size;141 return getSize();142}143 144// Copy this input chunk to an mmap'ed output file and apply relocations.145void InputChunk::writeTo(uint8_t *buf) const {146 if (const auto *f = dyn_cast<InputFunction>(this)) {147 if (file && ctx.arg.compressRelocations)148 return f->writeCompressed(buf);149 } else if (const auto *ms = dyn_cast<SyntheticMergedChunk>(this)) {150 ms->builder.write(buf + outSecOff);151 // Apply relocations152 ms->relocate(buf + outSecOff);153 return;154 }155 156 // Copy contents157 memcpy(buf + outSecOff, data().data(), data().size());158 159 // Apply relocations160 relocate(buf + outSecOff);161}162 163void InputChunk::relocate(uint8_t *buf) const {164 if (relocations.empty())165 return;166 167 LLVM_DEBUG(dbgs() << "applying relocations: " << toString(this)168 << " count=" << relocations.size() << "\n");169 int32_t inputSectionOffset = getInputSectionOffset();170 uint64_t tombstone = getTombstone();171 172 for (const WasmRelocation &rel : relocations) {173 uint8_t *loc = buf + rel.Offset - inputSectionOffset;174 LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type));175 if (rel.Type != R_WASM_TYPE_INDEX_LEB)176 LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName());177 LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index178 << " offset=" << rel.Offset << "\n");179 // TODO(sbc): Check that the value is within the range of the180 // relocation type below. Most likely we must error out here181 // if its not with range.182 uint64_t value = file->calcNewValue(rel, tombstone, this);183 184 switch (rel.Type) {185 case R_WASM_TYPE_INDEX_LEB:186 case R_WASM_FUNCTION_INDEX_LEB:187 case R_WASM_GLOBAL_INDEX_LEB:188 case R_WASM_TAG_INDEX_LEB:189 case R_WASM_MEMORY_ADDR_LEB:190 case R_WASM_TABLE_NUMBER_LEB:191 encodeULEB128(static_cast<uint32_t>(value), loc, 5);192 break;193 case R_WASM_MEMORY_ADDR_LEB64:194 encodeULEB128(value, loc, 10);195 break;196 case R_WASM_TABLE_INDEX_SLEB:197 case R_WASM_TABLE_INDEX_REL_SLEB:198 case R_WASM_MEMORY_ADDR_SLEB:199 case R_WASM_MEMORY_ADDR_REL_SLEB:200 case R_WASM_MEMORY_ADDR_TLS_SLEB:201 encodeSLEB128(static_cast<int32_t>(value), loc, 5);202 break;203 case R_WASM_TABLE_INDEX_SLEB64:204 case R_WASM_TABLE_INDEX_REL_SLEB64:205 case R_WASM_MEMORY_ADDR_SLEB64:206 case R_WASM_MEMORY_ADDR_REL_SLEB64:207 case R_WASM_MEMORY_ADDR_TLS_SLEB64:208 encodeSLEB128(static_cast<int64_t>(value), loc, 10);209 break;210 case R_WASM_TABLE_INDEX_I32:211 case R_WASM_MEMORY_ADDR_I32:212 case R_WASM_FUNCTION_OFFSET_I32:213 case R_WASM_FUNCTION_INDEX_I32:214 case R_WASM_SECTION_OFFSET_I32:215 case R_WASM_GLOBAL_INDEX_I32:216 case R_WASM_MEMORY_ADDR_LOCREL_I32:217 write32le(loc, value);218 break;219 case R_WASM_TABLE_INDEX_I64:220 case R_WASM_MEMORY_ADDR_I64:221 case R_WASM_FUNCTION_OFFSET_I64:222 write64le(loc, value);223 break;224 default:225 llvm_unreachable("unknown relocation type");226 }227 }228}229 230static bool relocIsLive(const WasmRelocation &rel, ObjFile *file) {231 return rel.Type == R_WASM_TYPE_INDEX_LEB ||232 file->getSymbol(rel.Index)->isLive();233}234 235size_t InputChunk::getNumLiveRelocations() const {236 return llvm::count_if(relocations, [this](const WasmRelocation &rel) {237 return relocIsLive(rel, file);238 });239}240 241// Copy relocation entries to a given output stream.242// This function is used only when a user passes "-r". For a regular link,243// we consume relocations instead of copying them to an output file.244void InputChunk::writeRelocations(raw_ostream &os) const {245 if (relocations.empty())246 return;247 248 int32_t off = outSecOff - getInputSectionOffset();249 LLVM_DEBUG(dbgs() << "writeRelocations: " << file->getName()250 << " offset=" << Twine(off) << "\n");251 252 for (const WasmRelocation &rel : relocations) {253 if (!relocIsLive(rel, file))254 continue;255 256 // If a memory-address relocation's symbol resolved to a FUNCTION, re-emit257 // it as the equivalent table-index relocation (function pointer == table258 // index); a memory-address relocation against a function symbol is invalid259 // in the wasm object format. See getFunctionPointerRelocType.260 uint8_t type = rel.Type;261 if (rel.Type != R_WASM_TYPE_INDEX_LEB && relocTypeIsMemoryAddr(rel.Type) &&262 isa<FunctionSymbol>(file->getSymbol(rel.Index))) {263 type = getFunctionPointerRelocType(rel.Type);264 if (!type || rel.Addend != 0)265 error(toString(this) + ": cannot take the address of function `" +266 toString(*file->getSymbol(rel.Index)) + "` via relocation " +267 relocTypeToString(rel.Type) +268 (rel.Addend != 0 ? " with a non-zero addend" : "") +269 "; a function pointer is a table index, not a memory address");270 }271 272 writeUleb128(os, type, "reloc type");273 writeUleb128(os, rel.Offset + off, "reloc offset");274 writeUleb128(os, file->calcNewIndex(rel), "reloc index");275 276 if (relocTypeHasAddend(type))277 writeSleb128(os, file->calcNewAddend(rel), "reloc addend");278 }279}280 281uint64_t InputChunk::getTombstone() const {282 if (const auto *s = dyn_cast<InputSection>(this)) {283 return s->tombstoneValue;284 }285 286 return 0;287}288 289void InputFunction::setFunctionIndex(uint32_t index) {290 LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << name << " -> "291 << index << "\n");292 assert(!hasFunctionIndex());293 functionIndex = index;294}295 296void InputFunction::setTableIndex(uint32_t index) {297 LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << name << " -> "298 << index << "\n");299 assert(!hasTableIndex());300 tableIndex = index;301}302 303// Write a relocation value without padding and return the number of bytes304// witten.305static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel,306 uint64_t value) {307 switch (rel.getType()) {308 case R_WASM_TYPE_INDEX_LEB:309 case R_WASM_FUNCTION_INDEX_LEB:310 case R_WASM_GLOBAL_INDEX_LEB:311 case R_WASM_TAG_INDEX_LEB:312 case R_WASM_MEMORY_ADDR_LEB:313 case R_WASM_MEMORY_ADDR_LEB64:314 case R_WASM_TABLE_NUMBER_LEB:315 return encodeULEB128(value, buf);316 case R_WASM_TABLE_INDEX_SLEB:317 case R_WASM_TABLE_INDEX_SLEB64:318 case R_WASM_TABLE_INDEX_REL_SLEB64:319 case R_WASM_MEMORY_ADDR_SLEB:320 case R_WASM_MEMORY_ADDR_SLEB64:321 case R_WASM_MEMORY_ADDR_REL_SLEB:322 case R_WASM_MEMORY_ADDR_REL_SLEB64:323 case R_WASM_MEMORY_ADDR_TLS_SLEB:324 case R_WASM_MEMORY_ADDR_TLS_SLEB64:325 case R_WASM_TABLE_INDEX_REL_SLEB:326 return encodeSLEB128(static_cast<int64_t>(value), buf);327 case R_WASM_TABLE_INDEX_I32:328 case R_WASM_MEMORY_ADDR_I32:329 case R_WASM_FUNCTION_OFFSET_I32:330 case R_WASM_SECTION_OFFSET_I32:331 case R_WASM_GLOBAL_INDEX_I32:332 case R_WASM_MEMORY_ADDR_I64:333 case R_WASM_TABLE_INDEX_I64:334 case R_WASM_FUNCTION_OFFSET_I64:335 case R_WASM_MEMORY_ADDR_LOCREL_I32:336 case R_WASM_FUNCTION_INDEX_I32:337 fatal("relocation compression not supported for " +338 relocTypeToString(rel.Type));339 }340 llvm_unreachable("unhandled relocation type");341}342 343static unsigned getRelocWidthPadded(const WasmRelocation &rel) {344 switch (rel.getType()) {345 case R_WASM_TYPE_INDEX_LEB:346 case R_WASM_FUNCTION_INDEX_LEB:347 case R_WASM_GLOBAL_INDEX_LEB:348 case R_WASM_TAG_INDEX_LEB:349 case R_WASM_MEMORY_ADDR_LEB:350 case R_WASM_TABLE_NUMBER_LEB:351 case R_WASM_TABLE_INDEX_SLEB:352 case R_WASM_TABLE_INDEX_REL_SLEB:353 case R_WASM_MEMORY_ADDR_SLEB:354 case R_WASM_MEMORY_ADDR_REL_SLEB:355 case R_WASM_MEMORY_ADDR_TLS_SLEB:356 return 5;357 case R_WASM_TABLE_INDEX_SLEB64:358 case R_WASM_TABLE_INDEX_REL_SLEB64:359 case R_WASM_MEMORY_ADDR_LEB64:360 case R_WASM_MEMORY_ADDR_SLEB64:361 case R_WASM_MEMORY_ADDR_REL_SLEB64:362 case R_WASM_MEMORY_ADDR_TLS_SLEB64:363 return 10;364 case R_WASM_TABLE_INDEX_I32:365 case R_WASM_MEMORY_ADDR_I32:366 case R_WASM_FUNCTION_OFFSET_I32:367 case R_WASM_SECTION_OFFSET_I32:368 case R_WASM_GLOBAL_INDEX_I32:369 case R_WASM_MEMORY_ADDR_I64:370 case R_WASM_TABLE_INDEX_I64:371 case R_WASM_FUNCTION_OFFSET_I64:372 case R_WASM_MEMORY_ADDR_LOCREL_I32:373 case R_WASM_FUNCTION_INDEX_I32:374 fatal("relocation compression not supported for " +375 relocTypeToString(rel.Type));376 }377 llvm_unreachable("unhandled relocation type");378}379 380static unsigned getRelocWidth(const WasmRelocation &rel, uint64_t value) {381 uint8_t buf[10];382 return writeCompressedReloc(buf, rel, value);383}384 385// Relocations of type LEB and SLEB in the code section are padded to 5 bytes386// so that a fast linker can blindly overwrite them without needing to worry387// about the number of bytes needed to encode the values.388// However, for optimal output the code section can be compressed to remove389// the padding then outputting non-relocatable files.390// In this case we need to perform a size calculation based on the value at each391// relocation. At best we end up saving 4 bytes for each relocation entry.392//393// This function only computes the final output size. It must be called394// before getSize() is used to calculate of layout of the code section.395void InputFunction::calculateSize() {396 if (!file || !ctx.arg.compressRelocations)397 return;398 399 LLVM_DEBUG(dbgs() << "calculateSize: " << name << "\n");400 401 const uint8_t *secStart = file->codeSection->Content.data();402 const uint8_t *funcStart = secStart + getInputSectionOffset();403 uint32_t functionSizeLength;404 decodeULEB128(funcStart, &functionSizeLength);405 406 uint32_t start = getInputSectionOffset();407 uint32_t end = start + function->Size;408 409 uint64_t tombstone = getTombstone();410 411 uint32_t lastRelocEnd = start + functionSizeLength;412 for (const WasmRelocation &rel : relocations) {413 LLVM_DEBUG(dbgs() << " region: " << (rel.Offset - lastRelocEnd) << "\n");414 compressedFuncSize += rel.Offset - lastRelocEnd;415 compressedFuncSize +=416 getRelocWidth(rel, file->calcNewValue(rel, tombstone, this));417 lastRelocEnd = rel.Offset + getRelocWidthPadded(rel);418 }419 LLVM_DEBUG(dbgs() << " final region: " << (end - lastRelocEnd) << "\n");420 compressedFuncSize += end - lastRelocEnd;421 422 // Now we know how long the resulting function is we can add the encoding423 // of its length424 uint8_t buf[5];425 compressedSize = compressedFuncSize + encodeULEB128(compressedFuncSize, buf);426 427 LLVM_DEBUG(dbgs() << " calculateSize orig: " << function->Size << "\n");428 LLVM_DEBUG(dbgs() << " calculateSize new: " << compressedSize << "\n");429}430 431// Override the default writeTo method so that we can (optionally) write the432// compressed version of the function.433void InputFunction::writeCompressed(uint8_t *buf) const {434 buf += outSecOff;435 uint8_t *orig = buf;436 (void)orig;437 438 const uint8_t *secStart = file->codeSection->Content.data();439 const uint8_t *funcStart = secStart + getInputSectionOffset();440 const uint8_t *end = funcStart + function->Size;441 uint64_t tombstone = getTombstone();442 uint32_t count;443 decodeULEB128(funcStart, &count);444 funcStart += count;445 446 LLVM_DEBUG(dbgs() << "write func: " << name << "\n");447 buf += encodeULEB128(compressedFuncSize, buf);448 const uint8_t *lastRelocEnd = funcStart;449 for (const WasmRelocation &rel : relocations) {450 unsigned chunkSize = (secStart + rel.Offset) - lastRelocEnd;451 LLVM_DEBUG(dbgs() << " write chunk: " << chunkSize << "\n");452 memcpy(buf, lastRelocEnd, chunkSize);453 buf += chunkSize;454 buf += writeCompressedReloc(buf, rel,455 file->calcNewValue(rel, tombstone, this));456 lastRelocEnd = secStart + rel.Offset + getRelocWidthPadded(rel);457 }458 459 unsigned chunkSize = end - lastRelocEnd;460 LLVM_DEBUG(dbgs() << " write final chunk: " << chunkSize << "\n");461 memcpy(buf, lastRelocEnd, chunkSize);462 LLVM_DEBUG(dbgs() << " total: " << (buf + chunkSize - orig) << "\n");463}464 465uint64_t InputChunk::getChunkOffset(uint64_t offset) const {466 if (const auto *ms = dyn_cast<MergeInputChunk>(this)) {467 LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n");468 LLVM_DEBUG(dbgs() << "offset: " << offset << "\n");469 LLVM_DEBUG(dbgs() << "parentOffset: " << ms->getParentOffset(offset)470 << "\n");471 assert(ms->parent);472 return ms->parent->getChunkOffset(ms->getParentOffset(offset));473 }474 return outputSegmentOffset + offset;475}476 477uint64_t InputChunk::getOffset(uint64_t offset) const {478 return outSecOff + getChunkOffset(offset);479}480 481uint64_t InputChunk::getVA(uint64_t offset) const {482 return (outputSeg ? outputSeg->startVA : 0) + getChunkOffset(offset);483}484 485bool isValidRuntimeRelocation(WasmRelocType type) {486 // TODO(https://github.com/llvm/llvm-project/issues/146923): Currently487 // this means that R_WASM_FUNCTION_INDEX_I32 is not valid in `-pie` data488 // sections.489 return type == R_WASM_TABLE_INDEX_I32 || type == R_WASM_TABLE_INDEX_I64 ||490 type == R_WASM_MEMORY_ADDR_I32 || type == R_WASM_MEMORY_ADDR_I64;491}492 493// Generate code to apply relocations to the data section at runtime.494// This is only called when generating shared libraries (PIC) where address are495// not known at static link time.496bool InputChunk::generateRelocationCode(raw_ostream &os) const {497 LLVM_DEBUG(dbgs() << "generating runtime relocations: " << name498 << " count=" << relocations.size() << "\n");499 500 bool is64 = ctx.arg.is64.value_or(false);501 bool generated = false;502 unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD503 : WASM_OPCODE_I32_ADD;504 505 uint64_t tombstone = getTombstone();506 // TODO(sbc): Encode the relocations in the data section and write a loop507 // here to apply them.508 for (const WasmRelocation &rel : relocations) {509 Symbol *sym = file->getSymbol(rel);510 // Runtime relocations are needed when we don't know the address of511 // a symbol statically.512 bool requiresRuntimeReloc = ctx.isPic || sym->hasGOTIndex();513 if (!requiresRuntimeReloc) {514 // wasm32 TLS pointer-initializer fix: in a NON-PIC link a TLS-segment515 // data relocation -- e.g. a `__thread T *p = &global` initializer such as516 // glibc's `__thread locale_t __libc_tsd_LOCALE = &_nl_global_locale` --517 // has a link-time-constant value that lld also bakes into the .tdata518 // template image. That baked image is NOT sufficient on its own: the519 // per-thread TLS block is materialised at runtime by __wasm_init_tls,520 // which memory.init's the passive .tdata template and then calls this521 // function (__wasm_apply_tls_relocs) to apply TLS relocations. A libc522 // may establish the block by ZERO-FILLING it rather than copying the523 // wasm template verbatim -- glibc's _dl_allocate_tls_init memsets the524 // whole block to 0 because a wasm TLS segment is passive and has no525 // linear init-image (l_tls_initimage == NULL) -- leaving every .tdata526 // pointer slot NULL. Historically this function emitted code only for527 // PIC/GOT relocations, so a non-PIC absolute TLS relocation was silently528 // dropped and __wasm_apply_tls_relocs was a no-op stub: the __thread529 // pointer then read 0 at runtime with no diagnostic -- a silent530 // miscompile of .tdata pointer initializers. Apply the absolute531 // link-time constant to the live per-thread slot here so the value is532 // re-established regardless of how the block was materialised; "never533 // silently leave a TLS relocation unapplied".534 //535 // Scoped to TLS output segments under --shared-memory: the non-TLS data536 // path (__wasm_apply_data_relocs) is PIC-only and its absolute values are537 // correct in the baked image; a single-threaded (non --shared-memory)538 // link lowers TLS to ordinary data placed at a fixed VA and has no539 // __tls_base / per-thread block, so it keeps the baked-image-only path.540 if (ctx.arg.sharedMemory && isTLS() && outputSeg && ctx.sym.tlsBase &&541 isValidRuntimeRelocation(rel.getType()) && sym->isDefined() &&542 !sym->isShared()) {543 uint64_t value = file->calcNewValue(rel, tombstone, this);544 // Offset of this slot within the per-thread TLS block (== its offset545 // within the .tdata output segment, whose runtime base is __tls_base).546 uint64_t tlsSlotOff = getChunkOffset(rel.Offset) - getInputSectionOffset();547 bool relIs64 = relocIs64(rel.Type);548 // addr = __tls_base + tlsSlotOff549 writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");550 writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "__tls_base");551 writePtrConst(os, tlsSlotOff, is64, "tls slot offset");552 writeU8(os, opcode_ptr_add, "ADD");553 // value = the link-time-constant address (matches the baked image)554 writePtrConst(os, value, relIs64, "tls reloc value");555 writeU8(os, relIs64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE,556 "STORE");557 writeUleb128(os, relIs64 ? 3 : 2, "align");558 writeUleb128(os, 0, "store offset");559 generated = true;560 }561 continue;562 }563 564 if (!isValidRuntimeRelocation(rel.getType())) {565 error("invalid runtime relocation type in data section: " +566 relocTypetoString(rel.Type));567 continue;568 }569 570 uint64_t offset = getVA(rel.Offset) - getInputSectionOffset();571 LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)572 << " addend=" << rel.Addend << " index=" << rel.Index573 << " output offset=" << offset << "\n");574 575 // Calculate the address at which to apply the relocation576 writePtrConst(os, offset, is64, "offset");577 578 // In PIC mode we need to add the __memory_base579 if (ctx.isPic) {580 writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");581 if (isTLS())582 writeUleb128(os, ctx.sym.tlsBase->getGlobalIndex(), "tls_base");583 else584 writeUleb128(os, ctx.sym.memoryBase->getGlobalIndex(), "memory_base");585 writeU8(os, opcode_ptr_add, "ADD");586 }587 588 // Now figure out what we want to store at this location589 bool is64 = relocIs64(rel.Type);590 unsigned opcode_reloc_add =591 is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD;592 unsigned opcode_reloc_store =593 is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE;594 595 if (sym->hasGOTIndex()) {596 writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");597 writeUleb128(os, sym->getGOTIndex(), "global index");598 if (rel.Addend) {599 writePtrConst(os, rel.Addend, is64, "addend");600 writeU8(os, opcode_reloc_add, "ADD");601 }602 } else {603 assert(ctx.isPic);604 const GlobalSymbol *baseSymbol = ctx.sym.memoryBase;605 if (rel.Type == R_WASM_TABLE_INDEX_I32 ||606 rel.Type == R_WASM_TABLE_INDEX_I64)607 baseSymbol = ctx.sym.tableBase;608 else if (sym->isTLS())609 baseSymbol = ctx.sym.tlsBase;610 writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");611 writeUleb128(os, baseSymbol->getGlobalIndex(), "base");612 writePtrConst(os, file->calcNewValue(rel, tombstone, this), is64,613 "offset");614 writeU8(os, opcode_reloc_add, "ADD");615 }616 617 // Store that value at the virtual address618 writeU8(os, opcode_reloc_store, "I32_STORE");619 writeUleb128(os, 2, "align");620 writeUleb128(os, 0, "offset");621 generated = true;622 }623 return generated;624}625 626// Split WASM_SEG_FLAG_STRINGS section. Such a section is a sequence of627// null-terminated strings.628void MergeInputChunk::splitStrings(ArrayRef<uint8_t> data) {629 LLVM_DEBUG(llvm::dbgs() << "splitStrings\n");630 size_t off = 0;631 StringRef s = toStringRef(data);632 633 while (!s.empty()) {634 size_t end = s.find(0);635 if (end == StringRef::npos)636 fatal(toString(this) + ": string is not null terminated");637 size_t size = end + 1;638 639 pieces.emplace_back(off, xxh3_64bits(s.substr(0, size)), true);640 s = s.substr(size);641 off += size;642 }643}644 645// This function is called after we obtain a complete list of input sections646// that need to be linked. This is responsible to split section contents647// into small chunks for further processing.648//649// Note that this function is called from parallelForEach. This must be650// thread-safe (i.e. no memory allocation from the pools).651void MergeInputChunk::splitIntoPieces() {652 assert(pieces.empty());653 // As of now we only support WASM_SEG_FLAG_STRINGS but in the future we654 // could add other types of splitting (see ELF's splitIntoPieces).655 assert(flags & WASM_SEG_FLAG_STRINGS);656 splitStrings(data());657}658 659SectionPiece *MergeInputChunk::getSectionPiece(uint64_t offset) {660 if (this->data().size() <= offset)661 fatal(toString(this) + ": offset is outside the section");662 663 // If Offset is not at beginning of a section piece, it is not in the map.664 // In that case we need to do a binary search of the original section piece665 // vector.666 auto it = partition_point(667 pieces, [=](SectionPiece p) { return p.inputOff <= offset; });668 return &it[-1];669}670 671// Returns the offset in an output section for a given input offset.672// Because contents of a mergeable section is not contiguous in output,673// it is not just an addition to a base output offset.674uint64_t MergeInputChunk::getParentOffset(uint64_t offset) const {675 // If Offset is not at beginning of a section piece, it is not in the map.676 // In that case we need to search from the original section piece vector.677 const SectionPiece *piece = getSectionPiece(offset);678 uint64_t addend = offset - piece->inputOff;679 return piece->outputOff + addend;680}681 682void SyntheticMergedChunk::finalizeContents() {683 // Add all string pieces to the string table builder to create section684 // contents.685 for (MergeInputChunk *sec : chunks)686 for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)687 if (sec->pieces[i].live)688 builder.add(sec->getData(i));689 690 // Fix the string table content. After this, the contents will never change.691 builder.finalize();692 693 // finalize() fixed tail-optimized strings, so we can now get694 // offsets of strings. Get an offset for each string and save it695 // to a corresponding SectionPiece for easy access.696 for (MergeInputChunk *sec : chunks)697 for (size_t i = 0, e = sec->pieces.size(); i != e; ++i)698 if (sec->pieces[i].live)699 sec->pieces[i].outputOff = builder.getOffset(sec->getData(i));700}701 702uint64_t InputSection::getTombstoneForSection(StringRef name) {703 // When a function is not live we need to update relocations referring to it.704 // If they occur in DWARF debug symbols, we want to change the pc of the705 // function to -1 to avoid overlapping with a valid range. However for the706 // debug_ranges and debug_loc sections that would conflict with the existing707 // meaning of -1 so we use -2.708 if (name == ".debug_ranges" || name == ".debug_loc")709 return UINT64_C(-2);710 if (name.starts_with(".debug_"))711 return UINT64_C(-1);712 // If the function occurs in an function attribute section change it to -1 since713 // 0 is a valid function index.714 if (name.starts_with("llvm.func_attr."))715 return UINT64_C(-1);716 // Returning 0 means there is no tombstone value for this section, and relocation717 // will just use the addend.718 return 0;719}720 721} // namespace wasm722} // namespace lld723