Skip to content

Commit 2c1a93a

Browse files
committed
[RISCV] Replace @plt/@gotpcrel in data directives with %plt %gotpcrel
clang -fexperimental-relative-c++-abi-vtables might generate `@plt` and `@gotpcrel` specifiers in data directives. The syntax is not used in humand-written assembly code, and is not supported by GNU assembler. Note: the `@plt` in `.word foo@plt` is different from the legacy `call func@plt` (where `@plt` is simply ignored). The `@plt` syntax was selected was simply due to a quirk of AsmParser: the syntax was supported by all targets until I updated it to be an opt-in feature in a067175 RISC-V favors the `%specifier(expr)` syntax following MIPS and Sparc, and we should follow this convention. This PR adds support for `.word %plt(foo-.)` and `.word %gotpcreel(foo)` and drops `@plt` `@gotpcrel`. * MCValue::SymA can no longer have a SymbolVariant. Add an assert similar to that of AArch64ELFObjectWriter.cpp before https://reviews.llvm.org/D81446 (see my analysis at https://maskray.me/blog/2025-03-16-relocation-generation-in-assemblers if intrigued) * `jump foo@plt, x31` now has a different diagnostic. Pull Request: llvm#132569
1 parent 77dbbb2 commit 2c1a93a

20 files changed

+140
-65
lines changed

lld/test/ELF/riscv-reloc-plt32.s

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
.globl _start
1919
_start:
2020
.data
21-
.word foo@PLT - .
22-
.word foo@PLT - . + 1
23-
.word foo@PLT - . - 1
21+
.word %plt(foo - .)
22+
.word %plt(foo - . + 1)
23+
.word %plt(foo - . - 1)

lld/test/ELF/riscv-undefined-weak.s

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,4 @@ branch:
9797
# PC-NOT: .plt:
9898
# PLT: .plt:
9999

100-
.word target@plt - .
100+
.word %plt(target - .)

lld/test/ELF/riscv64-reloc-got32-pcrel.s

+8-8
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ bar:
1212

1313
.globl _start
1414
_start: // PC = 0x33a8
15-
// bar@GOTPCREL = 0x2398 (got entry for `bar`) - 0x33a8 (.) = 0xf0efffff
16-
// bar@GOTPCREL+4 = 0x2398 (got entry for `bar`) - 0x33ac (.) + 4 = 0xf0efffff
17-
// bar@GOTPCREL-4 = 0x2398 (got entry for `bar`) - 0x33b0 (.) - 4 = 0xe4efffff
15+
// %gotpcrel(bar) = 0x2398 (got entry for `bar`) - 0x33a8 (.) = 0xf0efffff
16+
// %gotpcrel(bar+4) = 0x2398 (got entry for `bar`) - 0x33ac (.) + 4 = 0xf0efffff
17+
// %gotpcrel(bar-4) = 0x2398 (got entry for `bar`) - 0x33b0 (.) - 4 = 0xe4efffff
1818
// CHECK: Contents of section .data:
1919
// CHECK-NEXT: {{.*}} f0efffff f0efffff e4efffff
20-
.word bar@GOTPCREL
21-
.word bar@GOTPCREL+4
22-
.word bar@GOTPCREL-4
20+
.word %gotpcrel(bar)
21+
.word %gotpcrel(bar+4)
22+
.word %gotpcrel(bar-4)
2323

2424
// WARN: relocation R_RISCV_GOT32_PCREL out of range: {{.*}} is not in [-2147483648, 2147483647]; references 'baz'
2525
// WARN: relocation R_RISCV_GOT32_PCREL out of range: {{.*}} is not in [-2147483648, 2147483647]; references 'baz'
26-
.word baz@GOTPCREL+0xffffffff
27-
.word baz@GOTPCREL-0xffffffff
26+
.word %gotpcrel(baz+0xffffffff)
27+
.word %gotpcrel(baz-0xffffffff)

llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h

+5
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,16 @@ class TargetLoweringObjectFileELF : public TargetLoweringObjectFile {
112112
MCSection *getStaticDtorSection(unsigned Priority,
113113
const MCSymbol *KeySym) const override;
114114

115+
virtual const MCExpr *createTargetMCExpr(const MCExpr *Expr,
116+
uint8_t Specifier) const {
117+
return nullptr;
118+
}
115119
const MCExpr *lowerRelativeReference(const GlobalValue *LHS,
116120
const GlobalValue *RHS,
117121
const TargetMachine &TM) const override;
118122

119123
const MCExpr *lowerDSOLocalEquivalent(const DSOLocalEquivalent *Equiv,
124+
const MCSymbolRefExpr *RHS,
120125
const TargetMachine &TM) const override;
121126

122127
MCSection *getSectionForCommandLines() const override;

llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h

+6
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,12 @@ class MCTargetAsmParser : public MCAsmParserExtension {
396396
virtual bool parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) {
397397
return getParser().parsePrimaryExpr(Res, EndLoc, nullptr);
398398
}
399+
// Parse an expression in a data directive, possibly with a relocation
400+
// specifier.
401+
virtual bool parseDataExpr(const MCExpr *&Res) {
402+
SMLoc EndLoc;
403+
return getParser().parseExpression(Res, EndLoc);
404+
}
399405

400406
virtual bool parseRegister(MCRegister &Reg, SMLoc &StartLoc,
401407
SMLoc &EndLoc) = 0;

llvm/include/llvm/Target/TargetLoweringObjectFile.h

+1
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ class TargetLoweringObjectFile : public MCObjectFileInfo {
209209
}
210210

211211
virtual const MCExpr *lowerDSOLocalEquivalent(const DSOLocalEquivalent *Equiv,
212+
const MCSymbolRefExpr *RHS,
212213
const TargetMachine &TM) const {
213214
return nullptr;
214215
}

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -3382,7 +3382,7 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
33823382
return lowerBlockAddressConstant(*BA);
33833383

33843384
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(CV))
3385-
return getObjFileLowering().lowerDSOLocalEquivalent(Equiv, TM);
3385+
return getObjFileLowering().lowerDSOLocalEquivalent(Equiv, nullptr, TM);
33863386

33873387
if (const NoCFIValue *NC = dyn_cast<NoCFIValue>(CV))
33883388
return MCSymbolRefExpr::create(getSymbol(NC->getGlobalValue()), Ctx);
@@ -3481,12 +3481,13 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
34813481
if (!RelocExpr) {
34823482
const MCExpr *LHSExpr =
34833483
MCSymbolRefExpr::create(getSymbol(LHSGV), Ctx);
3484+
auto *RHSExpr = MCSymbolRefExpr::create(getSymbol(RHSGV), Ctx);
34843485
if (DSOEquiv &&
34853486
getObjFileLowering().supportDSOLocalEquivalentLowering())
3486-
LHSExpr =
3487-
getObjFileLowering().lowerDSOLocalEquivalent(DSOEquiv, TM);
3488-
RelocExpr = MCBinaryExpr::createSub(
3489-
LHSExpr, MCSymbolRefExpr::create(getSymbol(RHSGV), Ctx), Ctx);
3487+
RelocExpr = getObjFileLowering().lowerDSOLocalEquivalent(
3488+
DSOEquiv, RHSExpr, TM);
3489+
else
3490+
RelocExpr = MCBinaryExpr::createSub(LHSExpr, RHSExpr, Ctx);
34903491
}
34913492
int64_t Addend = (LHSOffset - RHSOffset).getSExtValue();
34923493
if (Addend != 0)

llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp

+23-7
Original file line numberDiff line numberDiff line change
@@ -1194,18 +1194,34 @@ const MCExpr *TargetLoweringObjectFileELF::lowerRelativeReference(
11941194
MCSymbolRefExpr::create(TM.getSymbol(RHS), getContext()), getContext());
11951195
}
11961196

1197+
// Reference the function or its PLT entry (`Equiv`), optionally with a
1198+
// subtrahend (`RHS`).
11971199
const MCExpr *TargetLoweringObjectFileELF::lowerDSOLocalEquivalent(
1198-
const DSOLocalEquivalent *Equiv, const TargetMachine &TM) const {
1200+
const DSOLocalEquivalent *Equiv, const MCSymbolRefExpr *RHS,
1201+
const TargetMachine &TM) const {
11991202
assert(supportDSOLocalEquivalentLowering());
12001203

1204+
// If GV is dso_local, reference the function itself. Return GV or GV-RHS.
12011205
const auto *GV = Equiv->getGlobalValue();
1202-
1203-
// A PLT entry is not needed for dso_local globals.
1206+
const MCExpr *Res = MCSymbolRefExpr::create(TM.getSymbol(GV), getContext());
1207+
if (RHS)
1208+
Res = MCBinaryExpr::createSub(Res, RHS, getContext());
12041209
if (GV->isDSOLocal() || GV->isImplicitDSOLocal())
1205-
return MCSymbolRefExpr::create(TM.getSymbol(GV), getContext());
1206-
1207-
return MCSymbolRefExpr::create(TM.getSymbol(GV), PLTRelativeSpecifier,
1208-
getContext());
1210+
return Res;
1211+
1212+
// Otherwise, reference the PLT. Return a relocatable expression with the PLT
1213+
// specifier, %plt(GV) or %plt(GV-RHS).
1214+
Res = createTargetMCExpr(Res, PLTRelativeSpecifier);
1215+
if (Res)
1216+
return Res;
1217+
1218+
// If the target only supports the legacy syntax @plt, return GV@plt or
1219+
// GV@plt - RHS.
1220+
Res = MCSymbolRefExpr::create(TM.getSymbol(GV), PLTRelativeSpecifier,
1221+
getContext());
1222+
if (RHS)
1223+
Res = MCBinaryExpr::createSub(Res, RHS, getContext());
1224+
return Res;
12091225
}
12101226

12111227
MCSection *TargetLoweringObjectFileELF::getSectionForCommandLines() const {

llvm/lib/MC/MCAssembler.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ bool MCAssembler::evaluateFixup(const MCFixup &Fixup, const MCFragment *DF,
237237
// A linker relaxation target may emit ADD/SUB relocations for A-B+C. Let
238238
// recordRelocation handle non-VK_None cases like A@plt-B+C.
239239
if (!IsResolved && Target.getSymA() && Target.getSymB() &&
240-
Target.getSymA()->getKind() == MCSymbolRefExpr::VK_None &&
240+
Target.getRefKind() == 0 &&
241241
getBackend().handleAddSubRelocations(*this, *DF, Fixup, Target, Value))
242242
return true;
243243

llvm/lib/MC/MCParser/AsmParser.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3144,7 +3144,7 @@ bool AsmParser::parseDirectiveValue(StringRef IDVal, unsigned Size) {
31443144
auto parseOp = [&]() -> bool {
31453145
const MCExpr *Value;
31463146
SMLoc ExprLoc = getLexer().getLoc();
3147-
if (checkForValidSection() || parseExpression(Value))
3147+
if (checkForValidSection() || getTargetParser().parseDataExpr(Value))
31483148
return true;
31493149
// Special case constant expressions to match code generator.
31503150
if (const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value)) {

llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp

+24-7
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ class RISCVAsmParser : public MCTargetAsmParser {
225225
}
226226

227227
bool parseOperand(OperandVector &Operands, StringRef Mnemonic);
228+
bool parseExprWithSpecifier(const MCExpr *&Res, SMLoc &E);
229+
bool parseDataExpr(const MCExpr *&Res) override;
228230

229231
bool parseDirectiveOption();
230232
bool parseDirectiveAttribute();
@@ -2233,8 +2235,17 @@ ParseStatus RISCVAsmParser::parseOperandWithSpecifier(OperandVector &Operands) {
22332235
SMLoc S = getLoc();
22342236
SMLoc E;
22352237

2236-
if (!parseOptionalToken(AsmToken::Percent) ||
2237-
getLexer().getKind() != AsmToken::Identifier)
2238+
if (!parseOptionalToken(AsmToken::Percent))
2239+
return Error(getLoc(), "expected '%' relocation specifier");
2240+
const MCExpr *Expr = nullptr;
2241+
bool Failed = parseExprWithSpecifier(Expr, E);
2242+
if (!Failed)
2243+
Operands.push_back(RISCVOperand::createImm(Expr, S, E, isRV64()));
2244+
return Failed;
2245+
}
2246+
2247+
bool RISCVAsmParser::parseExprWithSpecifier(const MCExpr *&Res, SMLoc &E) {
2248+
if (getLexer().getKind() != AsmToken::Identifier)
22382249
return Error(getLoc(), "expected '%' relocation specifier");
22392250
StringRef Identifier = getParser().getTok().getIdentifier();
22402251
auto Spec = RISCVMCExpr::getSpecifierForName(Identifier);
@@ -2243,15 +2254,21 @@ ParseStatus RISCVAsmParser::parseOperandWithSpecifier(OperandVector &Operands) {
22432254

22442255
getParser().Lex(); // Eat the identifier
22452256
if (parseToken(AsmToken::LParen, "expected '('"))
2246-
return ParseStatus::Failure;
2257+
return true;
22472258

22482259
const MCExpr *SubExpr;
22492260
if (getParser().parseParenExpression(SubExpr, E))
2250-
return ParseStatus::Failure;
2261+
return true;
22512262

2252-
const MCExpr *ModExpr = RISCVMCExpr::create(SubExpr, *Spec, getContext());
2253-
Operands.push_back(RISCVOperand::createImm(ModExpr, S, E, isRV64()));
2254-
return ParseStatus::Success;
2263+
Res = RISCVMCExpr::create(SubExpr, *Spec, getContext());
2264+
return false;
2265+
}
2266+
2267+
bool RISCVAsmParser::parseDataExpr(const MCExpr *&Res) {
2268+
SMLoc E;
2269+
if (parseOptionalToken(AsmToken::Percent))
2270+
return parseExprWithSpecifier(Res, E);
2271+
return getParser().parseExpression(Res);
22552272
}
22562273

22572274
ParseStatus RISCVAsmParser::parseBareSymbol(OperandVector &Operands) {

llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp

+17-8
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
5050
const MCValue &Target,
5151
const MCFixup &Fixup,
5252
bool IsPCRel) const {
53+
assert((!Target.getSymA() ||
54+
Target.getSymA()->getKind() == MCSymbolRefExpr::VK_None) &&
55+
"sym@specifier should have been rejected");
5356
const MCExpr *Expr = Fixup.getValue();
5457
// Determine the type of the relocation
5558
unsigned Kind = Fixup.getTargetKind();
@@ -64,6 +67,12 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
6467
if (auto *S = Target.getSymA())
6568
cast<MCSymbolELF>(S->getSymbol()).setType(ELF::STT_TLS);
6669
break;
70+
case RISCVMCExpr::VK_PLT:
71+
if (IsPCRel && Kind == FK_Data_4)
72+
break;
73+
Ctx.reportError(Fixup.getLoc(),
74+
"%plt must be PC-relative in a .word directive");
75+
return ELF::R_RISCV_NONE;
6776
}
6877

6978
if (IsPCRel) {
@@ -73,9 +82,8 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
7382
return ELF::R_RISCV_NONE;
7483
case FK_Data_4:
7584
case FK_PCRel_4:
76-
return uint8_t(Target.getAccessVariant()) == RISCVMCExpr::VK_PLT
77-
? ELF::R_RISCV_PLT32
78-
: ELF::R_RISCV_32_PCREL;
85+
return Target.getRefKind() == RISCVMCExpr::VK_PLT ? ELF::R_RISCV_PLT32
86+
: ELF::R_RISCV_32_PCREL;
7987
case RISCV::fixup_riscv_pcrel_hi20:
8088
return ELF::R_RISCV_PCREL_HI20;
8189
case RISCV::fixup_riscv_pcrel_lo12_i:
@@ -129,11 +137,12 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
129137
Ctx.reportError(Fixup.getLoc(), "2-byte data relocations not supported");
130138
return ELF::R_RISCV_NONE;
131139
case FK_Data_4:
132-
if (Expr->getKind() == MCExpr::Target &&
133-
cast<RISCVMCExpr>(Expr)->getSpecifier() == RISCVMCExpr::VK_32_PCREL)
134-
return ELF::R_RISCV_32_PCREL;
135-
if (getSpecifier(Target.getSymA()) == RISCVMCExpr::VK_GOTPCREL)
136-
return ELF::R_RISCV_GOT32_PCREL;
140+
if (Expr->getKind() == MCExpr::Target) {
141+
if (cast<RISCVMCExpr>(Expr)->getSpecifier() == RISCVMCExpr::VK_32_PCREL)
142+
return ELF::R_RISCV_32_PCREL;
143+
if (cast<RISCVMCExpr>(Expr)->getSpecifier() == RISCVMCExpr::VK_GOTPCREL)
144+
return ELF::R_RISCV_GOT32_PCREL;
145+
}
137146
return ELF::R_RISCV_32;
138147
case FK_Data_8:
139148
return ELF::R_RISCV_64;

llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.cpp

-7
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
1818
#include "llvm/TargetParser/Triple.h"
1919
using namespace llvm;
2020

21-
const MCAsmInfo::VariantKindDesc variantKindDescs[] = {
22-
{RISCVMCExpr::VK_GOTPCREL, "GOTPCREL"},
23-
{RISCVMCExpr::VK_PLT, "PLT"},
24-
};
25-
2621
void RISCVMCAsmInfo::anchor() {}
2722

2823
RISCVMCAsmInfo::RISCVMCAsmInfo(const Triple &TT) {
@@ -33,8 +28,6 @@ RISCVMCAsmInfo::RISCVMCAsmInfo(const Triple &TT) {
3328
ExceptionsType = ExceptionHandling::DwarfCFI;
3429
Data16bitsDirective = "\t.half\t";
3530
Data32bitsDirective = "\t.word\t";
36-
37-
initializeVariantKinds(variantKindDescs);
3831
}
3932

4033
const MCExpr *RISCVMCAsmInfo::getExprForFDESymbol(const MCSymbol *Sym,

llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp

+11-4
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res,
9494

9595
Res = MCValue::get(Res.getSymA(), Res.getSymB(), Res.getConstant(),
9696
getSpecifier());
97-
// Custom fixup types are not valid with symbol difference expressions.
98-
return Res.getSymB() ? getSpecifier() == VK_None : true;
97+
// When the subtrahend (SymB) is present, the relocatable expression
98+
// only allows None and PLT as the specifier.
99+
return !Res.getSymB() ||
100+
llvm::is_contained({VK_None, VK_PLT}, getSpecifier());
99101
}
100102

101103
void RISCVMCExpr::visitUsedExpr(MCStreamer &Streamer) const {
@@ -119,14 +121,15 @@ RISCVMCExpr::getSpecifierForName(StringRef name) {
119121
.Case("tlsdesc_load_lo", VK_TLSDESC_LOAD_LO)
120122
.Case("tlsdesc_add_lo", VK_TLSDESC_ADD_LO)
121123
.Case("tlsdesc_call", VK_TLSDESC_CALL)
124+
// Used in data directives
125+
.Case("plt", VK_PLT)
126+
.Case("gotpcrel", VK_GOTPCREL)
122127
.Default(std::nullopt);
123128
}
124129

125130
StringRef RISCVMCExpr::getSpecifierName(Specifier S) {
126131
switch (S) {
127132
case VK_None:
128-
case VK_PLT:
129-
case VK_GOTPCREL:
130133
llvm_unreachable("not used as %specifier()");
131134
case VK_LO:
132135
return "lo";
@@ -162,6 +165,10 @@ StringRef RISCVMCExpr::getSpecifierName(Specifier S) {
162165
return "call_plt";
163166
case VK_32_PCREL:
164167
return "32_pcrel";
168+
case VK_PLT:
169+
return "plt";
170+
case VK_GOTPCREL:
171+
return "gotpcrel";
165172
}
166173
llvm_unreachable("Invalid ELF symbol kind");
167174
}

llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,10 @@ MCSection *RISCVELFTargetObjectFile::getSectionForConstant(
180180
return TargetLoweringObjectFileELF::getSectionForConstant(DL, Kind, C,
181181
Alignment);
182182
}
183+
184+
const MCExpr *
185+
RISCVELFTargetObjectFile::createTargetMCExpr(const MCExpr *Expr,
186+
uint8_t Specifier) const {
187+
return RISCVMCExpr::create(Expr, RISCVMCExpr::Specifier(Specifier),
188+
getContext());
189+
}

llvm/lib/Target/RISCV/RISCVTargetObjectFile.h

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ class RISCVELFTargetObjectFile : public TargetLoweringObjectFileELF {
4848

4949
bool isInSmallSection(uint64_t Size) const;
5050

51+
const MCExpr *createTargetMCExpr(const MCExpr *Expr,
52+
uint8_t Specifier) const override;
53+
5154
const MCExpr *getIndirectSymViaGOTPCRel(const GlobalValue *GV,
5255
const MCSymbol *Sym,
5356
const MCValue &MV, int64_t Offset,

llvm/test/CodeGen/RISCV/dso_local_equivalent.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
declare void @extern_func()
55

66
; CHECK-LABEL: const:
7-
; CHECK-NEXT: .word extern_func@PLT-const
7+
; CHECK-NEXT: .word %plt(extern_func-const)
88

99
;; Note that for riscv32, the ptrtoint will actually upcast the ptr it to an
1010
;; oversized 64-bit pointer that eventually gets truncated. This isn't needed

0 commit comments

Comments
 (0)