Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1191,7 +1191,7 @@ void ObjFile<ELFT>::initSectionsAndLocalSyms(bool ignoreComdats) {

if (!firstGlobal)
return;
SymbolUnion *locals = makeThreadLocalN<SymbolUnion>(firstGlobal);
SymbolUnion *locals = allocSymbolUnions(firstGlobal); // arena
memset(locals, 0, sizeof(SymbolUnion) * firstGlobal);

ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
Expand Down
62 changes: 58 additions & 4 deletions lld/ELF/Relocations.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,55 @@ enum RelExpr {
R_LOONGARCH_TLSDESC_PAGE_PC,
};

// Architecture-neutral representation of relocation.
// Encode a Symbol* as a 32-bit index into the contiguous SymbolUnion arena
// (see Symbols.{h,cpp}). Every Symbol lives in that arena, so the pointer is
// symArena + index*64; index 0 is the nullptr sentinel. SymRef converts
// implicitly to/from Symbol*, so relocation `sym` use sites are unchanged.
class Symbol;
extern char *symArena;
struct SymRef {
uint32_t idx = 0;
SymRef() = default;
SymRef(Symbol *s) { *this = s; }
SymRef &operator=(Symbol *s) {
idx = s ? uint32_t((reinterpret_cast<char *>(s) - symArena) / 64) : 0;
return *this;
}
operator Symbol *() const {
return idx ? reinterpret_cast<Symbol *>(symArena + size_t(idx) * 64)
: nullptr;
}
Symbol *operator->() const {
return reinterpret_cast<Symbol *>(symArena + size_t(idx) * 64);
}
Symbol &operator*() const {
return *reinterpret_cast<Symbol *>(symArena + size_t(idx) * 64);
}
};

// Architecture-neutral representation of relocation, packed to 24 bytes (from
// 32) with no new input limits. `expr` needs 7 bits and `type` fits in 24 bits
// for every supported target. `sym` is a 32-bit arena index (SymRef); ELF's
// r_info already stores symbol references as uint32, so this imposes no limit
// beyond the ELF format. `offset` and `addend` keep their full 64-bit width, so
// huge-section inputs (e.g. monolithic LTO) are unaffected. `sym` sits next to
// the expr/type word so the two 8-byte fields stay naturally aligned with no
// padding (4+4 + 8 + 8 = 24). The constructor keeps the historical
// (expr, type, offset, addend, sym) parameter order so positional initializers
// are unchanged.
struct Relocation {
RelExpr expr;
RelType type;
RelExpr expr : 8;
RelType type : 24;
SymRef sym;
uint64_t offset;
int64_t addend;
Symbol *sym;

Relocation() = default;
Relocation(RelExpr expr, RelType type, uint64_t offset, int64_t addend,
Symbol *sym)
: expr(expr), type(type), sym(sym), offset(offset), addend(addend) {}
};
static_assert(sizeof(Relocation) == 24, "Relocation should pack to 24 bytes");

// Manipulate jump instructions with these modifiers. These are used to relax
// jump instruction opcodes at basic block boundaries and are particularly
Expand Down Expand Up @@ -333,4 +374,17 @@ sortRels(Relocs<llvm::object::Elf_Crel_Impl<is64>> rels,
bool needsGot(RelExpr expr);
} // namespace lld::elf

// Let LLVM's cast<>/dyn_cast<>/isa<> see SymRef as a Symbol*, so existing
// `dyn_cast<Defined>(rel.sym)` sites compile unchanged.
namespace llvm {
template <> struct simplify_type<lld::elf::SymRef> {
using SimpleType = lld::elf::Symbol *;
static SimpleType getSimplifiedValue(lld::elf::SymRef &s) { return s; }
};
template <> struct simplify_type<const lld::elf::SymRef> {
using SimpleType = lld::elf::Symbol *;
static SimpleType getSimplifiedValue(const lld::elf::SymRef &s) { return s; }
};
} // namespace llvm

#endif
2 changes: 1 addition & 1 deletion lld/ELF/SymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Symbol *SymbolTable::insert(StringRef name) {
return sym;
}

Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
Symbol *sym = reinterpret_cast<Symbol *>(allocSymbolUnions(1)); // arena
symVector.push_back(sym);

// *sym was not initialized by a constructor. Initialize all Symbol fields.
Expand Down
31 changes: 31 additions & 0 deletions lld/ELF/Symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
#include "lld/Common/ErrorHandler.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/Compiler.h"
#include <atomic>
#include <cstring>
#include <mutex>
#include <sys/mman.h>

using namespace llvm;
using namespace llvm::object;
Expand All @@ -26,6 +29,34 @@ using namespace lld;
using namespace lld::elf;

static_assert(sizeof(SymbolUnion) <= 64, "SymbolUnion too large");
// SymRef (Relocations.h) encodes Symbol* as index*64, so the arena stride must
// be exactly 64 for the shift-based conversion to be valid.
static_assert(sizeof(SymbolUnion) == 64, "SymRef assumes 64-byte stride");
static_assert(alignof(SymbolUnion) <= 64, "SymbolUnion overaligned");

// Contiguous SymbolUnion arena. Reserve a large virtual region (only touched
// pages commit) and hand out 64-byte slots with an atomic bump. Slot 0 is
// reserved as the nullptr sentinel, so the first real symbol has index 1.
char *elf::symArena = nullptr;
namespace {
constexpr size_t symArenaCapacity = size_t(1) << 29; // 512M symbols (32 GiB VA)
std::atomic<size_t> symArenaNext{1}; // index 0 == nullptr
std::once_flag symArenaOnce;
} // namespace
SymbolUnion *elf::allocSymbolUnions(size_t n) {
std::call_once(symArenaOnce, [] {
void *p = mmap(nullptr, symArenaCapacity * sizeof(SymbolUnion),
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
-1, 0);
if (p == MAP_FAILED)
fatal("failed to reserve symbol arena");
symArena = reinterpret_cast<char *>(p);
});
size_t idx = symArenaNext.fetch_add(n, std::memory_order_relaxed);
if (idx + n > symArenaCapacity)
fatal("symbol arena exhausted");
return reinterpret_cast<SymbolUnion *>(symArena) + idx;
}

template <typename T> struct AssertSymbol {
static_assert(std::is_trivially_destructible<T>(),
Expand Down
9 changes: 8 additions & 1 deletion lld/ELF/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,15 @@ union SymbolUnion {
alignas(LazySymbol) char e[sizeof(LazySymbol)];
};

// All SymbolUnion objects are allocated from a single contiguous arena so that
// any Symbol* can be encoded as a 32-bit index (see SymRef in Relocations.h).
// `symArena` is the base; slot 0 is the nullptr sentinel, so real symbols have
// index >= 1. See Symbols.cpp for the allocator.
extern char *symArena;
SymbolUnion *allocSymbolUnions(size_t n);

template <typename... T> Defined *makeDefined(T &&...args) {
auto *sym = getSpecificAllocSingleton<SymbolUnion>().Allocate();
auto *sym = allocSymbolUnions(1);
memset(sym, 0, sizeof(Symbol));
auto &s = *new (reinterpret_cast<Defined *>(sym)) Defined(std::forward<T>(args)...);
return &s;
Expand Down