llvm-project/lld/wasm/Relocations.cpp

120 lines
3.9 KiB
C++

//===- Relocations.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Relocations.h"
#include "InputChunks.h"
#include "SyntheticSections.h"
using namespace llvm;
using namespace llvm::wasm;
namespace lld {
namespace wasm {
static bool requiresGOTAccess(const Symbol *sym) {
return config->isPic && !sym->isHidden() && !sym->isLocal();
}
static bool allowUndefined(const Symbol* sym) {
// Undefined functions with explicit import name are allowed to be undefined
// at link time.
if (auto *F = dyn_cast<UndefinedFunction>(sym))
if (F->importName)
return true;
return (config->allowUndefined ||
config->allowUndefinedSymbols.count(sym->getName()) != 0);
}
static void reportUndefined(const Symbol* sym) {
assert(sym->isUndefined());
assert(!sym->isWeak());
if (!allowUndefined(sym))
error(toString(sym->getFile()) + ": undefined symbol: " + toString(*sym));
}
static void addGOTEntry(Symbol *sym) {
// In PIC mode a GOT entry is an imported global that the dynamic linker
// will assign.
// In non-PIC mode (i.e. when code compiled as fPIC is linked into a static
// binary) we create an internal wasm global with a fixed value that takes the
// place of th GOT entry and effectivly acts as an i32 const. This can
// potentially be optimized away at runtime or with a post-link tool.
// TODO(sbc): Linker relaxation might also be able to optimize this away.
if (config->isPic)
out.importSec->addGOTEntry(sym);
else
out.globalSec->addStaticGOTEntry(sym);
}
void scanRelocations(InputChunk *chunk) {
if (!chunk->live)
return;
ObjFile *file = chunk->file;
ArrayRef<WasmSignature> types = file->getWasmObj()->types();
for (const WasmRelocation &reloc : chunk->getRelocations()) {
if (reloc.Type == R_WASM_TYPE_INDEX_LEB) {
// Mark target type as live
file->typeMap[reloc.Index] =
out.typeSec->registerType(types[reloc.Index]);
file->typeIsUsed[reloc.Index] = true;
continue;
}
// Other relocation types all have a corresponding symbol
Symbol *sym = file->getSymbols()[reloc.Index];
switch (reloc.Type) {
case R_WASM_TABLE_INDEX_I32:
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB:
if (requiresGOTAccess(sym))
break;
out.elemSec->addEntry(cast<FunctionSymbol>(sym));
break;
case R_WASM_GLOBAL_INDEX_LEB:
case R_WASM_GLOBAL_INDEX_I32:
if (!isa<GlobalSymbol>(sym))
addGOTEntry(sym);
break;
}
if (config->isPic) {
switch (reloc.Type) {
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_LEB64:
// Certain relocation types can't be used when building PIC output,
// since they would require absolute symbol addresses at link time.
error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
" cannot be used against symbol " + toString(*sym) +
"; recompile with -fPIC");
break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_I64:
// These relocation types are only present in the data section and
// will be converted into code by `generateRelocationCode`. This code
// requires the symbols to have GOT entires.
if (requiresGOTAccess(sym))
addGOTEntry(sym);
break;
}
} else {
// Report undefined symbols
if (sym->isUndefined() && !config->relocatable && !sym->isWeak())
reportUndefined(sym);
}
}
}
} // namespace wasm
} // namespace lld