|
22 | 22 | #include "llvm/CodeGen/TargetLowering.h" |
23 | 23 | #include "llvm/CodeGen/TargetPassConfig.h" |
24 | 24 | #include "llvm/IR/Function.h" |
| 25 | +#include "llvm/IR/GlobalValue.h" |
25 | 26 | #include "llvm/IR/IRBuilder.h" |
26 | 27 | #include "llvm/IR/Instructions.h" |
27 | 28 | #include "llvm/IR/IntrinsicInst.h" |
| 29 | +#include "llvm/IR/Metadata.h" |
28 | 30 | #include "llvm/IR/Module.h" |
29 | 31 | #include "llvm/IR/RuntimeLibcalls.h" |
30 | 32 | #include "llvm/IR/Type.h" |
@@ -471,6 +473,144 @@ bool PreISelIntrinsicLowering::expandMemIntrinsicUses( |
471 | 473 | return Changed; |
472 | 474 | } |
473 | 475 |
|
| 476 | +static bool expandProtectedFieldPtr(Function &Intr) { |
| 477 | + Module &M = *Intr.getParent(); |
| 478 | + |
| 479 | + SmallPtrSet<GlobalValue *, 2> DSsToDeactivate; |
| 480 | + |
| 481 | + Type *Int8Ty = Type::getInt8Ty(M.getContext()); |
| 482 | + Type *Int64Ty = Type::getInt64Ty(M.getContext()); |
| 483 | + PointerType *PtrTy = PointerType::get(M.getContext(), 0); |
| 484 | + |
| 485 | + Function *SignIntr = |
| 486 | + Intrinsic::getOrInsertDeclaration(&M, Intrinsic::ptrauth_sign, {}); |
| 487 | + Function *AuthIntr = |
| 488 | + Intrinsic::getOrInsertDeclaration(&M, Intrinsic::ptrauth_auth, {}); |
| 489 | + |
| 490 | + auto *EmuFnTy = FunctionType::get(Int64Ty, {Int64Ty, Int64Ty}, false); |
| 491 | + |
| 492 | + auto CreateSign = [&](IRBuilder<> &B, Value *Val, Value *Disc, |
| 493 | + OperandBundleDef DSBundle) { |
| 494 | + Function *F = B.GetInsertBlock()->getParent(); |
| 495 | + Attribute FSAttr = F->getFnAttribute("target-features"); |
| 496 | + if (FSAttr.isValid() && FSAttr.getValueAsString().contains("+pauth")) |
| 497 | + return B.CreateCall( |
| 498 | + SignIntr, {Val, B.getInt32(/*AArch64PACKey::DA*/ 2), Disc}, DSBundle); |
| 499 | + FunctionCallee EmuSignIntr = |
| 500 | + M.getOrInsertFunction("__emupac_pacda", EmuFnTy); |
| 501 | + return B.CreateCall(EmuSignIntr, {Val, Disc}, DSBundle); |
| 502 | + }; |
| 503 | + |
| 504 | + auto CreateAuth = [&](IRBuilder<> &B, Value *Val, Value *Disc, |
| 505 | + OperandBundleDef DSBundle) { |
| 506 | + Function *F = B.GetInsertBlock()->getParent(); |
| 507 | + Attribute FSAttr = F->getFnAttribute("target-features"); |
| 508 | + if (FSAttr.isValid() && FSAttr.getValueAsString().contains("+pauth")) |
| 509 | + return B.CreateCall( |
| 510 | + AuthIntr, {Val, B.getInt32(/*AArch64PACKey::DA*/ 2), Disc}, DSBundle); |
| 511 | + FunctionCallee EmuAuthIntr = |
| 512 | + M.getOrInsertFunction("__emupac_autda", EmuFnTy); |
| 513 | + return B.CreateCall(EmuAuthIntr, {Val, Disc}, DSBundle); |
| 514 | + }; |
| 515 | + |
| 516 | + auto GetDeactivationSymbol = [&](CallInst *Call) -> GlobalValue * { |
| 517 | + if (auto Bundle = |
| 518 | + Call->getOperandBundle(LLVMContext::OB_deactivation_symbol)) |
| 519 | + return cast<GlobalValue>(Bundle->Inputs[0]); |
| 520 | + return nullptr; |
| 521 | + }; |
| 522 | + |
| 523 | + for (User *U : llvm::make_early_inc_range(Intr.users())) { |
| 524 | + auto *Call = cast<CallInst>(U); |
| 525 | + |
| 526 | + auto *Pointer = Call->getArgOperand(0); |
| 527 | + auto *Disc = Call->getArgOperand(1); |
| 528 | + bool UseHWEncoding = |
| 529 | + cast<ConstantInt>(Call->getArgOperand(2))->getZExtValue(); |
| 530 | + if (!UseHWEncoding) |
| 531 | + reportFatalUsageError("software encoding currently unsupported"); |
| 532 | + |
| 533 | + auto *DS = GetDeactivationSymbol(Call); |
| 534 | + OperandBundleDef DSBundle("deactivation-symbol", DS); |
| 535 | + |
| 536 | + for (Use &U : llvm::make_early_inc_range(Call->uses())) { |
| 537 | + // Insert code to encode each pointer stored to the pointer returned by |
| 538 | + // the intrinsic. |
| 539 | + if (auto *SI = dyn_cast<StoreInst>(U.getUser())) { |
| 540 | + if (U.getOperandNo() == 1 && |
| 541 | + isa<PointerType>(SI->getValueOperand()->getType())) { |
| 542 | + IRBuilder<> B(SI); |
| 543 | + auto *SIValInt = |
| 544 | + B.CreatePtrToInt(SI->getValueOperand(), B.getInt64Ty()); |
| 545 | + Value *Sign = CreateSign(B, SIValInt, Disc, DSBundle); |
| 546 | + SI->setOperand(0, B.CreateIntToPtr(Sign, B.getPtrTy())); |
| 547 | + SI->setOperand(1, Pointer); |
| 548 | + continue; |
| 549 | + } |
| 550 | + } |
| 551 | + |
| 552 | + // Insert code to decode each pointer loaded from the pointer returned by |
| 553 | + // the intrinsic. This is the inverse of the encode operation implemented |
| 554 | + // above. |
| 555 | + if (auto *LI = dyn_cast<LoadInst>(U.getUser())) { |
| 556 | + if (isa<PointerType>(LI->getType())) { |
| 557 | + IRBuilder<> B(LI); |
| 558 | + auto *NewLI = cast<LoadInst>(LI->clone()); |
| 559 | + NewLI->setOperand(0, Pointer); |
| 560 | + B.Insert(NewLI); |
| 561 | + auto *LIInt = B.CreatePtrToInt(NewLI, B.getInt64Ty()); |
| 562 | + Value *Auth = CreateAuth(B, LIInt, Disc, DSBundle); |
| 563 | + LI->replaceAllUsesWith(B.CreateIntToPtr(Auth, B.getPtrTy())); |
| 564 | + LI->eraseFromParent(); |
| 565 | + continue; |
| 566 | + } |
| 567 | + } |
| 568 | + // Comparisons against null cannot be used to recover the original |
| 569 | + // pointer so we replace them with comparisons against the original |
| 570 | + // pointer. |
| 571 | + if (auto *CI = dyn_cast<ICmpInst>(U.getUser())) { |
| 572 | + if (auto *Op = dyn_cast<Constant>(CI->getOperand(0))) { |
| 573 | + if (Op->isNullValue()) { |
| 574 | + CI->setOperand(1, Pointer); |
| 575 | + continue; |
| 576 | + } |
| 577 | + } |
| 578 | + if (auto *Op = dyn_cast<Constant>(CI->getOperand(1))) { |
| 579 | + if (Op->isNullValue()) { |
| 580 | + CI->setOperand(0, Pointer); |
| 581 | + continue; |
| 582 | + } |
| 583 | + } |
| 584 | + } |
| 585 | + |
| 586 | + // We couldn't rewrite away this use of the intrinsic. Replace it with the |
| 587 | + // pointer operand, and arrange to define a deactivation symbol. |
| 588 | + U.set(Pointer); |
| 589 | + if (DS) |
| 590 | + DSsToDeactivate.insert(DS); |
| 591 | + } |
| 592 | + |
| 593 | + Call->eraseFromParent(); |
| 594 | + } |
| 595 | + |
| 596 | + if (!DSsToDeactivate.empty()) { |
| 597 | + // This is an AArch64 NOP instruction. When the deactivation symbol support |
| 598 | + // is expanded to more architectures, there will likely need to be an API |
| 599 | + // for retrieving this constant. |
| 600 | + Constant *Nop = |
| 601 | + ConstantExpr::getIntToPtr(ConstantInt::get(Int64Ty, 0xd503201f), PtrTy); |
| 602 | + for (GlobalValue *OldDS : DSsToDeactivate) { |
| 603 | + GlobalValue *DS = GlobalAlias::create( |
| 604 | + Int8Ty, 0, GlobalValue::ExternalLinkage, OldDS->getName(), Nop, &M); |
| 605 | + DS->setVisibility(GlobalValue::HiddenVisibility); |
| 606 | + DS->takeName(OldDS); |
| 607 | + OldDS->replaceAllUsesWith(DS); |
| 608 | + OldDS->eraseFromParent(); |
| 609 | + } |
| 610 | + } |
| 611 | + return true; |
| 612 | +} |
| 613 | + |
474 | 614 | bool PreISelIntrinsicLowering::lowerIntrinsics(Module &M) const { |
475 | 615 | // Map unique constants to globals. |
476 | 616 | DenseMap<Constant *, GlobalVariable *> CMap; |
@@ -613,6 +753,9 @@ bool PreISelIntrinsicLowering::lowerIntrinsics(Module &M) const { |
613 | 753 | return lowerUnaryVectorIntrinsicAsLoop(M, CI); |
614 | 754 | }); |
615 | 755 | break; |
| 756 | + case Intrinsic::protected_field_ptr: |
| 757 | + Changed |= expandProtectedFieldPtr(F); |
| 758 | + break; |
616 | 759 | } |
617 | 760 | } |
618 | 761 | return Changed; |
|
0 commit comments