brintos

brintos / llvm-project-archived public Read only

0
0
Text · 27.4 KiB · fb81e9b Raw
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