Skip to content

[ELF] Respect orders of symbol assignments and DEFINED #65866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 11, 2023
Merged
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
6 changes: 6 additions & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,12 @@ struct Ctx {
// True if we need to reserve two .got entries for local-dynamic TLS model.
std::atomic<bool> needsTlsLd{false};

// Each symbol assignment and DEFINED(sym) reference is assigned an increasing
// order. Each DEFINED(sym) evaluation checks whether the reference happens
// before a possible `sym = expr;`.
unsigned scriptSymOrderCounter = 1;
llvm::DenseMap<const Symbol *, unsigned> scriptSymOrder;

void reset();

llvm::raw_fd_ostream openAuxiliaryFile(llvm::StringRef, std::error_code &);
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ void Ctx::reset() {
backwardReferences.clear();
hasSympart.store(false, std::memory_order_relaxed);
needsTlsLd.store(false, std::memory_order_relaxed);
scriptSymOrderCounter = 1;
scriptSymOrder.clear();
}

llvm::raw_fd_ostream Ctx::openAuxiliaryFile(llvm::StringRef filename,
Expand Down
7 changes: 6 additions & 1 deletion lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,13 @@ static void declareSymbol(SymbolAssignment *cmd) {
Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0,
nullptr);

// We can't calculate final value right now.
// If the symbol is already defined, its order is 0 (with absence indicating
// 0); otherwise it's assigned the order of the SymbolAssignment.
Symbol *sym = symtab.insert(cmd->name);
if (!sym->isDefined())
ctx.scriptSymOrder.insert({sym, cmd->symOrder});

// We can't calculate final value right now.
sym->mergeProperties(newSym);
newSym.overwrite(*sym);

Expand Down
6 changes: 4 additions & 2 deletions lld/ELF/LinkerScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ struct SectionCommand {

// This represents ". = <expr>" or "<symbol> = <expr>".
struct SymbolAssignment : SectionCommand {
SymbolAssignment(StringRef name, Expr e, std::string loc)
SymbolAssignment(StringRef name, Expr e, unsigned symOrder, std::string loc)
: SectionCommand(AssignmentKind), name(name), expression(e),
location(loc) {}
symOrder(symOrder), location(loc) {}

static bool classof(const SectionCommand *c) {
return c->kind == AssignmentKind;
Expand All @@ -105,6 +105,8 @@ struct SymbolAssignment : SectionCommand {
bool provide = false;
bool hidden = false;

unsigned symOrder;

// Holds file name and line number for error reporting.
std::string location;

Expand Down
17 changes: 11 additions & 6 deletions lld/ELF/ScriptParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ void ScriptParser::readDefsym(StringRef name) {
Expr e = readExpr();
if (!atEOF())
setError("EOF expected, but got " + next());
SymbolAssignment *cmd = make<SymbolAssignment>(name, e, getCurrentLocation());
auto *cmd = make<SymbolAssignment>(name, e, 0, getCurrentLocation());
script->sectionCommands.push_back(cmd);
}

Expand Down Expand Up @@ -568,7 +568,7 @@ SmallVector<SectionCommand *, 0> ScriptParser::readOverlay() {
max = std::max(max, cast<OutputDesc>(cmd)->osec.size);
return addrExpr().getValue() + max;
};
v.push_back(make<SymbolAssignment>(".", moveDot, getCurrentLocation()));
v.push_back(make<SymbolAssignment>(".", moveDot, 0, getCurrentLocation()));
return v;
}

Expand Down Expand Up @@ -1047,7 +1047,7 @@ SymbolAssignment *ScriptParser::readProvideHidden(bool provide, bool hidden) {
SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
// Assert expression returns Dot, so this is equal to ".=."
if (tok == "ASSERT")
return make<SymbolAssignment>(".", readAssert(), getCurrentLocation());
return make<SymbolAssignment>(".", readAssert(), 0, getCurrentLocation());

size_t oldPos = pos;
SymbolAssignment *cmd = nullptr;
Expand Down Expand Up @@ -1117,7 +1117,8 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
}
};
}
return make<SymbolAssignment>(name, e, getCurrentLocation());
return make<SymbolAssignment>(name, e, ctx.scriptSymOrderCounter++,
getCurrentLocation());
}

// This is an operator-precedence parser to parse a linker
Expand Down Expand Up @@ -1465,9 +1466,13 @@ Expr ScriptParser::readPrimary() {
}
if (tok == "DEFINED") {
StringRef name = unquote(readParenLiteral());
// Return 1 if s is defined. If the definition is only found in a linker
// script, it must happen before this DEFINED.
auto order = ctx.scriptSymOrderCounter++;
return [=] {
Symbol *b = symtab.find(name);
return (b && b->isDefined()) ? 1 : 0;
Symbol *s = symtab.find(name);
return s && s->isDefined() && ctx.scriptSymOrder.lookup(s) < order ? 1
: 0;
};
}
if (tok == "LENGTH") {
Expand Down
19 changes: 0 additions & 19 deletions lld/test/ELF/linkerscript/define.test

This file was deleted.

45 changes: 45 additions & 0 deletions lld/test/ELF/linkerscript/defined.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/define.s -o %t.o
# RUN: ld.lld -o %t --defsym vv=1 --script %s %t.o
# RUN: llvm-readelf -S -s %t | FileCheck %s

# CHECK: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
# CHECK-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
# CHECK-NEXT: [ 1] .foo PROGBITS 0000000000011000 001000 000008 00 A 0 0 1
# CHECK-NEXT: [ 2] .bar PROGBITS 0000000000013000 003000 000008 00 A 0 0 1
# CHECK-NEXT: [ 3] .test PROGBITS 0000000000015000 005000 000008 00 A 0 0 1
# CHECK-NEXT: [ 4] .text PROGBITS 0000000000015008 005008 000000 00 AX 0 0 4

# CHECK: Value Size Type Bind Vis Ndx Name
# CHECK-DAG: 0000000000000001 0 NOTYPE GLOBAL DEFAULT ABS vv
# CHECK-DAG: 0000000000000009 0 NOTYPE GLOBAL DEFAULT ABS ww
# CHECK-DAG: 0000000000000001 0 NOTYPE GLOBAL DEFAULT ABS x1
# CHECK-DAG: 0000000000000002 0 NOTYPE GLOBAL DEFAULT ABS x2
# CHECK-DAG: 0000000000000001 0 NOTYPE GLOBAL DEFAULT ABS y1
# CHECK-DAG: 0000000000000002 0 NOTYPE GLOBAL DEFAULT ABS y2

EXTERN(extern_defined)
SECTIONS {
. = DEFINED(defined) ? 0x11000 : .;
.foo : { *(.foo*) }
. = DEFINED(notdefined) ? 0x12000 : 0x13000;
.bar : { *(.bar*) }
. = DEFINED(extern_defined) ? 0x14000 : 0x15000;

## Take the value from --defsym.
vv = DEFINED(vv) ? vv : 9;
## 9 as ww is undefined.
ww = DEFINED(ww) ? ww : 9;

## 1 as xx is not yet defined.
x1 = DEFINED(xx) ? 2 : 1;
.test : {
xx = .;
*(.test*)
}
x2 = DEFINED(xx) ? 2 : 1;

y1 = DEFINED(yy) ? 2 : 1;
yy = .;
y2 = DEFINED(yy) ? 2 : 1;
}