| File: | build/source/lld/wasm/InputChunks.cpp |
| Warning: | line 116, column 7 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 | ||||
| 18 | #define DEBUG_TYPE"lld" "lld" | |||
| 19 | ||||
| 20 | using namespace llvm; | |||
| 21 | using namespace llvm::wasm; | |||
| 22 | using namespace llvm::support::endian; | |||
| 23 | ||||
| 24 | namespace lld { | |||
| 25 | StringRef relocTypeToString(uint8_t relocType) { | |||
| 26 | switch (relocType) { | |||
| 27 | #define WASM_RELOC(NAME, REL) \ | |||
| 28 | case REL: \ | |||
| 29 | return #NAME; | |||
| 30 | #include "llvm/BinaryFormat/WasmRelocs.def" | |||
| 31 | #undef WASM_RELOC | |||
| 32 | } | |||
| 33 | llvm_unreachable("unknown reloc type")::llvm::llvm_unreachable_internal("unknown reloc type", "lld/wasm/InputChunks.cpp" , 33); | |||
| 34 | } | |||
| 35 | ||||
| 36 | bool relocIs64(uint8_t relocType) { | |||
| 37 | switch (relocType) { | |||
| 38 | case R_WASM_MEMORY_ADDR_LEB64: | |||
| 39 | case R_WASM_MEMORY_ADDR_SLEB64: | |||
| 40 | case R_WASM_MEMORY_ADDR_REL_SLEB64: | |||
| 41 | case R_WASM_MEMORY_ADDR_I64: | |||
| 42 | case R_WASM_TABLE_INDEX_SLEB64: | |||
| 43 | case R_WASM_TABLE_INDEX_I64: | |||
| 44 | case R_WASM_FUNCTION_OFFSET_I64: | |||
| 45 | case R_WASM_TABLE_INDEX_REL_SLEB64: | |||
| 46 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: | |||
| 47 | return true; | |||
| 48 | default: | |||
| 49 | return false; | |||
| 50 | } | |||
| 51 | } | |||
| 52 | ||||
| 53 | std::string toString(const wasm::InputChunk *c) { | |||
| 54 | return (toString(c->file) + ":(" + c->name + ")").str(); | |||
| 55 | } | |||
| 56 | ||||
| 57 | namespace wasm { | |||
| 58 | StringRef InputChunk::getComdatName() const { | |||
| 59 | uint32_t index = getComdat(); | |||
| 60 | if (index == UINT32_MAX(4294967295U)) | |||
| 61 | return StringRef(); | |||
| 62 | return file->getWasmObj()->linkingData().Comdats[index]; | |||
| 63 | } | |||
| 64 | ||||
| 65 | uint32_t InputChunk::getSize() const { | |||
| 66 | if (const auto *ms = dyn_cast<SyntheticMergedChunk>(this)) | |||
| 67 | return ms->builder.getSize(); | |||
| 68 | ||||
| 69 | if (const auto *f = dyn_cast<InputFunction>(this)) { | |||
| 70 | if (config->compressRelocations && f->file) { | |||
| 71 | return f->getCompressedSize(); | |||
| 72 | } | |||
| 73 | } | |||
| 74 | ||||
| 75 | return data().size(); | |||
| 76 | } | |||
| 77 | ||||
| 78 | uint32_t InputChunk::getInputSize() const { | |||
| 79 | if (const auto *f = dyn_cast<InputFunction>(this)) | |||
| 80 | return f->function->Size; | |||
| 81 | return getSize(); | |||
| 82 | } | |||
| 83 | ||||
| 84 | // Copy this input chunk to an mmap'ed output file and apply relocations. | |||
| 85 | void InputChunk::writeTo(uint8_t *buf) const { | |||
| 86 | if (const auto *f
| |||
| ||||
| 87 | if (file && config->compressRelocations) | |||
| 88 | return f->writeCompressed(buf); | |||
| 89 | } else if (const auto *ms = dyn_cast<SyntheticMergedChunk>(this)) { | |||
| 90 | ms->builder.write(buf + outSecOff); | |||
| 91 | // Apply relocations | |||
| 92 | ms->relocate(buf + outSecOff); | |||
| 93 | return; | |||
| 94 | } | |||
| 95 | ||||
| 96 | // Copy contents | |||
| 97 | memcpy(buf + outSecOff, data().data(), data().size()); | |||
| 98 | ||||
| 99 | // Apply relocations | |||
| 100 | relocate(buf + outSecOff); | |||
| 101 | } | |||
| 102 | ||||
| 103 | void InputChunk::relocate(uint8_t *buf) const { | |||
| 104 | if (relocations.empty()) | |||
| 105 | return; | |||
| 106 | ||||
| 107 | LLVM_DEBUG(dbgs() << "applying relocations: " << toString(this)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "applying relocations: " << toString (this) << " count=" << relocations.size() << "\n"; } } while (false) | |||
| 108 | << " count=" << relocations.size() << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "applying relocations: " << toString (this) << " count=" << relocations.size() << "\n"; } } while (false); | |||
| 109 | int32_t inputSectionOffset = getInputSectionOffset(); | |||
| 110 | uint64_t tombstone = getTombstone(); | |||
| 111 | ||||
| 112 | for (const WasmRelocation &rel : relocations) { | |||
| 113 | uint8_t *loc = buf + rel.Offset - inputSectionOffset; | |||
| 114 | LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type))do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "apply reloc: type=" << relocTypeToString (rel.Type); } } while (false); | |||
| 115 | if (rel.Type != R_WASM_TYPE_INDEX_LEB) | |||
| 116 | LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName())do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " sym=" << file->getSymbols ()[rel.Index]->getName(); } } while (false); | |||
| ||||
| 117 | LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Indexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " addend=" << rel.Addend << " index=" << rel.Index << " offset=" << rel .Offset << "\n"; } } while (false) | |||
| 118 | << " offset=" << rel.Offset << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " addend=" << rel.Addend << " index=" << rel.Index << " offset=" << rel .Offset << "\n"; } } while (false); | |||
| 119 | // TODO(sbc): Check that the value is within the range of the | |||
| 120 | // relocation type below. Most likely we must error out here | |||
| 121 | // if its not with range. | |||
| 122 | uint64_t value = file->calcNewValue(rel, tombstone, this); | |||
| 123 | ||||
| 124 | switch (rel.Type) { | |||
| 125 | case R_WASM_TYPE_INDEX_LEB: | |||
| 126 | case R_WASM_FUNCTION_INDEX_LEB: | |||
| 127 | case R_WASM_GLOBAL_INDEX_LEB: | |||
| 128 | case R_WASM_TAG_INDEX_LEB: | |||
| 129 | case R_WASM_MEMORY_ADDR_LEB: | |||
| 130 | case R_WASM_TABLE_NUMBER_LEB: | |||
| 131 | encodeULEB128(static_cast<uint32_t>(value), loc, 5); | |||
| 132 | break; | |||
| 133 | case R_WASM_MEMORY_ADDR_LEB64: | |||
| 134 | encodeULEB128(value, loc, 10); | |||
| 135 | break; | |||
| 136 | case R_WASM_TABLE_INDEX_SLEB: | |||
| 137 | case R_WASM_TABLE_INDEX_REL_SLEB: | |||
| 138 | case R_WASM_MEMORY_ADDR_SLEB: | |||
| 139 | case R_WASM_MEMORY_ADDR_REL_SLEB: | |||
| 140 | case R_WASM_MEMORY_ADDR_TLS_SLEB: | |||
| 141 | encodeSLEB128(static_cast<int32_t>(value), loc, 5); | |||
| 142 | break; | |||
| 143 | case R_WASM_TABLE_INDEX_SLEB64: | |||
| 144 | case R_WASM_TABLE_INDEX_REL_SLEB64: | |||
| 145 | case R_WASM_MEMORY_ADDR_SLEB64: | |||
| 146 | case R_WASM_MEMORY_ADDR_REL_SLEB64: | |||
| 147 | case R_WASM_MEMORY_ADDR_TLS_SLEB64: | |||
| 148 | encodeSLEB128(static_cast<int64_t>(value), loc, 10); | |||
| 149 | break; | |||
| 150 | case R_WASM_TABLE_INDEX_I32: | |||
| 151 | case R_WASM_MEMORY_ADDR_I32: | |||
| 152 | case R_WASM_FUNCTION_OFFSET_I32: | |||
| 153 | case R_WASM_SECTION_OFFSET_I32: | |||
| 154 | case R_WASM_GLOBAL_INDEX_I32: | |||
| 155 | case R_WASM_MEMORY_ADDR_LOCREL_I32: | |||
| 156 | write32le(loc, value); | |||
| 157 | break; | |||
| 158 | case R_WASM_TABLE_INDEX_I64: | |||
| 159 | case R_WASM_MEMORY_ADDR_I64: | |||
| 160 | case R_WASM_FUNCTION_OFFSET_I64: | |||
| 161 | write64le(loc, value); | |||
| 162 | break; | |||
| 163 | default: | |||
| 164 | llvm_unreachable("unknown relocation type")::llvm::llvm_unreachable_internal("unknown relocation type", "lld/wasm/InputChunks.cpp" , 164); | |||
| 165 | } | |||
| 166 | } | |||
| 167 | } | |||
| 168 | ||||
| 169 | // Copy relocation entries to a given output stream. | |||
| 170 | // This function is used only when a user passes "-r". For a regular link, | |||
| 171 | // we consume relocations instead of copying them to an output file. | |||
| 172 | void InputChunk::writeRelocations(raw_ostream &os) const { | |||
| 173 | if (relocations.empty()) | |||
| 174 | return; | |||
| 175 | ||||
| 176 | int32_t off = outSecOff - getInputSectionOffset(); | |||
| 177 | LLVM_DEBUG(dbgs() << "writeRelocations: " << file->getName()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "writeRelocations: " << file ->getName() << " offset=" << Twine(off) << "\n"; } } while (false) | |||
| 178 | << " offset=" << Twine(off) << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "writeRelocations: " << file ->getName() << " offset=" << Twine(off) << "\n"; } } while (false); | |||
| 179 | ||||
| 180 | for (const WasmRelocation &rel : relocations) { | |||
| 181 | writeUleb128(os, rel.Type, "reloc type"); | |||
| 182 | writeUleb128(os, rel.Offset + off, "reloc offset"); | |||
| 183 | writeUleb128(os, file->calcNewIndex(rel), "reloc index"); | |||
| 184 | ||||
| 185 | if (relocTypeHasAddend(rel.Type)) | |||
| 186 | writeSleb128(os, file->calcNewAddend(rel), "reloc addend"); | |||
| 187 | } | |||
| 188 | } | |||
| 189 | ||||
| 190 | uint64_t InputChunk::getTombstone() const { | |||
| 191 | if (const auto *s = dyn_cast<InputSection>(this)) { | |||
| 192 | return s->tombstoneValue; | |||
| 193 | } | |||
| 194 | ||||
| 195 | return 0; | |||
| 196 | } | |||
| 197 | ||||
| 198 | void InputFunction::setFunctionIndex(uint32_t index) { | |||
| 199 | LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << name << " -> "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "InputFunction::setFunctionIndex: " << name << " -> " << index << "\n" ; } } while (false) | |||
| 200 | << index << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "InputFunction::setFunctionIndex: " << name << " -> " << index << "\n" ; } } while (false); | |||
| 201 | assert(!hasFunctionIndex())(static_cast <bool> (!hasFunctionIndex()) ? void (0) : __assert_fail ("!hasFunctionIndex()", "lld/wasm/InputChunks.cpp", 201, __extension__ __PRETTY_FUNCTION__)); | |||
| 202 | functionIndex = index; | |||
| 203 | } | |||
| 204 | ||||
| 205 | void InputFunction::setTableIndex(uint32_t index) { | |||
| 206 | LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << name << " -> "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "InputFunction::setTableIndex: " << name << " -> " << index << "\n"; } } while (false) | |||
| 207 | << index << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "InputFunction::setTableIndex: " << name << " -> " << index << "\n"; } } while (false); | |||
| 208 | assert(!hasTableIndex())(static_cast <bool> (!hasTableIndex()) ? void (0) : __assert_fail ("!hasTableIndex()", "lld/wasm/InputChunks.cpp", 208, __extension__ __PRETTY_FUNCTION__)); | |||
| 209 | tableIndex = index; | |||
| 210 | } | |||
| 211 | ||||
| 212 | // Write a relocation value without padding and return the number of bytes | |||
| 213 | // witten. | |||
| 214 | static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel, | |||
| 215 | uint64_t value) { | |||
| 216 | switch (rel.Type) { | |||
| 217 | case R_WASM_TYPE_INDEX_LEB: | |||
| 218 | case R_WASM_FUNCTION_INDEX_LEB: | |||
| 219 | case R_WASM_GLOBAL_INDEX_LEB: | |||
| 220 | case R_WASM_TAG_INDEX_LEB: | |||
| 221 | case R_WASM_MEMORY_ADDR_LEB: | |||
| 222 | case R_WASM_MEMORY_ADDR_LEB64: | |||
| 223 | case R_WASM_TABLE_NUMBER_LEB: | |||
| 224 | return encodeULEB128(value, buf); | |||
| 225 | case R_WASM_TABLE_INDEX_SLEB: | |||
| 226 | case R_WASM_TABLE_INDEX_SLEB64: | |||
| 227 | case R_WASM_MEMORY_ADDR_SLEB: | |||
| 228 | case R_WASM_MEMORY_ADDR_SLEB64: | |||
| 229 | return encodeSLEB128(static_cast<int64_t>(value), buf); | |||
| 230 | default: | |||
| 231 | llvm_unreachable("unexpected relocation type")::llvm::llvm_unreachable_internal("unexpected relocation type" , "lld/wasm/InputChunks.cpp", 231); | |||
| 232 | } | |||
| 233 | } | |||
| 234 | ||||
| 235 | static unsigned getRelocWidthPadded(const WasmRelocation &rel) { | |||
| 236 | switch (rel.Type) { | |||
| 237 | case R_WASM_TYPE_INDEX_LEB: | |||
| 238 | case R_WASM_FUNCTION_INDEX_LEB: | |||
| 239 | case R_WASM_GLOBAL_INDEX_LEB: | |||
| 240 | case R_WASM_TAG_INDEX_LEB: | |||
| 241 | case R_WASM_MEMORY_ADDR_LEB: | |||
| 242 | case R_WASM_TABLE_NUMBER_LEB: | |||
| 243 | case R_WASM_TABLE_INDEX_SLEB: | |||
| 244 | case R_WASM_MEMORY_ADDR_SLEB: | |||
| 245 | return 5; | |||
| 246 | case R_WASM_TABLE_INDEX_SLEB64: | |||
| 247 | case R_WASM_MEMORY_ADDR_LEB64: | |||
| 248 | case R_WASM_MEMORY_ADDR_SLEB64: | |||
| 249 | return 10; | |||
| 250 | default: | |||
| 251 | llvm_unreachable("unexpected relocation type")::llvm::llvm_unreachable_internal("unexpected relocation type" , "lld/wasm/InputChunks.cpp", 251); | |||
| 252 | } | |||
| 253 | } | |||
| 254 | ||||
| 255 | static unsigned getRelocWidth(const WasmRelocation &rel, uint64_t value) { | |||
| 256 | uint8_t buf[10]; | |||
| 257 | return writeCompressedReloc(buf, rel, value); | |||
| 258 | } | |||
| 259 | ||||
| 260 | // Relocations of type LEB and SLEB in the code section are padded to 5 bytes | |||
| 261 | // so that a fast linker can blindly overwrite them without needing to worry | |||
| 262 | // about the number of bytes needed to encode the values. | |||
| 263 | // However, for optimal output the code section can be compressed to remove | |||
| 264 | // the padding then outputting non-relocatable files. | |||
| 265 | // In this case we need to perform a size calculation based on the value at each | |||
| 266 | // relocation. At best we end up saving 4 bytes for each relocation entry. | |||
| 267 | // | |||
| 268 | // This function only computes the final output size. It must be called | |||
| 269 | // before getSize() is used to calculate of layout of the code section. | |||
| 270 | void InputFunction::calculateSize() { | |||
| 271 | if (!file || !config->compressRelocations) | |||
| 272 | return; | |||
| 273 | ||||
| 274 | LLVM_DEBUG(dbgs() << "calculateSize: " << name << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "calculateSize: " << name << "\n"; } } while (false); | |||
| 275 | ||||
| 276 | const uint8_t *secStart = file->codeSection->Content.data(); | |||
| 277 | const uint8_t *funcStart = secStart + getInputSectionOffset(); | |||
| 278 | uint32_t functionSizeLength; | |||
| 279 | decodeULEB128(funcStart, &functionSizeLength); | |||
| 280 | ||||
| 281 | uint32_t start = getInputSectionOffset(); | |||
| 282 | uint32_t end = start + function->Size; | |||
| 283 | ||||
| 284 | uint64_t tombstone = getTombstone(); | |||
| 285 | ||||
| 286 | uint32_t lastRelocEnd = start + functionSizeLength; | |||
| 287 | for (const WasmRelocation &rel : relocations) { | |||
| 288 | LLVM_DEBUG(dbgs() << " region: " << (rel.Offset - lastRelocEnd) << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " region: " << (rel.Offset - lastRelocEnd) << "\n"; } } while (false); | |||
| 289 | compressedFuncSize += rel.Offset - lastRelocEnd; | |||
| 290 | compressedFuncSize += | |||
| 291 | getRelocWidth(rel, file->calcNewValue(rel, tombstone, this)); | |||
| 292 | lastRelocEnd = rel.Offset + getRelocWidthPadded(rel); | |||
| 293 | } | |||
| 294 | LLVM_DEBUG(dbgs() << " final region: " << (end - lastRelocEnd) << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " final region: " << (end - lastRelocEnd) << "\n"; } } while (false); | |||
| 295 | compressedFuncSize += end - lastRelocEnd; | |||
| 296 | ||||
| 297 | // Now we know how long the resulting function is we can add the encoding | |||
| 298 | // of its length | |||
| 299 | uint8_t buf[5]; | |||
| 300 | compressedSize = compressedFuncSize + encodeULEB128(compressedFuncSize, buf); | |||
| 301 | ||||
| 302 | LLVM_DEBUG(dbgs() << " calculateSize orig: " << function->Size << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " calculateSize orig: " << function ->Size << "\n"; } } while (false); | |||
| 303 | LLVM_DEBUG(dbgs() << " calculateSize new: " << compressedSize << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " calculateSize new: " << compressedSize << "\n"; } } while (false); | |||
| 304 | } | |||
| 305 | ||||
| 306 | // Override the default writeTo method so that we can (optionally) write the | |||
| 307 | // compressed version of the function. | |||
| 308 | void InputFunction::writeCompressed(uint8_t *buf) const { | |||
| 309 | buf += outSecOff; | |||
| 310 | uint8_t *orig = buf; | |||
| 311 | (void)orig; | |||
| 312 | ||||
| 313 | const uint8_t *secStart = file->codeSection->Content.data(); | |||
| 314 | const uint8_t *funcStart = secStart + getInputSectionOffset(); | |||
| 315 | const uint8_t *end = funcStart + function->Size; | |||
| 316 | uint64_t tombstone = getTombstone(); | |||
| 317 | uint32_t count; | |||
| 318 | decodeULEB128(funcStart, &count); | |||
| 319 | funcStart += count; | |||
| 320 | ||||
| 321 | LLVM_DEBUG(dbgs() << "write func: " << name << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "write func: " << name << "\n"; } } while (false); | |||
| 322 | buf += encodeULEB128(compressedFuncSize, buf); | |||
| 323 | const uint8_t *lastRelocEnd = funcStart; | |||
| 324 | for (const WasmRelocation &rel : relocations) { | |||
| 325 | unsigned chunkSize = (secStart + rel.Offset) - lastRelocEnd; | |||
| 326 | LLVM_DEBUG(dbgs() << " write chunk: " << chunkSize << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " write chunk: " << chunkSize << "\n"; } } while (false); | |||
| 327 | memcpy(buf, lastRelocEnd, chunkSize); | |||
| 328 | buf += chunkSize; | |||
| 329 | buf += writeCompressedReloc(buf, rel, | |||
| 330 | file->calcNewValue(rel, tombstone, this)); | |||
| 331 | lastRelocEnd = secStart + rel.Offset + getRelocWidthPadded(rel); | |||
| 332 | } | |||
| 333 | ||||
| 334 | unsigned chunkSize = end - lastRelocEnd; | |||
| 335 | LLVM_DEBUG(dbgs() << " write final chunk: " << chunkSize << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " write final chunk: " << chunkSize << "\n"; } } while (false); | |||
| 336 | memcpy(buf, lastRelocEnd, chunkSize); | |||
| 337 | LLVM_DEBUG(dbgs() << " total: " << (buf + chunkSize - orig) << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << " total: " << (buf + chunkSize - orig) << "\n"; } } while (false); | |||
| 338 | } | |||
| 339 | ||||
| 340 | uint64_t InputChunk::getChunkOffset(uint64_t offset) const { | |||
| 341 | if (const auto *ms = dyn_cast<MergeInputChunk>(this)) { | |||
| 342 | LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "getChunkOffset(merged): " << name << "\n"; } } while (false); | |||
| 343 | LLVM_DEBUG(dbgs() << "offset: " << offset << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "offset: " << offset << "\n"; } } while (false); | |||
| 344 | LLVM_DEBUG(dbgs() << "parentOffset: " << ms->getParentOffset(offset)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "parentOffset: " << ms->getParentOffset (offset) << "\n"; } } while (false) | |||
| 345 | << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "parentOffset: " << ms->getParentOffset (offset) << "\n"; } } while (false); | |||
| 346 | assert(ms->parent)(static_cast <bool> (ms->parent) ? void (0) : __assert_fail ("ms->parent", "lld/wasm/InputChunks.cpp", 346, __extension__ __PRETTY_FUNCTION__)); | |||
| 347 | return ms->parent->getChunkOffset(ms->getParentOffset(offset)); | |||
| 348 | } | |||
| 349 | return outputSegmentOffset + offset; | |||
| 350 | } | |||
| 351 | ||||
| 352 | uint64_t InputChunk::getOffset(uint64_t offset) const { | |||
| 353 | return outSecOff + getChunkOffset(offset); | |||
| 354 | } | |||
| 355 | ||||
| 356 | uint64_t InputChunk::getVA(uint64_t offset) const { | |||
| 357 | return (outputSeg ? outputSeg->startVA : 0) + getChunkOffset(offset); | |||
| 358 | } | |||
| 359 | ||||
| 360 | // Generate code to apply relocations to the data section at runtime. | |||
| 361 | // This is only called when generating shared libraries (PIC) where address are | |||
| 362 | // not known at static link time. | |||
| 363 | void InputChunk::generateRelocationCode(raw_ostream &os) const { | |||
| 364 | LLVM_DEBUG(dbgs() << "generating runtime relocations: " << namedo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "generating runtime relocations: " << name << " count=" << relocations.size() << "\n"; } } while (false) | |||
| 365 | << " count=" << relocations.size() << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "generating runtime relocations: " << name << " count=" << relocations.size() << "\n"; } } while (false); | |||
| 366 | ||||
| 367 | bool is64 = config->is64.value_or(false); | |||
| 368 | unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST | |||
| 369 | : WASM_OPCODE_I32_CONST; | |||
| 370 | unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD | |||
| 371 | : WASM_OPCODE_I32_ADD; | |||
| 372 | ||||
| 373 | uint64_t tombstone = getTombstone(); | |||
| 374 | // TODO(sbc): Encode the relocations in the data section and write a loop | |||
| 375 | // here to apply them. | |||
| 376 | for (const WasmRelocation &rel : relocations) { | |||
| 377 | uint64_t offset = getVA(rel.Offset) - getInputSectionOffset(); | |||
| 378 | ||||
| 379 | Symbol *sym = file->getSymbol(rel); | |||
| 380 | if (!config->isPic && sym->isDefined()) | |||
| 381 | continue; | |||
| 382 | ||||
| 383 | LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "gen reloc: type=" << relocTypeToString (rel.Type) << " addend=" << rel.Addend << " index=" << rel.Index << " output offset=" << offset << "\n"; } } while (false) | |||
| 384 | << " addend=" << rel.Addend << " index=" << rel.Indexdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "gen reloc: type=" << relocTypeToString (rel.Type) << " addend=" << rel.Addend << " index=" << rel.Index << " output offset=" << offset << "\n"; } } while (false) | |||
| 385 | << " output offset=" << offset << "\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { dbgs() << "gen reloc: type=" << relocTypeToString (rel.Type) << " addend=" << rel.Addend << " index=" << rel.Index << " output offset=" << offset << "\n"; } } while (false); | |||
| 386 | ||||
| 387 | // Calculate the address at which to apply the relocation | |||
| 388 | writeU8(os, opcode_ptr_const, "CONST"); | |||
| 389 | writeSleb128(os, offset, "offset"); | |||
| 390 | ||||
| 391 | // In PIC mode we need to add the __memory_base | |||
| 392 | if (config->isPic) { | |||
| 393 | writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); | |||
| 394 | if (isTLS()) | |||
| 395 | writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "tls_base"); | |||
| 396 | else | |||
| 397 | writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base"); | |||
| 398 | writeU8(os, opcode_ptr_add, "ADD"); | |||
| 399 | } | |||
| 400 | ||||
| 401 | // Now figure out what we want to store at this location | |||
| 402 | bool is64 = relocIs64(rel.Type); | |||
| 403 | unsigned opcode_reloc_const = | |||
| 404 | is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST; | |||
| 405 | unsigned opcode_reloc_add = | |||
| 406 | is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD; | |||
| 407 | unsigned opcode_reloc_store = | |||
| 408 | is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE; | |||
| 409 | ||||
| 410 | if (sym->hasGOTIndex()) { | |||
| 411 | writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); | |||
| 412 | writeUleb128(os, sym->getGOTIndex(), "global index"); | |||
| 413 | if (rel.Addend) { | |||
| 414 | writeU8(os, opcode_reloc_const, "CONST"); | |||
| 415 | writeSleb128(os, rel.Addend, "addend"); | |||
| 416 | writeU8(os, opcode_reloc_add, "ADD"); | |||
| 417 | } | |||
| 418 | } else { | |||
| 419 | assert(config->isPic)(static_cast <bool> (config->isPic) ? void (0) : __assert_fail ("config->isPic", "lld/wasm/InputChunks.cpp", 419, __extension__ __PRETTY_FUNCTION__)); | |||
| 420 | const GlobalSymbol* baseSymbol = WasmSym::memoryBase; | |||
| 421 | if (rel.Type == R_WASM_TABLE_INDEX_I32 || | |||
| 422 | rel.Type == R_WASM_TABLE_INDEX_I64) | |||
| 423 | baseSymbol = WasmSym::tableBase; | |||
| 424 | else if (sym->isTLS()) | |||
| 425 | baseSymbol = WasmSym::tlsBase; | |||
| 426 | writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); | |||
| 427 | writeUleb128(os, baseSymbol->getGlobalIndex(), "base"); | |||
| 428 | writeU8(os, opcode_reloc_const, "CONST"); | |||
| 429 | writeSleb128(os, file->calcNewValue(rel, tombstone, this), "offset"); | |||
| 430 | writeU8(os, opcode_reloc_add, "ADD"); | |||
| 431 | } | |||
| 432 | ||||
| 433 | // Store that value at the virtual address | |||
| 434 | writeU8(os, opcode_reloc_store, "I32_STORE"); | |||
| 435 | writeUleb128(os, 2, "align"); | |||
| 436 | writeUleb128(os, 0, "offset"); | |||
| 437 | } | |||
| 438 | } | |||
| 439 | ||||
| 440 | // Split WASM_SEG_FLAG_STRINGS section. Such a section is a sequence of | |||
| 441 | // null-terminated strings. | |||
| 442 | void MergeInputChunk::splitStrings(ArrayRef<uint8_t> data) { | |||
| 443 | LLVM_DEBUG(llvm::dbgs() << "splitStrings\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("lld")) { llvm::dbgs() << "splitStrings\n"; } } while ( false); | |||
| 444 | size_t off = 0; | |||
| 445 | StringRef s = toStringRef(data); | |||
| 446 | ||||
| 447 | while (!s.empty()) { | |||
| 448 | size_t end = s.find(0); | |||
| 449 | if (end == StringRef::npos) | |||
| 450 | fatal(toString(this) + ": string is not null terminated"); | |||
| 451 | size_t size = end + 1; | |||
| 452 | ||||
| 453 | pieces.emplace_back(off, xxHash64(s.substr(0, size)), true); | |||
| 454 | s = s.substr(size); | |||
| 455 | off += size; | |||
| 456 | } | |||
| 457 | } | |||
| 458 | ||||
| 459 | // This function is called after we obtain a complete list of input sections | |||
| 460 | // that need to be linked. This is responsible to split section contents | |||
| 461 | // into small chunks for further processing. | |||
| 462 | // | |||
| 463 | // Note that this function is called from parallelForEach. This must be | |||
| 464 | // thread-safe (i.e. no memory allocation from the pools). | |||
| 465 | void MergeInputChunk::splitIntoPieces() { | |||
| 466 | assert(pieces.empty())(static_cast <bool> (pieces.empty()) ? void (0) : __assert_fail ("pieces.empty()", "lld/wasm/InputChunks.cpp", 466, __extension__ __PRETTY_FUNCTION__)); | |||
| 467 | // As of now we only support WASM_SEG_FLAG_STRINGS but in the future we | |||
| 468 | // could add other types of splitting (see ELF's splitIntoPieces). | |||
| 469 | assert(flags & WASM_SEG_FLAG_STRINGS)(static_cast <bool> (flags & WASM_SEG_FLAG_STRINGS) ? void (0) : __assert_fail ("flags & WASM_SEG_FLAG_STRINGS" , "lld/wasm/InputChunks.cpp", 469, __extension__ __PRETTY_FUNCTION__ )); | |||
| 470 | splitStrings(data()); | |||
| 471 | } | |||
| 472 | ||||
| 473 | SectionPiece *MergeInputChunk::getSectionPiece(uint64_t offset) { | |||
| 474 | if (this->data().size() <= offset) | |||
| 475 | fatal(toString(this) + ": offset is outside the section"); | |||
| 476 | ||||
| 477 | // If Offset is not at beginning of a section piece, it is not in the map. | |||
| 478 | // In that case we need to do a binary search of the original section piece | |||
| 479 | // vector. | |||
| 480 | auto it = partition_point( | |||
| 481 | pieces, [=](SectionPiece p) { return p.inputOff <= offset; }); | |||
| 482 | return &it[-1]; | |||
| 483 | } | |||
| 484 | ||||
| 485 | // Returns the offset in an output section for a given input offset. | |||
| 486 | // Because contents of a mergeable section is not contiguous in output, | |||
| 487 | // it is not just an addition to a base output offset. | |||
| 488 | uint64_t MergeInputChunk::getParentOffset(uint64_t offset) const { | |||
| 489 | // If Offset is not at beginning of a section piece, it is not in the map. | |||
| 490 | // In that case we need to search from the original section piece vector. | |||
| 491 | const SectionPiece *piece = getSectionPiece(offset); | |||
| 492 | uint64_t addend = offset - piece->inputOff; | |||
| 493 | return piece->outputOff + addend; | |||
| 494 | } | |||
| 495 | ||||
| 496 | void SyntheticMergedChunk::finalizeContents() { | |||
| 497 | // Add all string pieces to the string table builder to create section | |||
| 498 | // contents. | |||
| 499 | for (MergeInputChunk *sec : chunks) | |||
| 500 | for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) | |||
| 501 | if (sec->pieces[i].live) | |||
| 502 | builder.add(sec->getData(i)); | |||
| 503 | ||||
| 504 | // Fix the string table content. After this, the contents will never change. | |||
| 505 | builder.finalize(); | |||
| 506 | ||||
| 507 | // finalize() fixed tail-optimized strings, so we can now get | |||
| 508 | // offsets of strings. Get an offset for each string and save it | |||
| 509 | // to a corresponding SectionPiece for easy access. | |||
| 510 | for (MergeInputChunk *sec : chunks) | |||
| 511 | for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) | |||
| 512 | if (sec->pieces[i].live) | |||
| 513 | sec->pieces[i].outputOff = builder.getOffset(sec->getData(i)); | |||
| 514 | } | |||
| 515 | ||||
| 516 | uint64_t InputSection::getTombstoneForSection(StringRef name) { | |||
| 517 | // When a function is not live we need to update relocations referring to it. | |||
| 518 | // If they occur in DWARF debug symbols, we want to change the pc of the | |||
| 519 | // function to -1 to avoid overlapping with a valid range. However for the | |||
| 520 | // debug_ranges and debug_loc sections that would conflict with the existing | |||
| 521 | // meaning of -1 so we use -2. | |||
| 522 | // Returning 0 means there is no tombstone value for this section, and relocation | |||
| 523 | // will just use the addend. | |||
| 524 | if (!name.startswith(".debug_")) | |||
| 525 | return 0; | |||
| 526 | if (name.equals(".debug_ranges") || name.equals(".debug_loc")) | |||
| 527 | return UINT64_C(-2)-2UL; | |||
| 528 | return UINT64_C(-1)-1UL; | |||
| 529 | } | |||
| 530 | ||||
| 531 | } // namespace wasm | |||
| 532 | } // namespace lld |