Skip to content

[PAC][CodeGen][ELF][AArch64] Support signed GOT #96164

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 13 commits into from
Aug 7, 2024
54 changes: 46 additions & 8 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,10 @@ void AArch64AsmPrinter::LowerMOVaddrPAC(const MachineInstr &MI) {
};

const bool IsGOTLoad = MI.getOpcode() == AArch64::LOADgotPAC;
const bool IsELFSignedGOT = MI.getParent()
->getParent()
->getInfo<AArch64FunctionInfo>()
->hasELFSignedGOT();
MachineOperand GAOp = MI.getOperand(0);
const uint64_t KeyC = MI.getOperand(1).getImm();
assert(KeyC <= AArch64PACKey::LAST &&
Expand All @@ -2177,9 +2181,16 @@ void AArch64AsmPrinter::LowerMOVaddrPAC(const MachineInstr &MI) {
// Emit:
// target materialization:
// - via GOT:
// adrp x16, :got:target
// ldr x16, [x16, :got_lo12:target]
// add offset to x16 if offset != 0
// - unsigned GOT:
// adrp x16, :got:target
// ldr x16, [x16, :got_lo12:target]
// add offset to x16 if offset != 0
// - ELF signed GOT:
// adrp x17, :got:target
// add x17, x17, :got_auth_lo12:target
// ldr x16, [x17]
// aut{i|d}a x16, x17
// add offset to x16 if offset != 0
//
// - direct:
// adrp x16, target
Expand Down Expand Up @@ -2222,13 +2233,40 @@ void AArch64AsmPrinter::LowerMOVaddrPAC(const MachineInstr &MI) {
MCInstLowering.lowerOperand(GAMOLo, GAMCLo);

EmitAndIncrement(
MCInstBuilder(AArch64::ADRP).addReg(AArch64::X16).addOperand(GAMCHi));
MCInstBuilder(AArch64::ADRP)
.addReg(IsGOTLoad && IsELFSignedGOT ? AArch64::X17 : AArch64::X16)
.addOperand(GAMCHi));

if (IsGOTLoad) {
EmitAndIncrement(MCInstBuilder(AArch64::LDRXui)
.addReg(AArch64::X16)
.addReg(AArch64::X16)
.addOperand(GAMCLo));
if (IsELFSignedGOT) {
EmitAndIncrement(MCInstBuilder(AArch64::ADDXri)
.addReg(AArch64::X17)
.addReg(AArch64::X17)
.addOperand(GAMCLo)
.addImm(0));

EmitAndIncrement(MCInstBuilder(AArch64::LDRXui)
.addReg(AArch64::X16)
.addReg(AArch64::X17)
.addImm(0));

assert(GAOp.isGlobal());
assert(GAOp.getGlobal()->getValueType() != nullptr);
unsigned AuthOpcode = GAOp.getGlobal()->getValueType()->isFunctionTy()
? AArch64::AUTIA
: AArch64::AUTDA;

EmitAndIncrement(MCInstBuilder(AuthOpcode)
.addReg(AArch64::X16)
.addReg(AArch64::X16)
.addReg(AArch64::X17));

} else {
EmitAndIncrement(MCInstBuilder(AArch64::LDRXui)
.addReg(AArch64::X16)
.addReg(AArch64::X16)
.addOperand(GAMCLo));
}
} else {
EmitAndIncrement(MCInstBuilder(AArch64::ADDXri)
.addReg(AArch64::X16)
Expand Down
33 changes: 33 additions & 0 deletions llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1291,7 +1291,40 @@ bool AArch64ExpandPseudo::expandMI(MachineBasicBlock &MBB,
MI.eraseFromParent();
return true;
}
case AArch64::LOADgotAUTH: {
Register DstReg = MI.getOperand(0).getReg();
const MachineOperand &MO1 = MI.getOperand(1);

MachineOperand GAHiOp(MO1);
MachineOperand GALoOp(MO1);
GAHiOp.addTargetFlag(AArch64II::MO_PAGE);
GALoOp.addTargetFlag(AArch64II::MO_PAGEOFF | AArch64II::MO_NC);

DebugLoc DL = MI.getDebugLoc();
BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ADRP), AArch64::X16)
.add(GAHiOp);

BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADDXri), AArch64::X16)
.addReg(AArch64::X16)
.add(GALoOp)
.addImm(0);

BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRXui), DstReg)
.addReg(AArch64::X16)
.addImm(0);

assert(MO1.isGlobal());
assert(MO1.getGlobal()->getValueType() != nullptr);
unsigned AuthOpcode = MO1.getGlobal()->getValueType()->isFunctionTy()
? AArch64::AUTIA
: AArch64::AUTDA;
BuildMI(MBB, MBBI, DL, TII->get(AuthOpcode), DstReg)
.addReg(DstReg)
.addReg(AArch64::X16);

MI.eraseFromParent();
return true;
}
case AArch64::LOADgot: {
MachineFunction *MF = MBB.getParent();
Register DstReg = MI.getOperand(0).getReg();
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/AArch64/AArch64FastISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,9 @@ unsigned AArch64FastISel::materializeGV(const GlobalValue *GV) {
if (!Subtarget->useSmallAddressing() && !Subtarget->isTargetMachO())
return 0;

if (FuncInfo.MF->getInfo<AArch64FunctionInfo>()->hasELFSignedGOT())
return 0;

unsigned OpFlags = Subtarget->ClassifyGlobalReference(GV, TM);

EVT DestEVT = TLI.getValueType(DL, GV->getType(), true);
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9225,6 +9225,11 @@ SDValue AArch64TargetLowering::getGOT(NodeTy *N, SelectionDAG &DAG,
SDValue GotAddr = getTargetNode(N, Ty, DAG, AArch64II::MO_GOT | Flags);
// FIXME: Once remat is capable of dealing with instructions with register
// operands, expand this into two nodes instead of using a wrapper node.
if (DAG.getMachineFunction()
.getInfo<AArch64FunctionInfo>()
->hasELFSignedGOT())
return SDValue(DAG.getMachineNode(AArch64::LOADgotAUTH, DL, Ty, GotAddr),
0);
return DAG.getNode(AArch64ISD::LOADgot, DL, Ty, GotAddr);
}

Expand Down
7 changes: 6 additions & 1 deletion llvm/lib/Target/AArch64/AArch64InstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -1872,7 +1872,7 @@ let Predicates = [HasPAuth] in {
Sched<[WriteI, ReadI]> {
let isReMaterializable = 1;
let isCodeGenOnly = 1;
let Size = 40; // 12 fixed + 28 variable, for pointer offset, and discriminator
let Size = 48; // 12 fixed + 36 variable, for pointer offset, and discriminator
let Defs = [X16,X17];
}

Expand Down Expand Up @@ -1911,6 +1911,11 @@ let Predicates = [HasPAuth] in {
tcGPR64:$AddrDisc),
(AUTH_TCRETURN_BTI tcGPRx16x17:$dst, imm:$FPDiff, imm:$Key,
imm:$Disc, tcGPR64:$AddrDisc)>;

def LOADgotAUTH : Pseudo<(outs GPR64common:$dst), (ins i64imm:$addr), []>,
Sched<[WriteI, ReadI]> {
let Defs = [X16];
}
}

// v9.5-A pointer authentication extensions
Expand Down
10 changes: 7 additions & 3 deletions llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//

#include "AArch64MCInstLower.h"
#include "AArch64MachineFunctionInfo.h"
#include "MCTargetDesc/AArch64MCExpr.h"
#include "Utils/AArch64BaseInfo.h"
#include "llvm/CodeGen/AsmPrinter.h"
Expand Down Expand Up @@ -185,9 +186,12 @@ MCOperand AArch64MCInstLower::lowerSymbolOperandELF(const MachineOperand &MO,
MCSymbol *Sym) const {
uint32_t RefFlags = 0;

if (MO.getTargetFlags() & AArch64II::MO_GOT)
RefFlags |= AArch64MCExpr::VK_GOT;
else if (MO.getTargetFlags() & AArch64II::MO_TLS) {
if (MO.getTargetFlags() & AArch64II::MO_GOT) {
const MachineFunction *MF = MO.getParent()->getParent()->getParent();
RefFlags |= (MF->getInfo<AArch64FunctionInfo>()->hasELFSignedGOT()
? AArch64MCExpr::VK_GOT_AUTH
: AArch64MCExpr::VK_GOT);
} else if (MO.getTargetFlags() & AArch64II::MO_TLS) {
TLSModel::Model Model;
if (MO.isGlobal()) {
const GlobalValue *GV = MO.getGlobal();
Expand Down
25 changes: 25 additions & 0 deletions llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "AArch64MachineFunctionInfo.h"
#include "AArch64InstrInfo.h"
#include "AArch64Subtarget.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
Expand Down Expand Up @@ -72,6 +73,29 @@ static bool ShouldSignWithBKey(const Function &F, const AArch64Subtarget &STI) {
return Key == "b_key";
}

// Determine if we need to treat pointers in GOT as signed (as described in
// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#appendix-signed-got)
// based on PAuth core info encoded as "aarch64-elf-pauthabi-platform" and
// "aarch64-elf-pauthabi-version" module flags. Currently, only
// AARCH64_PAUTH_PLATFORM_LLVM_LINUX platform supports signed GOT with
// AARCH64_PAUTH_PLATFORM_LLVM_LINUX_VERSION_GOT bit in version value set.
static bool hasELFSignedGOTHelper(const Function &F,
const AArch64Subtarget *STI) {
if (!Triple(STI->getTargetTriple()).isOSBinFormatELF())
return false;
const Module *M = F.getParent();
const auto *PAP = mdconst::extract_or_null<ConstantInt>(
M->getModuleFlag("aarch64-elf-pauthabi-platform"));
if (!PAP || PAP->getZExtValue() != ELF::AARCH64_PAUTH_PLATFORM_LLVM_LINUX)
return false;
const auto *PAV = mdconst::extract_or_null<ConstantInt>(
M->getModuleFlag("aarch64-elf-pauthabi-version"));
if (!PAV)
return false;
return PAV->getZExtValue() &
(1 << ELF::AARCH64_PAUTH_PLATFORM_LLVM_LINUX_VERSION_GOT);
}

AArch64FunctionInfo::AArch64FunctionInfo(const Function &F,
const AArch64Subtarget *STI) {
// If we already know that the function doesn't have a redzone, set
Expand All @@ -80,6 +104,7 @@ AArch64FunctionInfo::AArch64FunctionInfo(const Function &F,
HasRedZone = false;
std::tie(SignReturnAddress, SignReturnAddressAll) = GetSignReturnAddress(F);
SignWithBKey = ShouldSignWithBKey(F, *STI);
HasELFSignedGOT = hasELFSignedGOTHelper(F, STI);
// TODO: skip functions that have no instrumented allocas for optimization
IsMTETagged = F.hasFnAttribute(Attribute::SanitizeMemTag);

Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ class AArch64FunctionInfo final : public MachineFunctionInfo {
/// SignWithBKey modifies the default PAC-RET mode to signing with the B key.
bool SignWithBKey = false;

/// HasELFSignedGOT is true if the target binary format is ELF and the IR
/// module containing the corresponding function has the following flags:
/// - aarch64-elf-pauthabi-platform flag equal to
/// AARCH64_PAUTH_PLATFORM_LLVM_LINUX;
/// - aarch64-elf-pauthabi-version flag with
/// AARCH64_PAUTH_PLATFORM_LLVM_LINUX_VERSION_GOT bit set.
bool HasELFSignedGOT = false;

/// SigningInstrOffset captures the offset of the PAC-RET signing instruction
/// within the prologue, so it can be re-used for authentication in the
/// epilogue when using PC as a second salt (FEAT_PAuth_LR)
Expand Down Expand Up @@ -509,6 +517,8 @@ class AArch64FunctionInfo final : public MachineFunctionInfo {

bool shouldSignWithBKey() const { return SignWithBKey; }

bool hasELFSignedGOT() const { return HasELFSignedGOT; }

MCSymbol *getSigningInstrLabel() const { return SignInstrLabel; }
void setSigningInstrLabel(MCSymbol *Label) { SignInstrLabel = Label; }

Expand Down
32 changes: 19 additions & 13 deletions llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ class AArch64Operand : public MCParsedAsmOperand {
if (DarwinRefKind == MCSymbolRefExpr::VK_PAGEOFF ||
ELFRefKind == AArch64MCExpr::VK_LO12 ||
ELFRefKind == AArch64MCExpr::VK_GOT_LO12 ||
ELFRefKind == AArch64MCExpr::VK_GOT_AUTH_LO12 ||
ELFRefKind == AArch64MCExpr::VK_DTPREL_LO12 ||
ELFRefKind == AArch64MCExpr::VK_DTPREL_LO12_NC ||
ELFRefKind == AArch64MCExpr::VK_TPREL_LO12 ||
Expand Down Expand Up @@ -986,19 +987,20 @@ class AArch64Operand : public MCParsedAsmOperand {
int64_t Addend;
if (AArch64AsmParser::classifySymbolRef(Expr, ELFRefKind,
DarwinRefKind, Addend)) {
return DarwinRefKind == MCSymbolRefExpr::VK_PAGEOFF
|| DarwinRefKind == MCSymbolRefExpr::VK_TLVPPAGEOFF
|| (DarwinRefKind == MCSymbolRefExpr::VK_GOTPAGEOFF && Addend == 0)
|| ELFRefKind == AArch64MCExpr::VK_LO12
|| ELFRefKind == AArch64MCExpr::VK_DTPREL_HI12
|| ELFRefKind == AArch64MCExpr::VK_DTPREL_LO12
|| ELFRefKind == AArch64MCExpr::VK_DTPREL_LO12_NC
|| ELFRefKind == AArch64MCExpr::VK_TPREL_HI12
|| ELFRefKind == AArch64MCExpr::VK_TPREL_LO12
|| ELFRefKind == AArch64MCExpr::VK_TPREL_LO12_NC
|| ELFRefKind == AArch64MCExpr::VK_TLSDESC_LO12
|| ELFRefKind == AArch64MCExpr::VK_SECREL_HI12
|| ELFRefKind == AArch64MCExpr::VK_SECREL_LO12;
return DarwinRefKind == MCSymbolRefExpr::VK_PAGEOFF ||
DarwinRefKind == MCSymbolRefExpr::VK_TLVPPAGEOFF ||
(DarwinRefKind == MCSymbolRefExpr::VK_GOTPAGEOFF && Addend == 0) ||
ELFRefKind == AArch64MCExpr::VK_LO12 ||
ELFRefKind == AArch64MCExpr::VK_GOT_AUTH_LO12 ||
ELFRefKind == AArch64MCExpr::VK_DTPREL_HI12 ||
ELFRefKind == AArch64MCExpr::VK_DTPREL_LO12 ||
ELFRefKind == AArch64MCExpr::VK_DTPREL_LO12_NC ||
ELFRefKind == AArch64MCExpr::VK_TPREL_HI12 ||
ELFRefKind == AArch64MCExpr::VK_TPREL_LO12 ||
ELFRefKind == AArch64MCExpr::VK_TPREL_LO12_NC ||
ELFRefKind == AArch64MCExpr::VK_TLSDESC_LO12 ||
ELFRefKind == AArch64MCExpr::VK_SECREL_HI12 ||
ELFRefKind == AArch64MCExpr::VK_SECREL_LO12;
}

// If it's a constant, it should be a real immediate in range.
Expand Down Expand Up @@ -3250,6 +3252,7 @@ ParseStatus AArch64AsmParser::tryParseAdrpLabel(OperandVector &Operands) {
DarwinRefKind != MCSymbolRefExpr::VK_TLVPPAGE &&
ELFRefKind != AArch64MCExpr::VK_ABS_PAGE_NC &&
ELFRefKind != AArch64MCExpr::VK_GOT_PAGE &&
ELFRefKind != AArch64MCExpr::VK_GOT_AUTH_PAGE &&
ELFRefKind != AArch64MCExpr::VK_GOT_PAGE_LO15 &&
ELFRefKind != AArch64MCExpr::VK_GOTTPREL_PAGE &&
ELFRefKind != AArch64MCExpr::VK_TLSDESC_PAGE) {
Expand Down Expand Up @@ -4334,6 +4337,8 @@ bool AArch64AsmParser::parseSymbolicImmVal(const MCExpr *&ImmVal) {
.Case("got", AArch64MCExpr::VK_GOT_PAGE)
.Case("gotpage_lo15", AArch64MCExpr::VK_GOT_PAGE_LO15)
.Case("got_lo12", AArch64MCExpr::VK_GOT_LO12)
.Case("got_auth", AArch64MCExpr::VK_GOT_AUTH_PAGE)
.Case("got_auth_lo12", AArch64MCExpr::VK_GOT_AUTH_LO12)
.Case("gottprel", AArch64MCExpr::VK_GOTTPREL_PAGE)
.Case("gottprel_lo12", AArch64MCExpr::VK_GOTTPREL_LO12_NC)
.Case("gottprel_g1", AArch64MCExpr::VK_GOTTPREL_G1)
Expand Down Expand Up @@ -5708,6 +5713,7 @@ bool AArch64AsmParser::validateInstruction(MCInst &Inst, SMLoc &IDLoc,

// Only allow these with ADDXri/ADDWri
if ((ELFRefKind == AArch64MCExpr::VK_LO12 ||
ELFRefKind == AArch64MCExpr::VK_GOT_AUTH_LO12 ||
ELFRefKind == AArch64MCExpr::VK_DTPREL_HI12 ||
ELFRefKind == AArch64MCExpr::VK_DTPREL_LO12 ||
ELFRefKind == AArch64MCExpr::VK_DTPREL_LO12_NC ||
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2845,7 +2845,9 @@ bool AArch64InstructionSelector::select(MachineInstr &I) {
}

if (OpFlags & AArch64II::MO_GOT) {
I.setDesc(TII.get(AArch64::LOADgot));
I.setDesc(TII.get(MF.getInfo<AArch64FunctionInfo>()->hasELFSignedGOT()
? AArch64::LOADgotAUTH
: AArch64::LOADgot));
I.getOperand(1).setTargetFlags(OpFlags);
} else if (TM.getCodeModel() == CodeModel::Large &&
!TM.isPositionIndependent()) {
Expand Down
34 changes: 29 additions & 5 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ unsigned AArch64ELFObjectWriter::getRelocType(MCContext &Ctx,
}
if (SymLoc == AArch64MCExpr::VK_GOT && !IsNC)
return R_CLS(ADR_GOT_PAGE);
if (SymLoc == AArch64MCExpr::VK_GOT_AUTH && !IsNC) {
if (IsILP32) {
Ctx.reportError(Fixup.getLoc(),
"ILP32 ADRP AUTH relocation not supported "
"(LP64 eqv: AUTH_ADR_GOT_PAGE)");
return ELF::R_AARCH64_NONE;
}
return ELF::R_AARCH64_AUTH_ADR_GOT_PAGE;
}
if (SymLoc == AArch64MCExpr::VK_GOTTPREL && !IsNC)
return R_CLS(TLSIE_ADR_GOTTPREL_PAGE21);
if (SymLoc == AArch64MCExpr::VK_TLSDESC && !IsNC)
Expand Down Expand Up @@ -237,6 +246,15 @@ unsigned AArch64ELFObjectWriter::getRelocType(MCContext &Ctx,
return R_CLS(TLSLE_ADD_TPREL_LO12);
if (RefKind == AArch64MCExpr::VK_TLSDESC_LO12)
return R_CLS(TLSDESC_ADD_LO12);
if (RefKind == AArch64MCExpr::VK_GOT_AUTH_LO12 && IsNC) {
if (IsILP32) {
Ctx.reportError(Fixup.getLoc(),
"ILP32 ADD AUTH relocation not supported "
"(LP64 eqv: AUTH_GOT_ADD_LO12_NC)");
return ELF::R_AARCH64_NONE;
}
return ELF::R_AARCH64_AUTH_GOT_ADD_LO12_NC;
}
if (SymLoc == AArch64MCExpr::VK_ABS && IsNC)
return R_CLS(ADD_ABS_LO12_NC);

Expand Down Expand Up @@ -329,17 +347,23 @@ unsigned AArch64ELFObjectWriter::getRelocType(MCContext &Ctx,
case AArch64::fixup_aarch64_ldst_imm12_scale8:
if (SymLoc == AArch64MCExpr::VK_ABS && IsNC)
return R_CLS(LDST64_ABS_LO12_NC);
if (SymLoc == AArch64MCExpr::VK_GOT && IsNC) {
if ((SymLoc == AArch64MCExpr::VK_GOT ||
SymLoc == AArch64MCExpr::VK_GOT_AUTH) &&
IsNC) {
AArch64MCExpr::VariantKind AddressLoc =
AArch64MCExpr::getAddressFrag(RefKind);
bool IsAuth = (SymLoc == AArch64MCExpr::VK_GOT_AUTH);
if (!IsILP32) {
if (AddressLoc == AArch64MCExpr::VK_LO15)
return ELF::R_AARCH64_LD64_GOTPAGE_LO15;
return ELF::R_AARCH64_LD64_GOT_LO12_NC;
return (IsAuth ? ELF::R_AARCH64_AUTH_LD64_GOT_LO12_NC
: ELF::R_AARCH64_LD64_GOT_LO12_NC);
}
Ctx.reportError(Fixup.getLoc(), "ILP32 64-bit load/store "
"relocation not supported (LP64 eqv: "
"LD64_GOT_LO12_NC)");
Ctx.reportError(Fixup.getLoc(),
Twine("ILP32 64-bit load/store "
"relocation not supported (LP64 eqv: ") +
(IsAuth ? "AUTH_GOT_LO12_NC" : "LD64_GOT_LO12_NC") +
Twine(')'));
return ELF::R_AARCH64_NONE;
}
if (SymLoc == AArch64MCExpr::VK_DTPREL && !IsNC)
Expand Down
Loading