[ELF] Support operator ^ and ^=

GNU ld added ^ support in July 2023 and it looks like ^= is in plan as
well.

For now, we don't support `a^=0` (^= without a preceding space).
This commit is contained in:
Fangrui Song 2023-07-15 14:10:40 -07:00
parent 6043d4dfec
commit fae96104d4
6 changed files with 39 additions and 18 deletions

View File

@ -140,7 +140,7 @@ void ScriptLexer::tokenize(MemoryBufferRef mb) {
s = s.substr(3);
continue;
}
if (s.size() > 1 && ((s[1] == '=' && strchr("*/+-<>&|", s[0])) ||
if (s.size() > 1 && ((s[1] == '=' && strchr("*/+-<>&^|", s[0])) ||
(s[0] == s[1] && strchr("<>&|", s[0])))) {
vec.push_back(s.substr(0, 2));
s = s.substr(2);
@ -196,7 +196,7 @@ bool ScriptLexer::atEOF() { return errorCount() || tokens.size() == pos; }
// Split a given string as an expression.
// This function returns "3", "*" and "5" for "3*5" for example.
static std::vector<StringRef> tokenizeExpr(StringRef s) {
StringRef ops = "!~*/+-<>?:="; // List of operators
StringRef ops = "!~*/+-<>?^:="; // List of operators
// Quoted strings are literal strings, so we don't want to split it.
if (s.starts_with("\""))

View File

@ -177,6 +177,12 @@ static ExprValue bitAnd(ExprValue a, ExprValue b) {
(a.getValue() & b.getValue()) - a.getSecAddr(), a.loc};
}
static ExprValue bitXor(ExprValue a, ExprValue b) {
moveAbsRight(a, b);
return {a.sec, a.forceAbsolute,
(a.getValue() ^ b.getValue()) - a.getSecAddr(), a.loc};
}
static ExprValue bitOr(ExprValue a, ExprValue b) {
moveAbsRight(a, b);
return {a.sec, a.forceAbsolute,
@ -638,12 +644,13 @@ void ScriptParser::readTarget() {
static int precedence(StringRef op) {
return StringSwitch<int>(op)
.Cases("*", "/", "%", 10)
.Cases("+", "-", 9)
.Cases("<<", ">>", 8)
.Cases("<", "<=", ">", ">=", 7)
.Cases("==", "!=", 6)
.Case("&", 5)
.Cases("*", "/", "%", 11)
.Cases("+", "-", 10)
.Cases("<<", ">>", 9)
.Cases("<", "<=", ">", ">=", 8)
.Cases("==", "!=", 7)
.Case("&", 6)
.Case("^", 5)
.Case("|", 4)
.Case("&&", 3)
.Case("||", 2)
@ -1047,7 +1054,7 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
// Support = followed by an expression without whitespace.
SaveAndRestore saved(inExpr, true);
cmd = readSymbolAssignment(tok);
} else if ((op.size() == 2 && op[1] == '=' && strchr("*/+-&|", op[0])) ||
} else if ((op.size() == 2 && op[1] == '=' && strchr("*/+-&^|", op[0])) ||
op == "<<=" || op == ">>=") {
cmd = readSymbolAssignment(tok);
} else if (tok == "PROVIDE") {
@ -1074,7 +1081,7 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
name = unquote(name);
StringRef op = next();
assert(op == "=" || op == "*=" || op == "/=" || op == "+=" || op == "-=" ||
op == "&=" || op == "|=" || op == "<<=" || op == ">>=");
op == "&=" || op == "^=" || op == "|=" || op == "<<=" || op == ">>=");
// Note: GNU ld does not support %= or ^=.
Expr e = readExpr();
if (op != "=") {
@ -1099,6 +1106,8 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
return lhs.getValue() >> e().getValue() % 64;
case '&':
return lhs.getValue() & e().getValue();
case '^':
return lhs.getValue() ^ e().getValue();
case '|':
return lhs.getValue() | e().getValue();
default:
@ -1168,6 +1177,8 @@ Expr ScriptParser::combine(StringRef op, Expr l, Expr r) {
return [=] { return l().getValue() && r().getValue(); };
if (op == "&")
return [=] { return bitAnd(l(), r()); };
if (op == "^")
return [=] { return bitXor(l(), r()); };
if (op == "|")
return [=] { return bitOr(l(), r()); };
llvm_unreachable("invalid operator");

View File

@ -8,6 +8,6 @@ SECTIONS {
boom ^temp : { *(.temp) }
}
# CHECK: 8: malformed number: ^temp
# CHECK: 8: malformed number: ^
# CHECK-NEXT: >>> boom ^temp : { *(.temp) }
# CHECK-NEXT: >>> ^

View File

@ -9,6 +9,6 @@ SECTIONS {
boom ^temp : { *(.temp) }
}
# CHECK: 9: malformed number: ^temp
# CHECK: 9: malformed number: ^{{$}}
# CHECK-NEXT: >>> boom ^temp : { *(.temp) }
# CHECK-NEXT: >>> ^

View File

@ -9,6 +9,6 @@ SECTIONS {
boom ^temp : { *(.temp) }
}
# CHECK: 9: malformed number: ^temp
# CHECK: 9: malformed number: ^
# CHECK-NEXT: >>> boom ^temp : { *(.temp) }
# CHECK-NEXT: >>> ^

View File

@ -21,6 +21,7 @@ SECTIONS {
neq = 1 != 1 <= 1 ? 1 : 2;
and = 3 & 4 > 0;
or = 0xbb & 0xee | 1;
xor = 3&3^5|1;
logicaland = (0 && 0) + (0&&1)*2 + (1&& 0)*4 + (1 &&1) *8;
logicaland2 = 1 & 0 && 1 | 1;
logicalor = (0 || 0) + (0||1)*2 + (1|| 0)*4 + (1 ||1) *8;
@ -44,8 +45,13 @@ SECTIONS {
rshiftassign >>= 130; # arbitrarily reduced to 2
andassign = 6;
andassign &= 4;
andassign&=4;
xorassign = 6;
xorassign ^= 3;
xorassign ^=0;
orassign = 4;
orassign |= 1;
orassign|=1;
braces = 1 + (2 + 3) * 4;
precedence1 = 1|0xff&1/1<<1+1*2;
precedence2 = (1 | (0xff & (1 << (1 + (1 * 2)))));
@ -85,6 +91,7 @@ SECTIONS {
# CHECK-NEXT: 0000000000000002 A neq
# CHECK-NEXT: 0000000000000001 A and
# CHECK-NEXT: 00000000000000ab A or
# CHECK-NEXT: 0000000000000007 A xor
# CHECK-NEXT: 0000000000000008 A logicaland
# CHECK-NEXT: 0000000000000000 A logicaland2
# CHECK-NEXT: 000000000000000e A logicalor
@ -98,6 +105,7 @@ SECTIONS {
# CHECK-NEXT: 0000000000000004 A lshiftassign
# CHECK-NEXT: 0000000000000003 A rshiftassign
# CHECK-NEXT: 0000000000000004 A andassign
# CHECK-NEXT: 0000000000000005 A xorassign
# CHECK-NEXT: 0000000000000005 A orassign
# CHECK-NEXT: 0000000000000015 A braces
# CHECK-NEXT: 0000000000000009 A precedence1
@ -165,10 +173,12 @@ SECTIONS {
# RUN: echo 'a = 1; a /= 0;' > %t.script
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | FileCheck --check-prefix=DIVZERO %s
## GNU ld does not support %= or ^=.
## GNU ld does not support %=.
# RUN: echo 'a = 1; a %= 0;' > %t.script
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | FileCheck --check-prefix=UNKNOWN %s
# RUN: echo 'a = 1; a ^= 0;' > %t.script
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | FileCheck --check-prefix=UNKNOWN %s
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | FileCheck --check-prefix=UNKNOWN1 %s
## For now, we don't support ^= without a preceding space.
# RUN: echo 'a = 1; a^=0;' > %t.script
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | FileCheck --check-prefix=UNKNOWN2 %s
# UNKNOWN: error: {{.*}}:1: unknown directive: a
# UNKNOWN1: error: {{.*}}:1: unknown directive: a{{$}}
# UNKNOWN2: error: {{.*}}:1: unknown directive: a^=0{{$}}