Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
11de49f
[𝘀𝗽𝗿] changes to main this commit is based on
pcc Mar 28, 2025
d70efad
[𝘀𝗽𝗿] initial version
pcc Mar 28, 2025
dd8803d
[𝘀𝗽𝗿] changes introduced through rebase
pcc Apr 10, 2025
2d015de
Fix bugs and add test
pcc Apr 10, 2025
96e7da9
Undo unnecessary change
pcc Apr 10, 2025
cf11f92
[𝘀𝗽𝗿] changes introduced through rebase
pcc Apr 10, 2025
b7b6140
Add comments
pcc Apr 10, 2025
bd3f5d7
[𝘀𝗽𝗿] changes introduced through rebase
pcc May 13, 2025
6b8482a
Implement new proposal
pcc May 13, 2025
818d4ca
[𝘀𝗽𝗿] changes introduced through rebase
pcc May 24, 2025
38bbafd
Rebase
pcc May 24, 2025
e1922f6
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 9, 2025
e137c55
Rebase
pcc Jul 9, 2025
914393d
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 10, 2025
3b3df34
Fix test
pcc Jul 10, 2025
643eb35
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 11, 2025
a0d52fa
Rebase
pcc Jul 11, 2025
a7ac801
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 19, 2025
3b1139d
Update relocation type number
pcc Jul 19, 2025
3029464
[𝘀𝗽𝗿] changes introduced through rebase
pcc Jul 30, 2025
d5b7987
Rebase
pcc Jul 30, 2025
9fa7de1
[𝘀𝗽𝗿] changes introduced through rebase
pcc Aug 1, 2025
5668bff
Rebase
pcc Aug 1, 2025
b33113c
Fix build
pcc Aug 1, 2025
d0b8663
[𝘀𝗽𝗿] changes introduced through rebase
pcc Sep 3, 2025
7cb284e
Rebase
pcc Sep 3, 2025
1137b9e
Split out lld changes
pcc Sep 3, 2025
48a46e8
[𝘀𝗽𝗿] changes introduced through rebase
igorkudrin Oct 9, 2025
970703d
Rebase
pcc Oct 9, 2025
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
17 changes: 16 additions & 1 deletion lld/ELF/Arch/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ AArch64::AArch64(Ctx &ctx) : TargetInfo(ctx) {
copyRel = R_AARCH64_COPY;
relativeRel = R_AARCH64_RELATIVE;
iRelativeRel = R_AARCH64_IRELATIVE;
iRelSymbolicRel = R_AARCH64_FUNCINIT64;
gotRel = R_AARCH64_GLOB_DAT;
pltRel = R_AARCH64_JUMP_SLOT;
symbolicRel = R_AARCH64_ABS64;
Expand All @@ -137,6 +138,7 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
case R_AARCH64_ABS16:
case R_AARCH64_ABS32:
case R_AARCH64_ABS64:
case R_AARCH64_FUNCINIT64:
case R_AARCH64_ADD_ABS_LO12_NC:
case R_AARCH64_LDST128_ABS_LO12_NC:
case R_AARCH64_LDST16_ABS_LO12_NC:
Expand All @@ -154,6 +156,12 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
case R_AARCH64_MOVW_UABS_G2_NC:
case R_AARCH64_MOVW_UABS_G3:
return R_ABS;
case R_AARCH64_PATCHINST:
if (!isAbsolute(s))
Err(ctx) << getErrorLoc(ctx, loc)
<< "R_AARCH64_PATCHINST relocation against non-absolute symbol "
<< &s;
return R_ABS;
case R_AARCH64_AUTH_ABS64:
return RE_AARCH64_AUTH;
case R_AARCH64_TLSDESC_ADR_PAGE21:
Expand Down Expand Up @@ -261,7 +269,8 @@ bool AArch64::usesOnlyLowPageBits(RelType type) const {
}

RelType AArch64::getDynRel(RelType type) const {
if (type == R_AARCH64_ABS64 || type == R_AARCH64_AUTH_ABS64)
if (type == R_AARCH64_ABS64 || type == R_AARCH64_AUTH_ABS64 ||
type == R_AARCH64_FUNCINIT64)
return type;
return R_AARCH64_NONE;
}
Expand Down Expand Up @@ -506,6 +515,12 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
checkIntUInt(ctx, loc, val, 32, rel);
write32(ctx, loc, val);
break;
case R_AARCH64_PATCHINST:
if (!rel.sym->isUndefined()) {
checkUInt(ctx, loc, val, 32, rel);
write32le(loc, val);
}
break;
case R_AARCH64_PLT32:
case R_AARCH64_GOTPCREL32:
checkInt(ctx, loc, val, 32, rel);
Expand Down
23 changes: 20 additions & 3 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ static RelType getMipsPairType(RelType type, bool isLocal) {

// True if non-preemptable symbol always has the same value regardless of where
// the DSO is loaded.
static bool isAbsolute(const Symbol &sym) {
bool elf::isAbsolute(const Symbol &sym) {
if (sym.isUndefined())
return true;
if (const auto *dr = dyn_cast<Defined>(&sym))
Expand Down Expand Up @@ -989,8 +989,8 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
// only the low bits are used.
if (e == R_GOT || e == R_PLT)
return ctx.target->usesOnlyLowPageBits(type) || !ctx.arg.isPic;
// R_AARCH64_AUTH_ABS64 requires a dynamic relocation.
if (e == RE_AARCH64_AUTH)
// R_AARCH64_AUTH_ABS64 and iRelSymbolicRel require a dynamic relocation.
if (e == RE_AARCH64_AUTH || type == ctx.target->iRelSymbolicRel)
return false;

// The behavior of an undefined weak reference is implementation defined.
Expand Down Expand Up @@ -1163,6 +1163,23 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
}
return;
}
if (LLVM_UNLIKELY(type == ctx.target->iRelSymbolicRel)) {
if (sym.isPreemptible) {
auto diag = Err(ctx);
diag << "relocation " << type
<< " cannot be used against preemptible symbol '" << &sym << "'";
printLocation(diag, *sec, sym, offset);
} else if (isIfunc) {
auto diag = Err(ctx);
diag << "relocation " << type
<< " cannot be used against ifunc symbol '" << &sym << "'";
printLocation(diag, *sec, sym, offset);
} else {
part.relaDyn->addReloc({ctx.target->iRelativeRel, sec, offset, false,
sym, addend, R_ABS});
return;
}
}
part.relaDyn->addSymbolReloc(rel, *sec, offset, sym, addend, type);

// MIPS ABI turns using of GOT and dynamic relocations inside out.
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/Relocations.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ void addGotEntry(Ctx &ctx, Symbol &sym);
void hexagonTLSSymbolUpdate(Ctx &ctx);
bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections);

bool isAbsolute(const Symbol &sym);

class ThunkSection;
class Thunk;
class InputSectionDescription;
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ class TargetInfo {
RelType relativeRel = 0;
RelType iRelativeRel = 0;
RelType symbolicRel = 0;
RelType iRelSymbolicRel = 0;
RelType tlsDescRel = 0;
RelType tlsGotRel = 0;
RelType tlsModuleIndexRel = 0;
Expand Down
18 changes: 18 additions & 0 deletions lld/test/ELF/aarch64-funcinit64-invalid.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# REQUIRES: aarch64

# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
# RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck --check-prefix=ERR %s

.rodata
# ERR: relocation R_AARCH64_FUNCINIT64 cannot be used against local symbol
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test error: for error messages

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (in #156564).

.8byte func@FUNCINIT

.data
# ERR: relocation R_AARCH64_FUNCINIT64 cannot be used against ifunc symbol 'ifunc'
.8byte ifunc@FUNCINIT

.text
func:
.type ifunc, @gnu_indirect_function
ifunc:
ret
19 changes: 19 additions & 0 deletions lld/test/ELF/aarch64-funcinit64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# REQUIRES: aarch64

# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
# RUN: ld.lld %t.o -o %t
# RUN: llvm-readelf -s -r %t | FileCheck %s
# RUN: ld.lld %t.o -o %t -pie
# RUN: llvm-readelf -s -r %t | FileCheck %s
# RUN: not ld.lld %t.o -o %t -shared 2>&1 | FileCheck --check-prefix=ERR %s

.data
# CHECK: R_AARCH64_IRELATIVE [[FOO:[0-9a-f]*]]
# ERR: relocation R_AARCH64_FUNCINIT64 cannot be used against preemptible symbol 'foo'
.8byte foo@FUNCINIT
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although not this patch, MaskRay is proposing that we use a different syntax for relocation specifiers to avoid ambiguity with the addend: https://maskray.me/blog/2025-03-16-relocation-generation-in-assemblers

I'm proposing that aarch64 ELF follows this for data relocation specifiers (first one in https://github.com/ARM-software/abi-aa/pull/330/files#diff-c74a0dce6771ac7b499e84c140122aaa972bd9d63aed84863e675ecc9b4b2c32R659)

I'm assuming that we could migrate to this syntax at a later date if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems fine to me when the new syntax for relocation specifiers is implemented. We can make it look like this:

.8byte %funcinit(foo)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Porting my RISC-V change from @gotpcrel to %gotpcrel(...) should be straightforward. I'd like to get the assembler change for %funcinit done now rather than postponing it, so it doesn't fall by the wayside.

Created #155776

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully the issue raised on that PR will be resolved soon. Otherwise if it takes longer than a few days I'd prefer not to keep this blocked on changing the syntax. This change has had enough blockers already and it should be straightforward to remove the support for the old syntax once the new syntax lands.


.text
# CHECK: {{0*}}[[FOO]] {{.*}} foo
.globl foo
foo:
ret
87 changes: 87 additions & 0 deletions lld/test/ELF/aarch64-patchinst.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# RUN: rm -rf %t && split-file %s %t
# RUN: llvm-mc -filetype=obj -triple=aarch64 %t/use.s -o %t/use-le.o
# RUN: llvm-mc -filetype=obj -triple=aarch64 %t/def.s -o %t/def-le.o
# RUN: llvm-mc -filetype=obj -triple=aarch64 %t/rel.s -o %t/rel-le.o

## Deactivation symbol used without being defined: instruction emitted as usual.
# RUN: ld.lld -o %t/undef-le %t/use-le.o --emit-relocs
# RUN: llvm-objdump -r %t/undef-le | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t/undef-le | FileCheck --check-prefix=UNDEF %s
# RUN: ld.lld -pie -o %t/undef-le %t/use-le.o --emit-relocs
# RUN: llvm-objdump -r %t/undef-le | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t/undef-le | FileCheck --check-prefix=UNDEF %s

## Deactivation symbol defined: instructions overwritten with NOPs.
# RUN: ld.lld -o %t/def-le %t/use-le.o %t/def-le.o --emit-relocs
# RUN: llvm-objdump -r %t/def-le | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t/def-le | FileCheck --check-prefix=DEF %s
# RUN: ld.lld -pie -o %t/def-le %t/use-le.o %t/def-le.o --emit-relocs
# RUN: llvm-objdump -r %t/def-le | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t/def-le | FileCheck --check-prefix=DEF %s

## Relocation pointing to a non-SHN_UNDEF non-SHN_ABS symbol is an error.
# RUN: not ld.lld -o %t/rel-le %t/use-le.o %t/rel-le.o 2>&1 | FileCheck --check-prefix=ERROR %s
# RUN: not ld.lld -pie -o %t/rel-le %t/use-le.o %t/rel-le.o 2>&1 | FileCheck --check-prefix=ERROR %s

## Behavior unchanged by endianness: relocation always written as little endian.
# RUN: llvm-mc -filetype=obj -triple=aarch64_be %t/use.s -o %t/use-be.o
# RUN: llvm-mc -filetype=obj -triple=aarch64_be %t/def.s -o %t/def-be.o
# RUN: llvm-mc -filetype=obj -triple=aarch64_be %t/rel.s -o %t/rel-be.o
# RUN: ld.lld -o %t/undef-be %t/use-be.o --emit-relocs
# RUN: llvm-objdump -r %t/undef-be | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t/undef-be | FileCheck --check-prefix=UNDEF %s
# RUN: ld.lld -pie -o %t/undef-be %t/use-be.o --emit-relocs
# RUN: llvm-objdump -r %t/undef-be | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t/undef-be | FileCheck --check-prefix=UNDEF %s
# RUN: ld.lld -o %t/def-be %t/use-be.o %t/def-be.o --emit-relocs
# RUN: llvm-objdump -r %t/def-be | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t/def-be | FileCheck --check-prefix=DEF %s
# RUN: ld.lld -pie -o %t/def-be %t/use-be.o %t/def-be.o --emit-relocs
# RUN: llvm-objdump -r %t/def-be | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %t/def-be | FileCheck --check-prefix=DEF %s
# RUN: not ld.lld -o %t/rel-be %t/use-be.o %t/rel-be.o 2>&1 | FileCheck --check-prefix=ERROR %s
# RUN: not ld.lld -pie -o %t/rel-be %t/use-be.o %t/rel-be.o 2>&1 | FileCheck --check-prefix=ERROR %s

# RELOC: R_AARCH64_JUMP26
# RELOC-NEXT: R_AARCH64_PATCHINST ds
# RELOC-NEXT: R_AARCH64_PATCHINST ds
# RELOC-NEXT: R_AARCH64_PATCHINST ds0+0xd503201f

#--- use.s
.weak ds
.weak ds0
# This instruction has a single relocation: the DS relocation.
# UNDEF: add x0, x1, x2
# DEF: nop
# ERROR: R_AARCH64_PATCHINST relocation against non-absolute symbol ds
.reloc ., R_AARCH64_PATCHINST, ds
add x0, x1, x2
# This instruction has two relocations: the DS relocation and the JUMP26 to f1.
# Make sure that the DS relocation takes precedence.
.reloc ., R_AARCH64_PATCHINST, ds
# UNDEF: b {{.*}} <f1>
# DEF: nop
# ERROR: R_AARCH64_PATCHINST relocation against non-absolute symbol ds
b f1
# Alternative representation: instruction opcode stored in addend.
# UNDEF: add x3, x4, x5
# DEF: nop
# ERROR: R_AARCH64_PATCHINST relocation against non-absolute symbol ds0
.reloc ., R_AARCH64_PATCHINST, ds0 + 0xd503201f
add x3, x4, x5

.section .text.f1,"ax",@progbits
f1:
ret

#--- def.s
.globl ds
ds = 0xd503201f
.globl ds0
ds0 = 0

#--- rel.s
.globl ds
ds:
.globl ds0
ds0:
2 changes: 2 additions & 0 deletions llvm/include/llvm/BinaryFormat/ELFRelocs/AArch64.def
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ ELF_RELOC(R_AARCH64_LD64_GOT_LO12_NC, 0x138)
ELF_RELOC(R_AARCH64_LD64_GOTPAGE_LO15, 0x139)
ELF_RELOC(R_AARCH64_PLT32, 0x13a)
ELF_RELOC(R_AARCH64_GOTPCREL32, 0x13b)
ELF_RELOC(R_AARCH64_PATCHINST, 0x13c)
ELF_RELOC(R_AARCH64_FUNCINIT64, 0x13d)
// General dynamic TLS relocations
ELF_RELOC(R_AARCH64_TLSGD_ADR_PREL21, 0x200)
ELF_RELOC(R_AARCH64_TLSGD_ADR_PAGE21, 0x201)
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8193,6 +8193,8 @@ bool AArch64AsmParser::parseDataExpr(const MCExpr *&Res) {
Spec = AArch64::S_GOTPCREL;
else if (Identifier == "plt")
Spec = AArch64::S_PLT;
else if (Identifier == "funcinit")
Spec = AArch64::S_FUNCINIT;
}
if (Spec == AArch64::S_None)
return Error(Loc, "invalid relocation specifier");
Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class AArch64ELFObjectWriter : public MCELFObjectTargetWriter {
bool IsPCRel) const override;
bool needsRelocateWithSymbol(const MCValue &, unsigned Type) const override;
bool isNonILP32reloc(const MCFixup &Fixup, AArch64::Specifier RefKind) const;
void sortRelocs(std::vector<ELFRelocationEntry> &Relocs) override;

bool IsILP32;
};
Expand Down Expand Up @@ -231,6 +232,8 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
}
if (RefKind == AArch64::S_AUTH || RefKind == AArch64::S_AUTHADDR)
return ELF::R_AARCH64_AUTH_ABS64;
if (RefKind == AArch64::S_FUNCINIT)
return ELF::R_AARCH64_FUNCINIT64;
return ELF::R_AARCH64_ABS64;
}
case AArch64::fixup_aarch64_add_imm12:
Expand Down Expand Up @@ -497,6 +500,17 @@ bool AArch64ELFObjectWriter::needsRelocateWithSymbol(const MCValue &Val,
Val.getSpecifier());
}

void AArch64ELFObjectWriter::sortRelocs(
std::vector<ELFRelocationEntry> &Relocs) {
// PATCHINST relocations should be applied last because they may overwrite the
// whole instruction and so should take precedence over other relocations that
// modify operands of the original instruction.
std::stable_partition(Relocs.begin(), Relocs.end(),
[](const ELFRelocationEntry &R) {
return R.Type != ELF::R_AARCH64_PATCHINST;
});
}

std::unique_ptr<MCObjectTargetWriter>
llvm::createAArch64ELFObjectWriter(uint8_t OSABI, bool IsILP32) {
return std::make_unique<AArch64ELFObjectWriter>(OSABI, IsILP32);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const MCAsmInfo::AtSpecifier ELFAtSpecifiers[] = {
{AArch64::S_GOT, "GOT"},
{AArch64::S_GOTPCREL, "GOTPCREL"},
{AArch64::S_PLT, "PLT"},
{AArch64::S_FUNCINIT, "FUNCINIT"},
};

const MCAsmInfo::AtSpecifier MachOAtSpecifiers[] = {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ enum {
// ELF relocation specifiers in data directives:
S_PLT = 0x400,
S_GOTPCREL,
S_FUNCINIT,

// Mach-O @ relocation specifiers:
S_MACHO_GOT,
Expand Down
3 changes: 3 additions & 0 deletions llvm/test/MC/AArch64/data-directive-specifier.s
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ l:
# CHECK-NEXT: 0x8 R_AARCH64_PLT32 extern 0x4
# CHECK-NEXT: 0xC R_AARCH64_PLT32 g 0x8
# CHECK-NEXT: 0x10 R_AARCH64_PLT32 g 0x18
# CHECK-NEXT: 0x14 R_AARCH64_FUNCINIT64 .text 0x0
# CHECK-NEXT: }
.data
.word l@plt - .
Expand All @@ -21,6 +22,8 @@ l:
.word g@plt - . + 8
.word g@plt - .data + 8

.quad l@funcinit

# CHECK: Section ({{.*}}) .rela.data1 {
# CHECK-NEXT: 0x0 R_AARCH64_GOTPCREL32 data1 0x0
# CHECK-NEXT: 0x4 R_AARCH64_GOTPCREL32 extern 0x4
Expand Down
7 changes: 7 additions & 0 deletions llvm/test/MC/AArch64/patchinst.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: llvm-mc -triple aarch64-elf -filetype=obj %s -o - | llvm-objdump -r - | FileCheck %s

// Test that PATCHINST appears after JUMP26.
// CHECK: R_AARCH64_JUMP26
// CHECK-NEXT: R_AARCH64_PATCHINST
.reloc ., R_AARCH64_PATCHINST, ds
b f1