xref: /openbsd-src/gnu/llvm/lld/wasm/Relocations.cpp (revision dfe94b169149f14cc1aee2cf6dad58a8d9a1860c)
1ece8a530Spatrick //===- Relocations.cpp ----------------------------------------------------===//
2ece8a530Spatrick //
3ece8a530Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ece8a530Spatrick // See https://llvm.org/LICENSE.txt for license information.
5ece8a530Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ece8a530Spatrick //
7ece8a530Spatrick //===----------------------------------------------------------------------===//
8ece8a530Spatrick 
9ece8a530Spatrick #include "Relocations.h"
10ece8a530Spatrick 
11ece8a530Spatrick #include "InputChunks.h"
121cf9926bSpatrick #include "OutputSegment.h"
131cf9926bSpatrick #include "SymbolTable.h"
14ece8a530Spatrick #include "SyntheticSections.h"
15ece8a530Spatrick 
16ece8a530Spatrick using namespace llvm;
17ece8a530Spatrick using namespace llvm::wasm;
18ece8a530Spatrick 
19ece8a530Spatrick namespace lld {
20ece8a530Spatrick namespace wasm {
211cf9926bSpatrick 
requiresGOTAccess(const Symbol * sym)22ece8a530Spatrick static bool requiresGOTAccess(const Symbol *sym) {
23*dfe94b16Srobert   if (!config->isPic &&
24*dfe94b16Srobert       config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic)
251cf9926bSpatrick     return false;
261cf9926bSpatrick   if (sym->isHidden() || sym->isLocal())
271cf9926bSpatrick     return false;
281cf9926bSpatrick   // With `-Bsymbolic` (or when building an executable) as don't need to use
291cf9926bSpatrick   // the GOT for symbols that are defined within the current module.
301cf9926bSpatrick   if (sym->isDefined() && (!config->shared || config->bsymbolic))
311cf9926bSpatrick     return false;
321cf9926bSpatrick   return true;
33ece8a530Spatrick }
34ece8a530Spatrick 
allowUndefined(const Symbol * sym)35ece8a530Spatrick static bool allowUndefined(const Symbol* sym) {
36*dfe94b16Srobert   // Symbols that are explicitly imported are always allowed to be undefined at
37*dfe94b16Srobert   // link time.
38*dfe94b16Srobert   if (sym->isImported())
39bb684c34Spatrick     return true;
40*dfe94b16Srobert   if (isa<UndefinedFunction>(sym) && config->importUndefined)
411cf9926bSpatrick     return true;
42*dfe94b16Srobert 
431cf9926bSpatrick   return config->allowUndefinedSymbols.count(sym->getName()) != 0;
44ece8a530Spatrick }
45ece8a530Spatrick 
reportUndefined(Symbol * sym)461cf9926bSpatrick static void reportUndefined(Symbol *sym) {
471cf9926bSpatrick   if (!allowUndefined(sym)) {
481cf9926bSpatrick     switch (config->unresolvedSymbols) {
491cf9926bSpatrick     case UnresolvedPolicy::ReportError:
50ece8a530Spatrick       error(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
511cf9926bSpatrick       break;
521cf9926bSpatrick     case UnresolvedPolicy::Warn:
531cf9926bSpatrick       warn(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
541cf9926bSpatrick       break;
551cf9926bSpatrick     case UnresolvedPolicy::Ignore:
561cf9926bSpatrick       LLVM_DEBUG(dbgs() << "ignoring undefined symbol: " + toString(*sym) +
571cf9926bSpatrick                                "\n");
581cf9926bSpatrick       if (!config->importUndefined) {
591cf9926bSpatrick         if (auto *f = dyn_cast<UndefinedFunction>(sym)) {
601cf9926bSpatrick           if (!f->stubFunction) {
611cf9926bSpatrick             f->stubFunction = symtab->createUndefinedStub(*f->getSignature());
621cf9926bSpatrick             f->stubFunction->markLive();
631cf9926bSpatrick             // Mark the function itself as a stub which prevents it from being
641cf9926bSpatrick             // assigned a table entry.
651cf9926bSpatrick             f->isStub = true;
661cf9926bSpatrick           }
671cf9926bSpatrick         }
681cf9926bSpatrick       }
691cf9926bSpatrick       break;
70*dfe94b16Srobert     case UnresolvedPolicy::ImportDynamic:
71*dfe94b16Srobert       break;
721cf9926bSpatrick     }
731cf9926bSpatrick   }
74ece8a530Spatrick }
75ece8a530Spatrick 
addGOTEntry(Symbol * sym)76ece8a530Spatrick static void addGOTEntry(Symbol *sym) {
771cf9926bSpatrick   if (requiresGOTAccess(sym))
78ece8a530Spatrick     out.importSec->addGOTEntry(sym);
79ece8a530Spatrick   else
801cf9926bSpatrick     out.globalSec->addInternalGOTEntry(sym);
81ece8a530Spatrick }
82ece8a530Spatrick 
scanRelocations(InputChunk * chunk)83ece8a530Spatrick void scanRelocations(InputChunk *chunk) {
84ece8a530Spatrick   if (!chunk->live)
85ece8a530Spatrick     return;
86ece8a530Spatrick   ObjFile *file = chunk->file;
87ece8a530Spatrick   ArrayRef<WasmSignature> types = file->getWasmObj()->types();
88ece8a530Spatrick   for (const WasmRelocation &reloc : chunk->getRelocations()) {
89ece8a530Spatrick     if (reloc.Type == R_WASM_TYPE_INDEX_LEB) {
90ece8a530Spatrick       // Mark target type as live
91ece8a530Spatrick       file->typeMap[reloc.Index] =
92ece8a530Spatrick           out.typeSec->registerType(types[reloc.Index]);
93ece8a530Spatrick       file->typeIsUsed[reloc.Index] = true;
94ece8a530Spatrick       continue;
95ece8a530Spatrick     }
96ece8a530Spatrick 
97ece8a530Spatrick     // Other relocation types all have a corresponding symbol
98ece8a530Spatrick     Symbol *sym = file->getSymbols()[reloc.Index];
99ece8a530Spatrick 
100ece8a530Spatrick     switch (reloc.Type) {
101ece8a530Spatrick     case R_WASM_TABLE_INDEX_I32:
1021cf9926bSpatrick     case R_WASM_TABLE_INDEX_I64:
103ece8a530Spatrick     case R_WASM_TABLE_INDEX_SLEB:
1041cf9926bSpatrick     case R_WASM_TABLE_INDEX_SLEB64:
105ece8a530Spatrick     case R_WASM_TABLE_INDEX_REL_SLEB:
1061cf9926bSpatrick     case R_WASM_TABLE_INDEX_REL_SLEB64:
107ece8a530Spatrick       if (requiresGOTAccess(sym))
108ece8a530Spatrick         break;
109ece8a530Spatrick       out.elemSec->addEntry(cast<FunctionSymbol>(sym));
110ece8a530Spatrick       break;
111ece8a530Spatrick     case R_WASM_GLOBAL_INDEX_LEB:
112bb684c34Spatrick     case R_WASM_GLOBAL_INDEX_I32:
113ece8a530Spatrick       if (!isa<GlobalSymbol>(sym))
114ece8a530Spatrick         addGOTEntry(sym);
115ece8a530Spatrick       break;
1161cf9926bSpatrick     case R_WASM_MEMORY_ADDR_TLS_SLEB:
1171cf9926bSpatrick     case R_WASM_MEMORY_ADDR_TLS_SLEB64:
118*dfe94b16Srobert       if (!sym->isDefined()) {
119*dfe94b16Srobert         error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
120*dfe94b16Srobert               " cannot be used against an undefined symbol `" + toString(*sym) +
121*dfe94b16Srobert               "`");
122*dfe94b16Srobert       }
1231cf9926bSpatrick       // In single-threaded builds TLS is lowered away and TLS data can be
1241cf9926bSpatrick       // merged with normal data and allowing TLS relocation in non-TLS
1251cf9926bSpatrick       // segments.
1261cf9926bSpatrick       if (config->sharedMemory) {
127*dfe94b16Srobert         if (!sym->isTLS()) {
128*dfe94b16Srobert           error(toString(file) + ": relocation " +
129*dfe94b16Srobert                 relocTypeToString(reloc.Type) +
130*dfe94b16Srobert                 " cannot be used against non-TLS symbol `" + toString(*sym) +
131*dfe94b16Srobert                 "`");
132*dfe94b16Srobert         }
1331cf9926bSpatrick         if (auto *D = dyn_cast<DefinedData>(sym)) {
1341cf9926bSpatrick           if (!D->segment->outputSeg->isTLS()) {
1351cf9926bSpatrick             error(toString(file) + ": relocation " +
1361cf9926bSpatrick                   relocTypeToString(reloc.Type) + " cannot be used against `" +
1371cf9926bSpatrick                   toString(*sym) +
1381cf9926bSpatrick                   "` in non-TLS section: " + D->segment->outputSeg->name);
1391cf9926bSpatrick           }
1401cf9926bSpatrick         }
1411cf9926bSpatrick       }
1421cf9926bSpatrick       break;
143ece8a530Spatrick     }
144ece8a530Spatrick 
145*dfe94b16Srobert     if (config->isPic ||
146*dfe94b16Srobert         (sym->isUndefined() &&
147*dfe94b16Srobert          config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {
148ece8a530Spatrick       switch (reloc.Type) {
149ece8a530Spatrick       case R_WASM_TABLE_INDEX_SLEB:
1501cf9926bSpatrick       case R_WASM_TABLE_INDEX_SLEB64:
151ece8a530Spatrick       case R_WASM_MEMORY_ADDR_SLEB:
152ece8a530Spatrick       case R_WASM_MEMORY_ADDR_LEB:
153bb684c34Spatrick       case R_WASM_MEMORY_ADDR_SLEB64:
154bb684c34Spatrick       case R_WASM_MEMORY_ADDR_LEB64:
155ece8a530Spatrick         // Certain relocation types can't be used when building PIC output,
156ece8a530Spatrick         // since they would require absolute symbol addresses at link time.
157ece8a530Spatrick         error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
158*dfe94b16Srobert               " cannot be used against symbol `" + toString(*sym) +
159*dfe94b16Srobert               "`; recompile with -fPIC");
1601cf9926bSpatrick         break;
161ece8a530Spatrick       case R_WASM_TABLE_INDEX_I32:
1621cf9926bSpatrick       case R_WASM_TABLE_INDEX_I64:
163ece8a530Spatrick       case R_WASM_MEMORY_ADDR_I32:
164bb684c34Spatrick       case R_WASM_MEMORY_ADDR_I64:
165ece8a530Spatrick         // These relocation types are only present in the data section and
166ece8a530Spatrick         // will be converted into code by `generateRelocationCode`.  This code
167*dfe94b16Srobert         // requires the symbols to have GOT entries.
168ece8a530Spatrick         if (requiresGOTAccess(sym))
169ece8a530Spatrick           addGOTEntry(sym);
170ece8a530Spatrick         break;
171ece8a530Spatrick       }
1721cf9926bSpatrick     } else if (sym->isUndefined() && !config->relocatable && !sym->isWeak()) {
173ece8a530Spatrick       // Report undefined symbols
174ece8a530Spatrick       reportUndefined(sym);
175ece8a530Spatrick     }
176ece8a530Spatrick   }
177ece8a530Spatrick }
178ece8a530Spatrick 
179ece8a530Spatrick } // namespace wasm
180ece8a530Spatrick } // namespace lld
181