Skip to content

[PAC][CodeGen][ELF][AArch64] Support signed GOT with tiny code model #114525

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 4 commits into from
Dec 10, 2024
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
47 changes: 29 additions & 18 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2356,28 +2356,39 @@ void AArch64AsmPrinter::LowerLOADgotAUTH(const MachineInstr &MI) {
const MachineOperand &GAMO = MI.getOperand(1);
assert(GAMO.getOffset() == 0);

MachineOperand GAHiOp(GAMO);
MachineOperand GALoOp(GAMO);
GAHiOp.addTargetFlag(AArch64II::MO_PAGE);
GALoOp.addTargetFlag(AArch64II::MO_PAGEOFF | AArch64II::MO_NC);
if (MI.getMF()->getTarget().getCodeModel() == CodeModel::Tiny) {
MCOperand GAMC;
MCInstLowering.lowerOperand(GAMO, GAMC);
EmitToStreamer(
MCInstBuilder(AArch64::ADR).addReg(AArch64::X17).addOperand(GAMC));
EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
.addReg(AuthResultReg)
.addReg(AArch64::X17)
.addImm(0));
} else {
MachineOperand GAHiOp(GAMO);
MachineOperand GALoOp(GAMO);
GAHiOp.addTargetFlag(AArch64II::MO_PAGE);
GALoOp.addTargetFlag(AArch64II::MO_PAGEOFF | AArch64II::MO_NC);

MCOperand GAMCHi, GAMCLo;
MCInstLowering.lowerOperand(GAHiOp, GAMCHi);
MCInstLowering.lowerOperand(GALoOp, GAMCLo);
MCOperand GAMCHi, GAMCLo;
MCInstLowering.lowerOperand(GAHiOp, GAMCHi);
MCInstLowering.lowerOperand(GALoOp, GAMCLo);

EmitToStreamer(
MCInstBuilder(AArch64::ADRP).addReg(AArch64::X17).addOperand(GAMCHi));
EmitToStreamer(
MCInstBuilder(AArch64::ADRP).addReg(AArch64::X17).addOperand(GAMCHi));

EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
.addReg(AArch64::X17)
.addReg(AArch64::X17)
.addOperand(GAMCLo)
.addImm(0));
EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
.addReg(AArch64::X17)
.addReg(AArch64::X17)
.addOperand(GAMCLo)
.addImm(0));

EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
.addReg(AuthResultReg)
.addReg(AArch64::X17)
.addImm(0));
EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
.addReg(AuthResultReg)
.addReg(AArch64::X17)
.addImm(0));
}

assert(GAMO.isGlobal());
MCSymbol *UndefWeakSym;
Expand Down
8 changes: 7 additions & 1 deletion llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3354,7 +3354,13 @@ ParseStatus AArch64AsmParser::tryParseAdrLabel(OperandVector &Operands) {
// No modifier was specified at all; this is the syntax for an ELF basic
// ADR relocation (unfortunately).
Expr = AArch64MCExpr::create(Expr, AArch64MCExpr::VK_ABS, getContext());
} else {
} else if (ELFRefKind != AArch64MCExpr::VK_GOT_AUTH_PAGE) {
// For tiny code model, we use :got_auth: operator to fill 21-bit imm of
// adr. It's not actually GOT entry page address but the GOT address
// itself - we just share the same variant kind with :got_auth: operator
// applied for adrp.
// TODO: can we somehow get current TargetMachine object to call
// getCodeModel() on it to ensure we are using tiny code model?
return Error(S, "unexpected adr label");
}
}
Expand Down
18 changes: 18 additions & 0 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ unsigned AArch64ELFObjectWriter::getRelocType(MCContext &Ctx,
}
return ELF::R_AARCH64_PREL64;
case AArch64::fixup_aarch64_pcrel_adr_imm21:
if (SymLoc == AArch64MCExpr::VK_GOT_AUTH) {
if (IsILP32) {
Ctx.reportError(Fixup.getLoc(),
"ILP32 ADR AUTH relocation not supported "
"(LP64 eqv: AUTH_GOT_ADR_PREL_LO21)");
return ELF::R_AARCH64_NONE;
}
return ELF::R_AARCH64_AUTH_GOT_ADR_PREL_LO21;
}
if (SymLoc != AArch64MCExpr::VK_ABS)
Ctx.reportError(Fixup.getLoc(),
"invalid symbol kind for ADR relocation");
Expand Down Expand Up @@ -190,6 +199,15 @@ unsigned AArch64ELFObjectWriter::getRelocType(MCContext &Ctx,
return R_CLS(TLSIE_LD_GOTTPREL_PREL19);
if (SymLoc == AArch64MCExpr::VK_GOT)
return R_CLS(GOT_LD_PREL19);
if (SymLoc == AArch64MCExpr::VK_GOT_AUTH) {
if (IsILP32) {
Ctx.reportError(Fixup.getLoc(),
"ILP32 LDR AUTH relocation not supported "
"(LP64 eqv: AUTH_GOT_LD_PREL19)");
return ELF::R_AARCH64_NONE;
}
return ELF::R_AARCH64_AUTH_GOT_LD_PREL19;
}
return R_CLS(LD_PREL_LO19);
case AArch64::fixup_aarch64_pcrel_branch14:
return R_CLS(TSTBR14);
Expand Down
42 changes: 42 additions & 0 deletions llvm/test/CodeGen/AArch64/ptrauth-extern-weak.ll
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
; RUN: llc -mtriple=aarch64-none-linux-gnu -global-isel=1 -global-isel-abort=1 -relocation-model=pic \
; RUN: -mattr=+pauth -o - %s | FileCheck --check-prefixes=CHECK,TRAP %s

; RUN: llc -mtriple=aarch64-none-linux-gnu -code-model=tiny -mattr=+pauth -mattr=+fpac -o - %s | \
; RUN: FileCheck --check-prefixes=CHECK-TINY,NOTRAP-TINY %s
; RUN: llc -mtriple=aarch64-none-linux-gnu -code-model=tiny -mattr=+pauth -o - %s | \
; RUN: FileCheck --check-prefixes=CHECK-TINY,TRAP-TINY %s

;; Note: for FastISel, we fall back to SelectionDAG

declare extern_weak dso_local i32 @var()
Expand Down Expand Up @@ -41,6 +46,24 @@ define ptr @foo() {
; TRAP-NEXT: .Lauth_success_0:
; TRAP-NEXT: mov x0, x16
; CHECK-NEXT: ret

; CHECK-TINY-LABEL: foo:
; CHECK-TINY: adr x17, :got_auth:var
; NOTRAP-TINY-NEXT: ldr x0, [x17]
; NOTRAP-TINY-NEXT: cbz x0, .Lundef_weak0
; NOTRAP-TINY-NEXT: autia x0, x17
; TRAP-TINY-NEXT: ldr x16, [x17]
; TRAP-TINY-NEXT: cbz x16, .Lundef_weak0
; TRAP-TINY-NEXT: autia x16, x17
; CHECK-TINY-NEXT: .Lundef_weak0:
; TRAP-TINY-NEXT: mov x17, x16
; TRAP-TINY-NEXT: xpaci x17
; TRAP-TINY-NEXT: cmp x16, x17
; TRAP-TINY-NEXT: b.eq .Lauth_success_0
; TRAP-TINY-NEXT: brk #0xc470
; TRAP-TINY-NEXT: .Lauth_success_0:
; TRAP-TINY-NEXT: mov x0, x16
; CHECK-TINY-NEXT: ret
}

@arr_var = extern_weak global [10 x i32]
Expand Down Expand Up @@ -68,6 +91,25 @@ define ptr @bar() {
; TRAP-NEXT: mov x8, x16
; CHECK-NEXT: add x0, x8, #20
; CHECK-NEXT: ret

; CHECK-TINY-LABEL: bar:
; CHECK-TINY: adr x17, :got_auth:arr_var
; NOTRAP-TINY-NEXT: ldr x8, [x17]
; NOTRAP-TINY-NEXT: cbz x8, .Lundef_weak1
; NOTRAP-TINY-NEXT: autda x8, x17
; TRAP-TINY-NEXT: ldr x16, [x17]
; TRAP-TINY-NEXT: cbz x16, .Lundef_weak1
; TRAP-TINY-NEXT: autda x16, x17
; CHECK-TINY-NEXT: .Lundef_weak1:
; TRAP-TINY-NEXT: mov x17, x16
; TRAP-TINY-NEXT: xpacd x17
; TRAP-TINY-NEXT: cmp x16, x17
; TRAP-TINY-NEXT: b.eq .Lauth_success_1
; TRAP-TINY-NEXT: brk #0xc472
; TRAP-TINY-NEXT: .Lauth_success_1:
; TRAP-TINY-NEXT: mov x8, x16
; CHECK-TINY-NEXT: add x0, x8, #20
; CHECK-TINY-NEXT: ret
}

!llvm.module.flags = !{!0}
Expand Down
182 changes: 182 additions & 0 deletions llvm/test/CodeGen/AArch64/ptrauth-tiny-model-pic.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
; RUN: llc -verify-machineinstrs -mtriple=aarch64 -mattr=+pauth -mattr=+fpac -code-model=tiny \
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps combine pic and static .ll tests and just use update_llc_test_checks.py

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps combine pic and static .ll tests

While we can do this, I'm not sure if it would give that much value: RUN lines are different (-relocation-model=pic is used in ptrauth-tiny-model-pic.ll) so we'll have x2 RUN lines when combined to one file which might be overwhelming IMHO, and also we'll have to change check prefixes so they don't overlap

and just use update_llc_test_checks.py

I was unable to make update_llc_test_checks.py produce nice output which shows that there are common CHECK lines and different TRAP and NOTRAP lines for -mattr=+fpac missing and present correspondingly. We can have duplicate check lines for both cases, but it consumes more space and doesn't explicitly show that the only difference between fpac and no-fpac case is the absence/presence of trap sequence.

Please let me know if I miss smth and update_llc_test_checks.py is capable of generating such outputs (and I'm just missing smth)

; RUN: -relocation-model=pic < %s | FileCheck --check-prefixes=CHECK,NOTRAP %s
; RUN: llc -verify-machineinstrs -mtriple=aarch64 -mattr=+pauth -code-model=tiny \
; RUN: -relocation-model=pic < %s | FileCheck --check-prefixes=CHECK,TRAP %s

; RUN: llc -verify-machineinstrs -mtriple=aarch64 -mattr=+pauth -mattr=+fpac -code-model=tiny \
; RUN: -relocation-model=pic -fast-isel < %s | FileCheck --check-prefixes=CHECK,NOTRAP %s
; RUN: llc -verify-machineinstrs -mtriple=aarch64 -mattr=+pauth -code-model=tiny \
; RUN: -relocation-model=pic -fast-isel < %s | FileCheck --check-prefixes=CHECK,TRAP %s

; RUN: llc -verify-machineinstrs -mtriple=aarch64 -mattr=+pauth -mattr=+fpac -code-model=tiny \
; RUN: -relocation-model=pic -global-isel -global-isel-abort=1 < %s | FileCheck --check-prefixes=CHECK,NOTRAP %s
; RUN: llc -verify-machineinstrs -mtriple=aarch64 -mattr=+pauth -code-model=tiny \
; RUN: -relocation-model=pic -global-isel -global-isel-abort=1 < %s | FileCheck --check-prefixes=CHECK,TRAP %s

; Note: fast-isel tests here will fall back to isel

@src = external local_unnamed_addr global [65536 x i8], align 1
@dst = external global [65536 x i8], align 1
@ptr = external local_unnamed_addr global ptr, align 8

define dso_preemptable void @foo1() {
; CHECK-LABEL: foo1:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: adr x17, :got_auth:src
; NOTRAP-NEXT: ldr x8, [x17]
; NOTRAP-NEXT: autda x8, x17
; TRAP-NEXT: ldr x16, [x17]
; TRAP-NEXT: autda x16, x17
; TRAP-NEXT: mov x17, x16
; TRAP-NEXT: xpacd x17
; TRAP-NEXT: cmp x16, x17
; TRAP-NEXT: b.eq .Lauth_success_0
; TRAP-NEXT: brk #0xc472
; TRAP-NEXT: .Lauth_success_0:
; TRAP-NEXT: mov x8, x16
; CHECK-NEXT: ldrb w8, [x8]
; CHECK-NEXT: adr x17, :got_auth:dst
; NOTRAP-NEXT: ldr x9, [x17]
; NOTRAP-NEXT: autda x9, x17
; TRAP-NEXT: ldr x16, [x17]
; TRAP-NEXT: autda x16, x17
; TRAP-NEXT: mov x17, x16
; TRAP-NEXT: xpacd x17
; TRAP-NEXT: cmp x16, x17
; TRAP-NEXT: b.eq .Lauth_success_1
; TRAP-NEXT: brk #0xc472
; TRAP-NEXT: .Lauth_success_1:
; TRAP-NEXT: mov x9, x16
; CHECK-NEXT: strb w8, [x9]
; CHECK-NEXT: ret

entry:
%0 = load i8, ptr @src, align 1
store i8 %0, ptr @dst, align 1
ret void
}

define dso_preemptable void @foo2() {
; CHECK-LABEL: foo2:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: adr x17, :got_auth:ptr
; NOTRAP-NEXT: ldr x8, [x17]
; NOTRAP-NEXT: autda x8, x17
; TRAP-NEXT: ldr x16, [x17]
; TRAP-NEXT: autda x16, x17
; TRAP-NEXT: mov x17, x16
; TRAP-NEXT: xpacd x17
; TRAP-NEXT: cmp x16, x17
; TRAP-NEXT: b.eq .Lauth_success_2
; TRAP-NEXT: brk #0xc472
; TRAP-NEXT: .Lauth_success_2:
; TRAP-NEXT: mov x8, x16
; CHECK-NEXT: adr x17, :got_auth:dst
; NOTRAP-NEXT: ldr x9, [x17]
; NOTRAP-NEXT: autda x9, x17
; TRAP-NEXT: ldr x16, [x17]
; TRAP-NEXT: autda x16, x17
; TRAP-NEXT: mov x17, x16
; TRAP-NEXT: xpacd x17
; TRAP-NEXT: cmp x16, x17
; TRAP-NEXT: b.eq .Lauth_success_3
; TRAP-NEXT: brk #0xc472
; TRAP-NEXT: .Lauth_success_3:
; TRAP-NEXT: mov x9, x16
; CHECK-NEXT: str x9, [x8]
; CHECK-NEXT: ret

entry:
store ptr @dst, ptr @ptr, align 8
ret void
}

define dso_preemptable void @foo3() {
; CHECK-LABEL: foo3:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: adr x17, :got_auth:src
; NOTRAP-NEXT: ldr x8, [x17]
; NOTRAP-NEXT: autda x8, x17
; TRAP-NEXT: ldr x16, [x17]
; TRAP-NEXT: autda x16, x17
; TRAP-NEXT: mov x17, x16
; TRAP-NEXT: xpacd x17
; TRAP-NEXT: cmp x16, x17
; TRAP-NEXT: b.eq .Lauth_success_4
; TRAP-NEXT: brk #0xc472
; TRAP-NEXT: .Lauth_success_4:
; TRAP-NEXT: mov x8, x16
; CHECK-NEXT: ldrb w8, [x8]
; CHECK-NEXT: adr x17, :got_auth:ptr
; NOTRAP-NEXT: ldr x9, [x17]
; NOTRAP-NEXT: autda x9, x17
; TRAP-NEXT: ldr x16, [x17]
; TRAP-NEXT: autda x16, x17
; TRAP-NEXT: mov x17, x16
; TRAP-NEXT: xpacd x17
; TRAP-NEXT: cmp x16, x17
; TRAP-NEXT: b.eq .Lauth_success_5
; TRAP-NEXT: brk #0xc472
; TRAP-NEXT: .Lauth_success_5:
; TRAP-NEXT: mov x9, x16
; CHECK-NEXT: ldr x9, [x9]
; CHECK-NEXT: strb w8, [x9]
; CHECK-NEXT: ret

entry:
%0 = load i8, ptr @src, align 1
%1 = load ptr, ptr @ptr, align 8
store i8 %0, ptr %1, align 1
ret void
}

@lsrc = internal global i8 0, align 4
@ldst = internal global i8 0, align 4
@lptr = internal global ptr null, align 8

declare void @func(...)

define dso_preemptable ptr @externfuncaddr() {
; CHECK-LABEL: externfuncaddr:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: adr x17, :got_auth:func
; NOTRAP-NEXT: ldr x0, [x17]
; NOTRAP-NEXT: autia x0, x17
; TRAP-NEXT: ldr x16, [x17]
; TRAP-NEXT: autia x16, x17
; TRAP-NEXT: mov x17, x16
; TRAP-NEXT: xpaci x17
; TRAP-NEXT: cmp x16, x17
; TRAP-NEXT: b.eq .Lauth_success_6
; TRAP-NEXT: brk #0xc470
; TRAP-NEXT: .Lauth_success_6:
; TRAP-NEXT: mov x0, x16
; CHECK-NEXT: ret

entry:
ret ptr @func
}

define dso_preemptable ptr @localfuncaddr() {
; CHECK-LABEL: localfuncaddr:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: adr x17, :got_auth:externfuncaddr
; NOTRAP-NEXT: ldr x0, [x17]
; NOTRAP-NEXT: autia x0, x17
; TRAP-NEXT: ldr x16, [x17]
; TRAP-NEXT: autia x16, x17
; TRAP-NEXT: mov x17, x16
; TRAP-NEXT: xpaci x17
; TRAP-NEXT: cmp x16, x17
; TRAP-NEXT: b.eq .Lauth_success_7
; TRAP-NEXT: brk #0xc470
; TRAP-NEXT: .Lauth_success_7:
; TRAP-NEXT: mov x0, x16
; CHECK-NEXT: ret

entry:
ret ptr @externfuncaddr
}

!llvm.module.flags = !{!0}
!0 = !{i32 8, !"ptrauth-elf-got", i32 1}
Loading
Loading