227 lines · cpp
1//===- Relocations.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 "Relocations.h"10 11#include "InputChunks.h"12#include "OutputSegment.h"13#include "SymbolTable.h"14#include "SyntheticSections.h"15 16using namespace llvm;17using namespace llvm::wasm;18 19namespace lld::wasm {20 21static bool requiresGOTAccess(const Symbol *sym) {22 if (sym->isShared())23 return true;24 if (!ctx.isPic &&25 ctx.arg.unresolvedSymbols != UnresolvedPolicy::ImportDynamic)26 return false;27 if (sym->isHidden() || sym->isLocal())28 return false;29 // With `-Bsymbolic` (or when building an executable) as don't need to use30 // the GOT for symbols that are defined within the current module.31 if (sym->isDefined() && (!ctx.arg.shared || ctx.arg.bsymbolic))32 return false;33 return true;34}35 36static bool allowUndefined(const Symbol* sym) {37 // Symbols that are explicitly imported are always allowed to be undefined at38 // link time.39 if (sym->isImported())40 return true;41 if (isa<UndefinedFunction>(sym) && ctx.arg.importUndefined)42 return true;43 44 return ctx.arg.allowUndefinedSymbols.count(sym->getName()) != 0;45}46 47static void reportUndefined(ObjFile *file, Symbol *sym) {48 if (!allowUndefined(sym)) {49 switch (ctx.arg.unresolvedSymbols) {50 case UnresolvedPolicy::ReportError:51 error(toString(file) + ": undefined symbol: " + toString(*sym));52 break;53 case UnresolvedPolicy::Warn:54 warn(toString(file) + ": undefined symbol: " + toString(*sym));55 break;56 case UnresolvedPolicy::Ignore:57 LLVM_DEBUG(dbgs() << "ignoring undefined symbol: " + toString(*sym) +58 "\n");59 break;60 case UnresolvedPolicy::ImportDynamic:61 break;62 }63 64 if (auto *f = dyn_cast<UndefinedFunction>(sym)) {65 if (!f->stubFunction &&66 ctx.arg.unresolvedSymbols != UnresolvedPolicy::ImportDynamic &&67 !ctx.arg.importUndefined) {68 f->stubFunction = symtab->createUndefinedStub(*f->getSignature());69 f->stubFunction->markLive();70 // Mark the function itself as a stub which prevents it from being71 // assigned a table entry.72 f->isStub = true;73 }74 }75 }76}77 78static void addGOTEntry(Symbol *sym) {79 if (requiresGOTAccess(sym))80 out.importSec->addGOTEntry(sym);81 else82 out.globalSec->addInternalGOTEntry(sym);83}84 85void scanRelocations(InputChunk *chunk) {86 if (!chunk->live)87 return;88 ObjFile *file = chunk->file;89 ArrayRef<WasmSignature> types = file->getWasmObj()->types();90 for (const WasmRelocation &reloc : chunk->getRelocations()) {91 if (reloc.Type == R_WASM_TYPE_INDEX_LEB) {92 // Mark target type as live93 file->typeMap[reloc.Index] =94 out.typeSec->registerType(types[reloc.Index]);95 file->typeIsUsed[reloc.Index] = true;96 continue;97 }98 99 // Other relocation types all have a corresponding symbol100 Symbol *sym = file->getSymbols()[reloc.Index];101 102 switch (reloc.Type) {103 case R_WASM_TABLE_INDEX_I32:104 case R_WASM_TABLE_INDEX_I64:105 case R_WASM_TABLE_INDEX_SLEB:106 case R_WASM_TABLE_INDEX_SLEB64:107 case R_WASM_TABLE_INDEX_REL_SLEB:108 case R_WASM_TABLE_INDEX_REL_SLEB64:109 if (requiresGOTAccess(sym))110 break;111 out.elemSec->addEntry(cast<FunctionSymbol>(sym));112 break;113 case R_WASM_MEMORY_ADDR_LEB:114 case R_WASM_MEMORY_ADDR_LEB64:115 case R_WASM_MEMORY_ADDR_SLEB:116 case R_WASM_MEMORY_ADDR_SLEB64:117 case R_WASM_MEMORY_ADDR_I32:118 case R_WASM_MEMORY_ADDR_I64:119 // glibc/wasm32 (HWJS-B-1): after commit 12776b692 a strong undefined-DATA120 // reference (e.g. the target of a GNU-as alias `memset = __GI_memset`)121 // can resolve to a real FUNCTION definition. Taking the address of that122 // symbol yields a function pointer, i.e. its index in123 // __indirect_function_table. Register it in the indirect function table124 // exactly as a R_WASM_TABLE_INDEX_* reference would, so a real (non-zero)125 // table index is assigned; ObjFile::calcNewValue then emits that index.126 // Data (non-function) targets are unaffected and take the normal path.127 if (auto *f = dyn_cast<FunctionSymbol>(sym))128 if (!requiresGOTAccess(sym)) {129 // Unlike a normal R_WASM_TABLE_INDEX_* reference, an object that130 // takes a function's address through a *data* relocation need not131 // import __indirect_function_table, so the table symbol may not yet132 // exist. Materialize it (as ensureIndirectFunctionTable does for GOT133 // function entries) before adding the table entry.134 if (!ctx.sym.indirectFunctionTable)135 ctx.sym.indirectFunctionTable =136 symtab->resolveIndirectFunctionTable(/*required=*/true);137 out.elemSec->addEntry(f);138 }139 break;140 case R_WASM_GLOBAL_INDEX_LEB:141 case R_WASM_GLOBAL_INDEX_I32:142 if (!isa<GlobalSymbol>(sym))143 addGOTEntry(sym);144 break;145 case R_WASM_MEMORY_ADDR_TLS_SLEB:146 case R_WASM_MEMORY_ADDR_TLS_SLEB64:147 if (!sym->isDefined()) {148 error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +149 " cannot be used against an undefined symbol `" + toString(*sym) +150 "`");151 }152 // In single-threaded builds TLS is lowered away and TLS data can be153 // merged with normal data and allowing TLS relocation in non-TLS154 // segments.155 if (ctx.arg.sharedMemory) {156 if (!sym->isTLS()) {157 error(toString(file) + ": relocation " +158 relocTypeToString(reloc.Type) +159 " cannot be used against non-TLS symbol `" + toString(*sym) +160 "`");161 }162 if (auto *D = dyn_cast<DefinedData>(sym)) {163 if (!D->segment->outputSeg->isTLS()) {164 error(toString(file) + ": relocation " +165 relocTypeToString(reloc.Type) + " cannot be used against `" +166 toString(*sym) +167 "` in non-TLS section: " + D->segment->outputSeg->name);168 }169 }170 }171 break;172 }173 174 if (ctx.isPic || sym->isShared() ||175 (sym->isUndefined() &&176 ctx.arg.unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {177 switch (reloc.Type) {178 case R_WASM_TABLE_INDEX_SLEB:179 case R_WASM_TABLE_INDEX_SLEB64:180 case R_WASM_MEMORY_ADDR_SLEB:181 case R_WASM_MEMORY_ADDR_LEB:182 case R_WASM_MEMORY_ADDR_SLEB64:183 case R_WASM_MEMORY_ADDR_LEB64:184 // Certain relocation types can't be used when building PIC output,185 // since they would require absolute symbol addresses at link time.186 error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +187 " cannot be used against symbol `" + toString(*sym) +188 "`; recompile with -fPIC");189 break;190 case R_WASM_TABLE_INDEX_I32:191 case R_WASM_TABLE_INDEX_I64:192 case R_WASM_MEMORY_ADDR_I32:193 case R_WASM_MEMORY_ADDR_I64:194 // These relocation types are only present in the data section and195 // will be converted into code by `generateRelocationCode`. This196 // code requires the symbols to have GOT entries.197 if (requiresGOTAccess(sym))198 addGOTEntry(sym);199 break;200 }201 }202 203 if (!ctx.arg.relocatable && sym->isUndefined()) {204 switch (reloc.Type) {205 case R_WASM_TABLE_INDEX_REL_SLEB:206 case R_WASM_TABLE_INDEX_REL_SLEB64:207 case R_WASM_MEMORY_ADDR_REL_SLEB:208 case R_WASM_MEMORY_ADDR_REL_SLEB64:209 // These relocation types are for symbols that exists relative to210 // `__memory_base` or `__table_base` and as such only make sense for211 // defined symbols.212 error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +213 " is not supported against an undefined symbol `" +214 toString(*sym) + "`");215 break;216 }217 218 if (!sym->isWeak()) {219 // Report undefined symbols220 reportUndefined(file, sym);221 }222 }223 }224}225 226} // namespace lld::wasm227