Skip to content

Commit 0a0effd

Browse files
committed
[ELF] Support -= *= /= <<= >>= &= |= in symbol assignments
1 parent a7938c7 commit 0a0effd

File tree

3 files changed

+68
-9
lines changed

3 files changed

+68
-9
lines changed

lld/ELF/ScriptLexer.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,14 @@ void ScriptLexer::tokenize(MemoryBufferRef mb) {
134134
continue;
135135
}
136136

137-
// ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>".
138-
// "|", "||", "&" and "&&" are different operators.
139-
if (s.startswith("<<") || s.startswith("<=") || s.startswith(">>") ||
140-
s.startswith(">=") || s.startswith("||") || s.startswith("&&")) {
137+
// Some operators form separate tokens.
138+
if (s.startswith("<<=") || s.startswith(">>=")) {
139+
vec.push_back(s.substr(0, 3));
140+
s = s.substr(3);
141+
continue;
142+
}
143+
if (s.size() > 1 && ((s[1] == '=' && strchr("*/+-<>&|", s[0])) ||
144+
(s[0] == s[1] && strchr("<>&|", s[0])))) {
141145
vec.push_back(s.substr(0, 2));
142146
s = s.substr(2);
143147
continue;

lld/ELF/ScriptParser.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,11 +1038,14 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
10381038

10391039
size_t oldPos = pos;
10401040
SymbolAssignment *cmd = nullptr;
1041-
if (peek().startswith("=")) {
1041+
const StringRef op = peek();
1042+
if (op.startswith("=")) {
10421043
// Support = followed by an expression without whitespace.
10431044
SaveAndRestore<bool> saved(inExpr, true);
10441045
cmd = readSymbolAssignment(tok);
1045-
} else if (peek() == "+=") {
1046+
} else if ((op.size() == 2 && op[1] == '=' &&
1047+
is_contained("*/+-&|", op[0])) ||
1048+
op == "<<=" || op == ">>=") {
10461049
cmd = readSymbolAssignment(tok);
10471050
} else if (tok == "PROVIDE") {
10481051
SaveAndRestore<bool> saved(inExpr, true);
@@ -1067,11 +1070,38 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
10671070
SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
10681071
name = unquote(name);
10691072
StringRef op = next();
1070-
assert(op == "=" || op == "+=");
1073+
assert(op == "=" || op == "*=" || op == "/=" || op == "+=" || op == "-=" ||
1074+
op == "&=" || op == "|=" || op == "<<=" || op == ">>=");
1075+
// Note: GNU ld does not support %= or ^=.
10711076
Expr e = readExpr();
1072-
if (op == "+=") {
1077+
if (op != "=") {
10731078
std::string loc = getCurrentLocation();
1074-
e = [=] { return add(script->getSymbolValue(name, loc), e()); };
1079+
e = [=, c = op[0]]() -> ExprValue {
1080+
ExprValue lhs = script->getSymbolValue(name, loc);
1081+
switch (c) {
1082+
case '*':
1083+
return lhs.getValue() * e().getValue();
1084+
case '/':
1085+
if (uint64_t rv = e().getValue())
1086+
return lhs.getValue() / rv;
1087+
error(loc + ": division by zero");
1088+
return 0;
1089+
case '+':
1090+
return add(lhs, e());
1091+
case '-':
1092+
return sub(lhs, e());
1093+
case '<':
1094+
return lhs.getValue() << e().getValue();
1095+
case '>':
1096+
return lhs.getValue() >> e().getValue();
1097+
case '&':
1098+
return lhs.getValue() & e().getValue();
1099+
case '|':
1100+
return lhs.getValue() | e().getValue();
1101+
default:
1102+
llvm_unreachable("");
1103+
}
1104+
};
10751105
}
10761106
return make<SymbolAssignment>(name, e, getCurrentLocation());
10771107
}

lld/test/ELF/linkerscript/operators.test

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,22 @@ SECTIONS {
2727
ternary1 = 0 ? 1 : 2 & 6;
2828
ternary2 = 1 ? 2?3:4 : 5?6 :7;
2929

30+
mulassign =2;
31+
mulassign *= 2;
32+
divassign = 8;
33+
divassign /= 2;
3034
plusassign =1;
3135
plusassign += 2;
36+
minusassign = 3;
37+
minusassign -= 1;
38+
lshiftassign = 1;
39+
lshiftassign <<= 2;
40+
rshiftassign = 5;
41+
rshiftassign >>= 1;
42+
andassign = 6;
43+
andassign &= 4;
44+
orassign = 4;
45+
orassign |= 1;
3246
braces = 1 + (2 + 3) * 4;
3347
precedence1 = 1|0xff&1/1<<1+1*2;
3448
precedence2 = (1 | (0xff & (1 << (1 + (1 * 2)))));
@@ -73,7 +87,14 @@ SECTIONS {
7387
# CHECK-NEXT: 0000000000000001 A logicalor2
7488
# CHECK-NEXT: 0000000000000002 A ternary1
7589
# CHECK-NEXT: 0000000000000003 A ternary2
90+
# CHECK-NEXT: 0000000000000004 A mulassign
91+
# CHECK-NEXT: 0000000000000004 A divassign
7692
# CHECK-NEXT: 0000000000000003 A plusassign
93+
# CHECK-NEXT: 0000000000000002 A minusassign
94+
# CHECK-NEXT: 0000000000000004 A lshiftassign
95+
# CHECK-NEXT: 0000000000000002 A rshiftassign
96+
# CHECK-NEXT: 0000000000000004 A andassign
97+
# CHECK-NEXT: 0000000000000005 A orassign
7798
# CHECK-NEXT: 0000000000000015 A braces
7899
# CHECK-NEXT: 0000000000000009 A precedence1
79100
# CHECK-NEXT: 0000000000000009 A precedence2
@@ -135,3 +156,7 @@ SECTIONS {
135156
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | \
136157
# RUN: FileCheck --check-prefix=TERNERR %s
137158
# TERNERR: : expected, but got ;
159+
160+
## Div by zero error.
161+
# RUN: echo 'a = 1; a /= 0;' > %t.script
162+
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | FileCheck --check-prefix=DIVZERO %s

0 commit comments

Comments
 (0)