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-exception
6//
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 {
20namespace wasm {
21
22static bool requiresGOTAccess(const Symbol *sym) {
23  if (!config->isPic &&
24      config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic)
25    return false;
26  if (sym->isHidden() || sym->isLocal())
27    return false;
28  // With `-Bsymbolic` (or when building an executable) as don't need to use
29  // the GOT for symbols that are defined within the current module.
30  if (sym->isDefined() && (!config->shared || config->bsymbolic))
31    return false;
32  return true;
33}
34
35static bool allowUndefined(const Symbol* sym) {
36  // Symbols that are explicitly imported are always allowed to be undefined at
37  // link time.
38  if (sym->isImported())
39    return true;
40  if (isa<UndefinedFunction>(sym) && config->importUndefined)
41    return true;
42
43  return config->allowUndefinedSymbols.count(sym->getName()) != 0;
44}
45
46static void reportUndefined(Symbol *sym) {
47  if (!allowUndefined(sym)) {
48    switch (config->unresolvedSymbols) {
49    case UnresolvedPolicy::ReportError:
50      error(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
51      break;
52    case UnresolvedPolicy::Warn:
53      warn(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
54      break;
55    case UnresolvedPolicy::Ignore:
56      LLVM_DEBUG(dbgs() << "ignoring undefined symbol: " + toString(*sym) +
57                               "\n");
58      if (!config->importUndefined) {
59        if (auto *f = dyn_cast<UndefinedFunction>(sym)) {
60          if (!f->stubFunction) {
61            f->stubFunction = symtab->createUndefinedStub(*f->getSignature());
62            f->stubFunction->markLive();
63            // Mark the function itself as a stub which prevents it from being
64            // assigned a table entry.
65            f->isStub = true;
66          }
67        }
68      }
69      break;
70    case UnresolvedPolicy::ImportDynamic:
71      break;
72    }
73  }
74}
75
76static void addGOTEntry(Symbol *sym) {
77  if (requiresGOTAccess(sym))
78    out.importSec->addGOTEntry(sym);
79  else
80    out.globalSec->addInternalGOTEntry(sym);
81}
82
83void scanRelocations(InputChunk *chunk) {
84  if (!chunk->live)
85    return;
86  ObjFile *file = chunk->file;
87  ArrayRef<WasmSignature> types = file->getWasmObj()->types();
88  for (const WasmRelocation &reloc : chunk->getRelocations()) {
89    if (reloc.Type == R_WASM_TYPE_INDEX_LEB) {
90      // Mark target type as live
91      file->typeMap[reloc.Index] =
92          out.typeSec->registerType(types[reloc.Index]);
93      file->typeIsUsed[reloc.Index] = true;
94      continue;
95    }
96
97    // Other relocation types all have a corresponding symbol
98    Symbol *sym = file->getSymbols()[reloc.Index];
99
100    switch (reloc.Type) {
101    case R_WASM_TABLE_INDEX_I32:
102    case R_WASM_TABLE_INDEX_I64:
103    case R_WASM_TABLE_INDEX_SLEB:
104    case R_WASM_TABLE_INDEX_SLEB64:
105    case R_WASM_TABLE_INDEX_REL_SLEB:
106    case R_WASM_TABLE_INDEX_REL_SLEB64:
107      if (requiresGOTAccess(sym))
108        break;
109      out.elemSec->addEntry(cast<FunctionSymbol>(sym));
110      break;
111    case R_WASM_GLOBAL_INDEX_LEB:
112    case R_WASM_GLOBAL_INDEX_I32:
113      if (!isa<GlobalSymbol>(sym))
114        addGOTEntry(sym);
115      break;
116    case R_WASM_MEMORY_ADDR_TLS_SLEB:
117    case R_WASM_MEMORY_ADDR_TLS_SLEB64:
118      if (!sym->isDefined()) {
119        error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
120              " cannot be used against an undefined symbol `" + toString(*sym) +
121              "`");
122      }
123      // In single-threaded builds TLS is lowered away and TLS data can be
124      // merged with normal data and allowing TLS relocation in non-TLS
125      // segments.
126      if (config->sharedMemory) {
127        if (!sym->isTLS()) {
128          error(toString(file) + ": relocation " +
129                relocTypeToString(reloc.Type) +
130                " cannot be used against non-TLS symbol `" + toString(*sym) +
131                "`");
132        }
133        if (auto *D = dyn_cast<DefinedData>(sym)) {
134          if (!D->segment->outputSeg->isTLS()) {
135            error(toString(file) + ": relocation " +
136                  relocTypeToString(reloc.Type) + " cannot be used against `" +
137                  toString(*sym) +
138                  "` in non-TLS section: " + D->segment->outputSeg->name);
139          }
140        }
141      }
142      break;
143    }
144
145    if (config->isPic ||
146        (sym->isUndefined() &&
147         config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {
148      switch (reloc.Type) {
149      case R_WASM_TABLE_INDEX_SLEB:
150      case R_WASM_TABLE_INDEX_SLEB64:
151      case R_WASM_MEMORY_ADDR_SLEB:
152      case R_WASM_MEMORY_ADDR_LEB:
153      case R_WASM_MEMORY_ADDR_SLEB64:
154      case R_WASM_MEMORY_ADDR_LEB64:
155        // Certain relocation types can't be used when building PIC output,
156        // since they would require absolute symbol addresses at link time.
157        error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
158              " cannot be used against symbol `" + toString(*sym) +
159              "`; recompile with -fPIC");
160        break;
161      case R_WASM_TABLE_INDEX_I32:
162      case R_WASM_TABLE_INDEX_I64:
163      case R_WASM_MEMORY_ADDR_I32:
164      case R_WASM_MEMORY_ADDR_I64:
165        // These relocation types are only present in the data section and
166        // will be converted into code by `generateRelocationCode`.  This code
167        // requires the symbols to have GOT entries.
168        if (requiresGOTAccess(sym))
169          addGOTEntry(sym);
170        break;
171      }
172    } else if (sym->isUndefined() && !config->relocatable && !sym->isWeak()) {
173      // Report undefined symbols
174      reportUndefined(sym);
175    }
176  }
177}
178
179} // namespace wasm
180} // namespace lld
181