1f61910d8SSam Clegg //===- InputChunks.cpp ----------------------------------------------------===// 25fa274beSSam Clegg // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65fa274beSSam Clegg // 75fa274beSSam Clegg //===----------------------------------------------------------------------===// 85fa274beSSam Clegg 95fa274beSSam Clegg #include "InputChunks.h" 10d96d9357SSam Clegg #include "Config.h" 115fa274beSSam Clegg #include "OutputSegment.h" 12bf450d90SRui Ueyama #include "WriterUtils.h" 13d96d9357SSam Clegg #include "lld/Common/ErrorHandler.h" 145fa274beSSam Clegg #include "lld/Common/LLVM.h" 15d96d9357SSam Clegg #include "llvm/Support/LEB128.h" 163b8d2be5SSam Clegg #include "llvm/Support/xxhash.h" 175fa274beSSam Clegg 185fa274beSSam Clegg #define DEBUG_TYPE "lld" 195fa274beSSam Clegg 205fa274beSSam Clegg using namespace llvm; 21d96d9357SSam Clegg using namespace llvm::wasm; 22e351c3a4SRui Ueyama using namespace llvm::support::endian; 235fa274beSSam Clegg 2433c59abfSFangrui Song namespace lld { 2533c59abfSFangrui Song StringRef relocTypeToString(uint8_t relocType) { 26136d27abSRui Ueyama switch (relocType) { 274821ebf7SHeejin Ahn #define WASM_RELOC(NAME, REL) \ 284821ebf7SHeejin Ahn case REL: \ 294821ebf7SHeejin Ahn return #NAME; 30c1be8230SSam Clegg #include "llvm/BinaryFormat/WasmRelocs.def" 31c1be8230SSam Clegg #undef WASM_RELOC 32c1be8230SSam Clegg } 33c1be8230SSam Clegg llvm_unreachable("unknown reloc type"); 34c1be8230SSam Clegg } 35c1be8230SSam Clegg 36b9a539c0SWouter van Oortmerssen bool relocIs64(uint8_t relocType) { 37b9a539c0SWouter van Oortmerssen switch (relocType) { 38b9a539c0SWouter van Oortmerssen case R_WASM_MEMORY_ADDR_LEB64: 39b9a539c0SWouter van Oortmerssen case R_WASM_MEMORY_ADDR_SLEB64: 40b9a539c0SWouter van Oortmerssen case R_WASM_MEMORY_ADDR_REL_SLEB64: 41b9a539c0SWouter van Oortmerssen case R_WASM_MEMORY_ADDR_I64: 424157b603SWouter van Oortmerssen case R_WASM_TABLE_INDEX_SLEB64: 434157b603SWouter van Oortmerssen case R_WASM_TABLE_INDEX_I64: 444157b603SWouter van Oortmerssen case R_WASM_FUNCTION_OFFSET_I64: 454157b603SWouter van Oortmerssen case R_WASM_TABLE_INDEX_REL_SLEB64: 46670944fbSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_TLS_SLEB64: 47b9a539c0SWouter van Oortmerssen return true; 48b9a539c0SWouter van Oortmerssen default: 49b9a539c0SWouter van Oortmerssen return false; 50b9a539c0SWouter van Oortmerssen } 51b9a539c0SWouter van Oortmerssen } 52b9a539c0SWouter van Oortmerssen 5333c59abfSFangrui Song std::string toString(const wasm::InputChunk *c) { 5487099a04SSam Clegg return (toString(c->file) + ":(" + c->name + ")").str(); 5581bee04bSRui Ueyama } 5681bee04bSRui Ueyama 5733c59abfSFangrui Song namespace wasm { 58c4d9aa1bSNicholas Wilson StringRef InputChunk::getComdatName() const { 59136d27abSRui Ueyama uint32_t index = getComdat(); 60136d27abSRui Ueyama if (index == UINT32_MAX) 61c4d9aa1bSNicholas Wilson return StringRef(); 62136d27abSRui Ueyama return file->getWasmObj()->linkingData().Comdats[index]; 63c4d9aa1bSNicholas Wilson } 64c4d9aa1bSNicholas Wilson 655a9b25e1SSam Clegg uint32_t InputChunk::getSize() const { 665a9b25e1SSam Clegg if (const auto *ms = dyn_cast<SyntheticMergedChunk>(this)) 675a9b25e1SSam Clegg return ms->builder.getSize(); 685a9b25e1SSam Clegg 695a9b25e1SSam Clegg if (const auto *f = dyn_cast<InputFunction>(this)) { 70*3792b362SFangrui Song if (ctx.arg.compressRelocations && f->file) { 715a9b25e1SSam Clegg return f->getCompressedSize(); 725a9b25e1SSam Clegg } 735a9b25e1SSam Clegg } 745a9b25e1SSam Clegg 755a9b25e1SSam Clegg return data().size(); 765a9b25e1SSam Clegg } 775a9b25e1SSam Clegg 785a9b25e1SSam Clegg uint32_t InputChunk::getInputSize() const { 795a9b25e1SSam Clegg if (const auto *f = dyn_cast<InputFunction>(this)) 805a9b25e1SSam Clegg return f->function->Size; 815a9b25e1SSam Clegg return getSize(); 825a9b25e1SSam Clegg } 835a9b25e1SSam Clegg 84bf450d90SRui Ueyama // Copy this input chunk to an mmap'ed output file and apply relocations. 85136d27abSRui Ueyama void InputChunk::writeTo(uint8_t *buf) const { 865a9b25e1SSam Clegg if (const auto *f = dyn_cast<InputFunction>(this)) { 87*3792b362SFangrui Song if (file && ctx.arg.compressRelocations) 885a9b25e1SSam Clegg return f->writeCompressed(buf); 895a9b25e1SSam Clegg } else if (const auto *ms = dyn_cast<SyntheticMergedChunk>(this)) { 905a9b25e1SSam Clegg ms->builder.write(buf + outSecOff); 915a9b25e1SSam Clegg // Apply relocations 925a9b25e1SSam Clegg ms->relocate(buf + outSecOff); 935a9b25e1SSam Clegg return; 945a9b25e1SSam Clegg } 955a9b25e1SSam Clegg 96bf450d90SRui Ueyama // Copy contents 9714ffbb84SSam Clegg memcpy(buf + outSecOff, data().data(), data().size()); 98c06d94aaSRui Ueyama 99bf450d90SRui Ueyama // Apply relocations 1003b8d2be5SSam Clegg relocate(buf + outSecOff); 1013b8d2be5SSam Clegg } 1023b8d2be5SSam Clegg 1033b8d2be5SSam Clegg void InputChunk::relocate(uint8_t *buf) const { 104136d27abSRui Ueyama if (relocations.empty()) 105bf450d90SRui Ueyama return; 106c06d94aaSRui Ueyama 107cf2b8722SSam Clegg LLVM_DEBUG(dbgs() << "applying relocations: " << toString(this) 108136d27abSRui Ueyama << " count=" << relocations.size() << "\n"); 1093b8d2be5SSam Clegg int32_t inputSectionOffset = getInputSectionOffset(); 1105a9b25e1SSam Clegg uint64_t tombstone = getTombstone(); 111bf450d90SRui Ueyama 112136d27abSRui Ueyama for (const WasmRelocation &rel : relocations) { 1133b8d2be5SSam Clegg uint8_t *loc = buf + rel.Offset - inputSectionOffset; 114136d27abSRui Ueyama LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type)); 115136d27abSRui Ueyama if (rel.Type != R_WASM_TYPE_INDEX_LEB) 116136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName()); 117136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index 118d01e673aSSam Clegg << " offset=" << rel.Offset << "\n"); 1194c75521cSSam Clegg // TODO(sbc): Check that the value is within the range of the 1204c75521cSSam Clegg // relocation type below. Most likely we must error out here 1214c75521cSSam Clegg // if its not with range. 1224c75521cSSam Clegg uint64_t value = file->calcNewValue(rel, tombstone, this); 123bf450d90SRui Ueyama 124136d27abSRui Ueyama switch (rel.Type) { 12579e33171SSam Clegg case R_WASM_TYPE_INDEX_LEB: 12679e33171SSam Clegg case R_WASM_FUNCTION_INDEX_LEB: 12779e33171SSam Clegg case R_WASM_GLOBAL_INDEX_LEB: 1281d891d44SHeejin Ahn case R_WASM_TAG_INDEX_LEB: 12979e33171SSam Clegg case R_WASM_MEMORY_ADDR_LEB: 13053e3b81fSAndy Wingo case R_WASM_TABLE_NUMBER_LEB: 1314c75521cSSam Clegg encodeULEB128(static_cast<uint32_t>(value), loc, 5); 132d96d9357SSam Clegg break; 1333b29376eSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_LEB64: 1343b29376eSWouter van Oortmerssen encodeULEB128(value, loc, 10); 1353b29376eSWouter van Oortmerssen break; 13679e33171SSam Clegg case R_WASM_TABLE_INDEX_SLEB: 1372a7cac93SSam Clegg case R_WASM_TABLE_INDEX_REL_SLEB: 13879e33171SSam Clegg case R_WASM_MEMORY_ADDR_SLEB: 1392a7cac93SSam Clegg case R_WASM_MEMORY_ADDR_REL_SLEB: 140a28a4662SSam Clegg case R_WASM_MEMORY_ADDR_TLS_SLEB: 141136d27abSRui Ueyama encodeSLEB128(static_cast<int32_t>(value), loc, 5); 142d96d9357SSam Clegg break; 143cc1b9b68SWouter van Oortmerssen case R_WASM_TABLE_INDEX_SLEB64: 1443a293cbfSWouter van Oortmerssen case R_WASM_TABLE_INDEX_REL_SLEB64: 1453b29376eSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_SLEB64: 1463b29376eSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_REL_SLEB64: 147670944fbSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_TLS_SLEB64: 1483b29376eSWouter van Oortmerssen encodeSLEB128(static_cast<int64_t>(value), loc, 10); 1493b29376eSWouter van Oortmerssen break; 15079e33171SSam Clegg case R_WASM_TABLE_INDEX_I32: 15179e33171SSam Clegg case R_WASM_MEMORY_ADDR_I32: 15279e33171SSam Clegg case R_WASM_FUNCTION_OFFSET_I32: 153220fe00aSBrendan Dahl case R_WASM_FUNCTION_INDEX_I32: 15479e33171SSam Clegg case R_WASM_SECTION_OFFSET_I32: 15548139ebcSWouter van Oortmerssen case R_WASM_GLOBAL_INDEX_I32: 156aa0c571aSYuta Saito case R_WASM_MEMORY_ADDR_LOCREL_I32: 157136d27abSRui Ueyama write32le(loc, value); 158d96d9357SSam Clegg break; 159cc1b9b68SWouter van Oortmerssen case R_WASM_TABLE_INDEX_I64: 1603b29376eSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_I64: 16116f02431SWouter van Oortmerssen case R_WASM_FUNCTION_OFFSET_I64: 1623b29376eSWouter van Oortmerssen write64le(loc, value); 1633b29376eSWouter van Oortmerssen break; 164d96d9357SSam Clegg default: 165d96d9357SSam Clegg llvm_unreachable("unknown relocation type"); 166d96d9357SSam Clegg } 167d96d9357SSam Clegg } 168d96d9357SSam Clegg } 169d96d9357SSam Clegg 170bf450d90SRui Ueyama // Copy relocation entries to a given output stream. 171bf450d90SRui Ueyama // This function is used only when a user passes "-r". For a regular link, 172bf450d90SRui Ueyama // we consume relocations instead of copying them to an output file. 173136d27abSRui Ueyama void InputChunk::writeRelocations(raw_ostream &os) const { 174136d27abSRui Ueyama if (relocations.empty()) 17550686856SSam Clegg return; 176bf450d90SRui Ueyama 17714ffbb84SSam Clegg int32_t off = outSecOff - getInputSectionOffset(); 178136d27abSRui Ueyama LLVM_DEBUG(dbgs() << "writeRelocations: " << file->getName() 179136d27abSRui Ueyama << " offset=" << Twine(off) << "\n"); 180d96d9357SSam Clegg 181136d27abSRui Ueyama for (const WasmRelocation &rel : relocations) { 182136d27abSRui Ueyama writeUleb128(os, rel.Type, "reloc type"); 183136d27abSRui Ueyama writeUleb128(os, rel.Offset + off, "reloc offset"); 184136d27abSRui Ueyama writeUleb128(os, file->calcNewIndex(rel), "reloc index"); 185d96d9357SSam Clegg 186136d27abSRui Ueyama if (relocTypeHasAddend(rel.Type)) 187136d27abSRui Ueyama writeSleb128(os, file->calcNewAddend(rel), "reloc addend"); 188d96d9357SSam Clegg } 189d96d9357SSam Clegg } 19050686856SSam Clegg 1915a9b25e1SSam Clegg uint64_t InputChunk::getTombstone() const { 1925a9b25e1SSam Clegg if (const auto *s = dyn_cast<InputSection>(this)) { 1935a9b25e1SSam Clegg return s->tombstoneValue; 1945a9b25e1SSam Clegg } 1955a9b25e1SSam Clegg 1965a9b25e1SSam Clegg return 0; 1975a9b25e1SSam Clegg } 1985a9b25e1SSam Clegg 199136d27abSRui Ueyama void InputFunction::setFunctionIndex(uint32_t index) { 20087099a04SSam Clegg LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << name << " -> " 20187099a04SSam Clegg << index << "\n"); 202e3f3ccf8SSam Clegg assert(!hasFunctionIndex()); 203136d27abSRui Ueyama functionIndex = index; 2049ea500b4SEric Christopher } 20567abf539SSam Clegg 206136d27abSRui Ueyama void InputFunction::setTableIndex(uint32_t index) { 20787099a04SSam Clegg LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << name << " -> " 208136d27abSRui Ueyama << index << "\n"); 20967abf539SSam Clegg assert(!hasTableIndex()); 210136d27abSRui Ueyama tableIndex = index; 21167abf539SSam Clegg } 212fb983cdaSSam Clegg 213fb983cdaSSam Clegg // Write a relocation value without padding and return the number of bytes 214fb983cdaSSam Clegg // witten. 215136d27abSRui Ueyama static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel, 2163b29376eSWouter van Oortmerssen uint64_t value) { 217136d27abSRui Ueyama switch (rel.Type) { 21879e33171SSam Clegg case R_WASM_TYPE_INDEX_LEB: 21979e33171SSam Clegg case R_WASM_FUNCTION_INDEX_LEB: 22079e33171SSam Clegg case R_WASM_GLOBAL_INDEX_LEB: 2211d891d44SHeejin Ahn case R_WASM_TAG_INDEX_LEB: 22279e33171SSam Clegg case R_WASM_MEMORY_ADDR_LEB: 2233b29376eSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_LEB64: 22453e3b81fSAndy Wingo case R_WASM_TABLE_NUMBER_LEB: 225136d27abSRui Ueyama return encodeULEB128(value, buf); 22679e33171SSam Clegg case R_WASM_TABLE_INDEX_SLEB: 227cc1b9b68SWouter van Oortmerssen case R_WASM_TABLE_INDEX_SLEB64: 22879e33171SSam Clegg case R_WASM_MEMORY_ADDR_SLEB: 2293b29376eSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_SLEB64: 2303b29376eSWouter van Oortmerssen return encodeSLEB128(static_cast<int64_t>(value), buf); 231fb983cdaSSam Clegg default: 232f377030aSSam Clegg llvm_unreachable("unexpected relocation type"); 233fb983cdaSSam Clegg } 234fb983cdaSSam Clegg } 235fb983cdaSSam Clegg 236136d27abSRui Ueyama static unsigned getRelocWidthPadded(const WasmRelocation &rel) { 237136d27abSRui Ueyama switch (rel.Type) { 23879e33171SSam Clegg case R_WASM_TYPE_INDEX_LEB: 23979e33171SSam Clegg case R_WASM_FUNCTION_INDEX_LEB: 24079e33171SSam Clegg case R_WASM_GLOBAL_INDEX_LEB: 2411d891d44SHeejin Ahn case R_WASM_TAG_INDEX_LEB: 24279e33171SSam Clegg case R_WASM_MEMORY_ADDR_LEB: 24353e3b81fSAndy Wingo case R_WASM_TABLE_NUMBER_LEB: 24479e33171SSam Clegg case R_WASM_TABLE_INDEX_SLEB: 24579e33171SSam Clegg case R_WASM_MEMORY_ADDR_SLEB: 246fb983cdaSSam Clegg return 5; 247cc1b9b68SWouter van Oortmerssen case R_WASM_TABLE_INDEX_SLEB64: 2483b29376eSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_LEB64: 2493b29376eSWouter van Oortmerssen case R_WASM_MEMORY_ADDR_SLEB64: 2503b29376eSWouter van Oortmerssen return 10; 251fb983cdaSSam Clegg default: 252f377030aSSam Clegg llvm_unreachable("unexpected relocation type"); 253fb983cdaSSam Clegg } 254fb983cdaSSam Clegg } 255fb983cdaSSam Clegg 2563b29376eSWouter van Oortmerssen static unsigned getRelocWidth(const WasmRelocation &rel, uint64_t value) { 2573b29376eSWouter van Oortmerssen uint8_t buf[10]; 258136d27abSRui Ueyama return writeCompressedReloc(buf, rel, value); 259fb983cdaSSam Clegg } 260fb983cdaSSam Clegg 261fb983cdaSSam Clegg // Relocations of type LEB and SLEB in the code section are padded to 5 bytes 262fb983cdaSSam Clegg // so that a fast linker can blindly overwrite them without needing to worry 263fb983cdaSSam Clegg // about the number of bytes needed to encode the values. 264fb983cdaSSam Clegg // However, for optimal output the code section can be compressed to remove 265fb983cdaSSam Clegg // the padding then outputting non-relocatable files. 266fb983cdaSSam Clegg // In this case we need to perform a size calculation based on the value at each 267fb983cdaSSam Clegg // relocation. At best we end up saving 4 bytes for each relocation entry. 268fb983cdaSSam Clegg // 269fb983cdaSSam Clegg // This function only computes the final output size. It must be called 270fb983cdaSSam Clegg // before getSize() is used to calculate of layout of the code section. 271fb983cdaSSam Clegg void InputFunction::calculateSize() { 272*3792b362SFangrui Song if (!file || !ctx.arg.compressRelocations) 273fb983cdaSSam Clegg return; 274fb983cdaSSam Clegg 27587099a04SSam Clegg LLVM_DEBUG(dbgs() << "calculateSize: " << name << "\n"); 276fb983cdaSSam Clegg 277136d27abSRui Ueyama const uint8_t *secStart = file->codeSection->Content.data(); 278136d27abSRui Ueyama const uint8_t *funcStart = secStart + getInputSectionOffset(); 279136d27abSRui Ueyama uint32_t functionSizeLength; 280136d27abSRui Ueyama decodeULEB128(funcStart, &functionSizeLength); 281fb983cdaSSam Clegg 282136d27abSRui Ueyama uint32_t start = getInputSectionOffset(); 283136d27abSRui Ueyama uint32_t end = start + function->Size; 284fb983cdaSSam Clegg 2855a9b25e1SSam Clegg uint64_t tombstone = getTombstone(); 2868b8088acSEric Leese 287136d27abSRui Ueyama uint32_t lastRelocEnd = start + functionSizeLength; 288136d27abSRui Ueyama for (const WasmRelocation &rel : relocations) { 289136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " region: " << (rel.Offset - lastRelocEnd) << "\n"); 290136d27abSRui Ueyama compressedFuncSize += rel.Offset - lastRelocEnd; 291aa0c571aSYuta Saito compressedFuncSize += 292aa0c571aSYuta Saito getRelocWidth(rel, file->calcNewValue(rel, tombstone, this)); 293136d27abSRui Ueyama lastRelocEnd = rel.Offset + getRelocWidthPadded(rel); 294fb983cdaSSam Clegg } 295136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " final region: " << (end - lastRelocEnd) << "\n"); 296136d27abSRui Ueyama compressedFuncSize += end - lastRelocEnd; 297fb983cdaSSam Clegg 298fb983cdaSSam Clegg // Now we know how long the resulting function is we can add the encoding 299fb983cdaSSam Clegg // of its length 300136d27abSRui Ueyama uint8_t buf[5]; 301136d27abSRui Ueyama compressedSize = compressedFuncSize + encodeULEB128(compressedFuncSize, buf); 302fb983cdaSSam Clegg 303136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " calculateSize orig: " << function->Size << "\n"); 304136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " calculateSize new: " << compressedSize << "\n"); 305fb983cdaSSam Clegg } 306fb983cdaSSam Clegg 307fb983cdaSSam Clegg // Override the default writeTo method so that we can (optionally) write the 308fb983cdaSSam Clegg // compressed version of the function. 3095a9b25e1SSam Clegg void InputFunction::writeCompressed(uint8_t *buf) const { 31014ffbb84SSam Clegg buf += outSecOff; 311136d27abSRui Ueyama uint8_t *orig = buf; 312136d27abSRui Ueyama (void)orig; 313fb983cdaSSam Clegg 314136d27abSRui Ueyama const uint8_t *secStart = file->codeSection->Content.data(); 315136d27abSRui Ueyama const uint8_t *funcStart = secStart + getInputSectionOffset(); 316136d27abSRui Ueyama const uint8_t *end = funcStart + function->Size; 3175a9b25e1SSam Clegg uint64_t tombstone = getTombstone(); 318136d27abSRui Ueyama uint32_t count; 319136d27abSRui Ueyama decodeULEB128(funcStart, &count); 320136d27abSRui Ueyama funcStart += count; 321fb983cdaSSam Clegg 32287099a04SSam Clegg LLVM_DEBUG(dbgs() << "write func: " << name << "\n"); 323136d27abSRui Ueyama buf += encodeULEB128(compressedFuncSize, buf); 324136d27abSRui Ueyama const uint8_t *lastRelocEnd = funcStart; 325136d27abSRui Ueyama for (const WasmRelocation &rel : relocations) { 326136d27abSRui Ueyama unsigned chunkSize = (secStart + rel.Offset) - lastRelocEnd; 327136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " write chunk: " << chunkSize << "\n"); 328136d27abSRui Ueyama memcpy(buf, lastRelocEnd, chunkSize); 329136d27abSRui Ueyama buf += chunkSize; 330aa0c571aSYuta Saito buf += writeCompressedReloc(buf, rel, 331aa0c571aSYuta Saito file->calcNewValue(rel, tombstone, this)); 332136d27abSRui Ueyama lastRelocEnd = secStart + rel.Offset + getRelocWidthPadded(rel); 333fb983cdaSSam Clegg } 334fb983cdaSSam Clegg 335136d27abSRui Ueyama unsigned chunkSize = end - lastRelocEnd; 336136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " write final chunk: " << chunkSize << "\n"); 337136d27abSRui Ueyama memcpy(buf, lastRelocEnd, chunkSize); 338136d27abSRui Ueyama LLVM_DEBUG(dbgs() << " total: " << (buf + chunkSize - orig) << "\n"); 339fb983cdaSSam Clegg } 34009137be7SSam Clegg 34145b7cf99SSam Clegg uint64_t InputChunk::getChunkOffset(uint64_t offset) const { 3425a9b25e1SSam Clegg if (const auto *ms = dyn_cast<MergeInputChunk>(this)) { 34387099a04SSam Clegg LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n"); 3443b8d2be5SSam Clegg LLVM_DEBUG(dbgs() << "offset: " << offset << "\n"); 3453b8d2be5SSam Clegg LLVM_DEBUG(dbgs() << "parentOffset: " << ms->getParentOffset(offset) 3463b8d2be5SSam Clegg << "\n"); 3473b8d2be5SSam Clegg assert(ms->parent); 34845b7cf99SSam Clegg return ms->parent->getChunkOffset(ms->getParentOffset(offset)); 3493b8d2be5SSam Clegg } 3503b8d2be5SSam Clegg return outputSegmentOffset + offset; 3513b8d2be5SSam Clegg } 3523b8d2be5SSam Clegg 35345b7cf99SSam Clegg uint64_t InputChunk::getOffset(uint64_t offset) const { 35445b7cf99SSam Clegg return outSecOff + getChunkOffset(offset); 35545b7cf99SSam Clegg } 35645b7cf99SSam Clegg 3575a9b25e1SSam Clegg uint64_t InputChunk::getVA(uint64_t offset) const { 35845b7cf99SSam Clegg return (outputSeg ? outputSeg->startVA : 0) + getChunkOffset(offset); 35970f3c6e9SSam Clegg } 36070f3c6e9SSam Clegg 36109137be7SSam Clegg // Generate code to apply relocations to the data section at runtime. 3629f903475SNico Weber // This is only called when generating shared libraries (PIC) where address are 36309137be7SSam Clegg // not known at static link time. 364a5cd5d35SYAMAMOTO Takashi bool InputChunk::generateRelocationCode(raw_ostream &os) const { 36587099a04SSam Clegg LLVM_DEBUG(dbgs() << "generating runtime relocations: " << name 366136d27abSRui Ueyama << " count=" << relocations.size() << "\n"); 367b685ddf2SSam Clegg 368*3792b362SFangrui Song bool is64 = ctx.arg.is64.value_or(false); 369a5cd5d35SYAMAMOTO Takashi bool generated = false; 3704157b603SWouter van Oortmerssen unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST 37129f8c9f6SWouter van Oortmerssen : WASM_OPCODE_I32_CONST; 3724157b603SWouter van Oortmerssen unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD 37329f8c9f6SWouter van Oortmerssen : WASM_OPCODE_I32_ADD; 374b9a539c0SWouter van Oortmerssen 3755a9b25e1SSam Clegg uint64_t tombstone = getTombstone(); 376b685ddf2SSam Clegg // TODO(sbc): Encode the relocations in the data section and write a loop 377b685ddf2SSam Clegg // here to apply them. 378136d27abSRui Ueyama for (const WasmRelocation &rel : relocations) { 37914ffbb84SSam Clegg uint64_t offset = getVA(rel.Offset) - getInputSectionOffset(); 380b685ddf2SSam Clegg 38186c90f9bSSam Clegg Symbol *sym = file->getSymbol(rel); 382a5cd5d35SYAMAMOTO Takashi // Runtime relocations are needed when we don't know the address of 383a5cd5d35SYAMAMOTO Takashi // a symbol statically. 384a5cd5d35SYAMAMOTO Takashi bool requiresRuntimeReloc = ctx.isPic || sym->hasGOTIndex(); 385a5cd5d35SYAMAMOTO Takashi if (!requiresRuntimeReloc) 38686c90f9bSSam Clegg continue; 38786c90f9bSSam Clegg 388136d27abSRui Ueyama LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type) 389136d27abSRui Ueyama << " addend=" << rel.Addend << " index=" << rel.Index 39014ffbb84SSam Clegg << " output offset=" << offset << "\n"); 39109137be7SSam Clegg 392a0495e6bSSam Clegg // Calculate the address at which to apply the relocation 393b9a539c0SWouter van Oortmerssen writeU8(os, opcode_ptr_const, "CONST"); 39414ffbb84SSam Clegg writeSleb128(os, offset, "offset"); 395b9a539c0SWouter van Oortmerssen 39686c90f9bSSam Clegg // In PIC mode we need to add the __memory_base 397184c22ddSSam Clegg if (ctx.isPic) { 39886c90f9bSSam Clegg writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); 399a0495e6bSSam Clegg if (isTLS()) 400a0495e6bSSam Clegg writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "tls_base"); 401a0495e6bSSam Clegg else 40286c90f9bSSam Clegg writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base"); 40386c90f9bSSam Clegg writeU8(os, opcode_ptr_add, "ADD"); 40486c90f9bSSam Clegg } 40586c90f9bSSam Clegg 40686c90f9bSSam Clegg // Now figure out what we want to store at this location 407b9a539c0SWouter van Oortmerssen bool is64 = relocIs64(rel.Type); 408b9a539c0SWouter van Oortmerssen unsigned opcode_reloc_const = 409b9a539c0SWouter van Oortmerssen is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST; 410b9a539c0SWouter van Oortmerssen unsigned opcode_reloc_add = 411b9a539c0SWouter van Oortmerssen is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD; 412b9a539c0SWouter van Oortmerssen unsigned opcode_reloc_store = 413b9a539c0SWouter van Oortmerssen is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE; 41409137be7SSam Clegg 415136d27abSRui Ueyama if (sym->hasGOTIndex()) { 416136d27abSRui Ueyama writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); 417136d27abSRui Ueyama writeUleb128(os, sym->getGOTIndex(), "global index"); 418136d27abSRui Ueyama if (rel.Addend) { 419b9a539c0SWouter van Oortmerssen writeU8(os, opcode_reloc_const, "CONST"); 420136d27abSRui Ueyama writeSleb128(os, rel.Addend, "addend"); 421b9a539c0SWouter van Oortmerssen writeU8(os, opcode_reloc_add, "ADD"); 42209137be7SSam Clegg } 423b685ddf2SSam Clegg } else { 424184c22ddSSam Clegg assert(ctx.isPic); 425136d27abSRui Ueyama const GlobalSymbol* baseSymbol = WasmSym::memoryBase; 426cc1b9b68SWouter van Oortmerssen if (rel.Type == R_WASM_TABLE_INDEX_I32 || 427cc1b9b68SWouter van Oortmerssen rel.Type == R_WASM_TABLE_INDEX_I64) 428136d27abSRui Ueyama baseSymbol = WasmSym::tableBase; 429a0495e6bSSam Clegg else if (sym->isTLS()) 430a0495e6bSSam Clegg baseSymbol = WasmSym::tlsBase; 431136d27abSRui Ueyama writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); 432136d27abSRui Ueyama writeUleb128(os, baseSymbol->getGlobalIndex(), "base"); 433b9a539c0SWouter van Oortmerssen writeU8(os, opcode_reloc_const, "CONST"); 434aa0c571aSYuta Saito writeSleb128(os, file->calcNewValue(rel, tombstone, this), "offset"); 435b9a539c0SWouter van Oortmerssen writeU8(os, opcode_reloc_add, "ADD"); 43609137be7SSam Clegg } 43709137be7SSam Clegg 43809137be7SSam Clegg // Store that value at the virtual address 439b9a539c0SWouter van Oortmerssen writeU8(os, opcode_reloc_store, "I32_STORE"); 440136d27abSRui Ueyama writeUleb128(os, 2, "align"); 441136d27abSRui Ueyama writeUleb128(os, 0, "offset"); 442a5cd5d35SYAMAMOTO Takashi generated = true; 44309137be7SSam Clegg } 444a5cd5d35SYAMAMOTO Takashi return generated; 44509137be7SSam Clegg } 44633c59abfSFangrui Song 4473b8d2be5SSam Clegg // Split WASM_SEG_FLAG_STRINGS section. Such a section is a sequence of 4483b8d2be5SSam Clegg // null-terminated strings. 4495a9b25e1SSam Clegg void MergeInputChunk::splitStrings(ArrayRef<uint8_t> data) { 4503b8d2be5SSam Clegg LLVM_DEBUG(llvm::dbgs() << "splitStrings\n"); 4513b8d2be5SSam Clegg size_t off = 0; 4523b8d2be5SSam Clegg StringRef s = toStringRef(data); 4533b8d2be5SSam Clegg 4543b8d2be5SSam Clegg while (!s.empty()) { 4553b8d2be5SSam Clegg size_t end = s.find(0); 4563b8d2be5SSam Clegg if (end == StringRef::npos) 4573b8d2be5SSam Clegg fatal(toString(this) + ": string is not null terminated"); 4583b8d2be5SSam Clegg size_t size = end + 1; 4593b8d2be5SSam Clegg 460a3622ac8SFangrui Song pieces.emplace_back(off, xxh3_64bits(s.substr(0, size)), true); 4613b8d2be5SSam Clegg s = s.substr(size); 4623b8d2be5SSam Clegg off += size; 4633b8d2be5SSam Clegg } 4643b8d2be5SSam Clegg } 4653b8d2be5SSam Clegg 4663b8d2be5SSam Clegg // This function is called after we obtain a complete list of input sections 4673b8d2be5SSam Clegg // that need to be linked. This is responsible to split section contents 4683b8d2be5SSam Clegg // into small chunks for further processing. 4693b8d2be5SSam Clegg // 4703b8d2be5SSam Clegg // Note that this function is called from parallelForEach. This must be 4713b8d2be5SSam Clegg // thread-safe (i.e. no memory allocation from the pools). 4725a9b25e1SSam Clegg void MergeInputChunk::splitIntoPieces() { 4733b8d2be5SSam Clegg assert(pieces.empty()); 4743b8d2be5SSam Clegg // As of now we only support WASM_SEG_FLAG_STRINGS but in the future we 4753b8d2be5SSam Clegg // could add other types of splitting (see ELF's splitIntoPieces). 4765a9b25e1SSam Clegg assert(flags & WASM_SEG_FLAG_STRINGS); 4773b8d2be5SSam Clegg splitStrings(data()); 4783b8d2be5SSam Clegg } 4793b8d2be5SSam Clegg 4805a9b25e1SSam Clegg SectionPiece *MergeInputChunk::getSectionPiece(uint64_t offset) { 4813b8d2be5SSam Clegg if (this->data().size() <= offset) 4823b8d2be5SSam Clegg fatal(toString(this) + ": offset is outside the section"); 4833b8d2be5SSam Clegg 4843b8d2be5SSam Clegg // If Offset is not at beginning of a section piece, it is not in the map. 4853b8d2be5SSam Clegg // In that case we need to do a binary search of the original section piece 4863b8d2be5SSam Clegg // vector. 4873b8d2be5SSam Clegg auto it = partition_point( 4885a9b25e1SSam Clegg pieces, [=](SectionPiece p) { return p.inputOff <= offset; }); 4893b8d2be5SSam Clegg return &it[-1]; 4903b8d2be5SSam Clegg } 4913b8d2be5SSam Clegg 4923b8d2be5SSam Clegg // Returns the offset in an output section for a given input offset. 4933b8d2be5SSam Clegg // Because contents of a mergeable section is not contiguous in output, 4943b8d2be5SSam Clegg // it is not just an addition to a base output offset. 4955a9b25e1SSam Clegg uint64_t MergeInputChunk::getParentOffset(uint64_t offset) const { 4963b8d2be5SSam Clegg // If Offset is not at beginning of a section piece, it is not in the map. 4973b8d2be5SSam Clegg // In that case we need to search from the original section piece vector. 4985a9b25e1SSam Clegg const SectionPiece *piece = getSectionPiece(offset); 4993b8d2be5SSam Clegg uint64_t addend = offset - piece->inputOff; 5003b8d2be5SSam Clegg return piece->outputOff + addend; 5013b8d2be5SSam Clegg } 5023b8d2be5SSam Clegg 5035a9b25e1SSam Clegg void SyntheticMergedChunk::finalizeContents() { 5043b8d2be5SSam Clegg // Add all string pieces to the string table builder to create section 5053b8d2be5SSam Clegg // contents. 5065a9b25e1SSam Clegg for (MergeInputChunk *sec : chunks) 5073b8d2be5SSam Clegg for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) 5083b8d2be5SSam Clegg if (sec->pieces[i].live) 5093b8d2be5SSam Clegg builder.add(sec->getData(i)); 5103b8d2be5SSam Clegg 5113b8d2be5SSam Clegg // Fix the string table content. After this, the contents will never change. 5123b8d2be5SSam Clegg builder.finalize(); 5133b8d2be5SSam Clegg 5143b8d2be5SSam Clegg // finalize() fixed tail-optimized strings, so we can now get 5153b8d2be5SSam Clegg // offsets of strings. Get an offset for each string and save it 5163b8d2be5SSam Clegg // to a corresponding SectionPiece for easy access. 5175a9b25e1SSam Clegg for (MergeInputChunk *sec : chunks) 5183b8d2be5SSam Clegg for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) 5193b8d2be5SSam Clegg if (sec->pieces[i].live) 5203b8d2be5SSam Clegg sec->pieces[i].outputOff = builder.getOffset(sec->getData(i)); 5213b8d2be5SSam Clegg } 5223b8d2be5SSam Clegg 5238b8088acSEric Leese uint64_t InputSection::getTombstoneForSection(StringRef name) { 5248b8088acSEric Leese // When a function is not live we need to update relocations referring to it. 5258b8088acSEric Leese // If they occur in DWARF debug symbols, we want to change the pc of the 5268b8088acSEric Leese // function to -1 to avoid overlapping with a valid range. However for the 5278b8088acSEric Leese // debug_ranges and debug_loc sections that would conflict with the existing 5288b8088acSEric Leese // meaning of -1 so we use -2. 529f841ca0cSKazu Hirata if (name == ".debug_ranges" || name == ".debug_loc") 5308b8088acSEric Leese return UINT64_C(-2); 531220fe00aSBrendan Dahl if (name.starts_with(".debug_")) 5328b8088acSEric Leese return UINT64_C(-1); 533220fe00aSBrendan Dahl // If the function occurs in an function attribute section change it to -1 since 534220fe00aSBrendan Dahl // 0 is a valid function index. 535220fe00aSBrendan Dahl if (name.starts_with("llvm.func_attr.")) 536220fe00aSBrendan Dahl return UINT64_C(-1); 537220fe00aSBrendan Dahl // Returning 0 means there is no tombstone value for this section, and relocation 538220fe00aSBrendan Dahl // will just use the addend. 539220fe00aSBrendan Dahl return 0; 5408b8088acSEric Leese } 5418b8088acSEric Leese 54233c59abfSFangrui Song } // namespace wasm 54333c59abfSFangrui Song } // namespace lld 544