Skip to content

Commit d540ebf

Browse files
[ARM64EC] Avoid emitting unnecessary symbol references with /guard:cf. (#123235)
.gfids$y contains a list of indirect calls for Control Flow Guard. This wasn't working properly for ARM64EC: direct calls were being treated as indirect calls. Make sure we correctly filter out direct calls. This improves the protection from Control Flow Guard, and also fixes a link error when using certain functions from oldnames.lib.
1 parent 7cf8add commit d540ebf

File tree

2 files changed

+34
-12
lines changed

2 files changed

+34
-12
lines changed

llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,32 @@ static bool isPossibleIndirectCallTarget(const Function *F) {
4949
const Value *FnOrCast = Users.pop_back_val();
5050
for (const Use &U : FnOrCast->uses()) {
5151
const User *FnUser = U.getUser();
52-
if (isa<BlockAddress>(FnUser))
52+
if (isa<BlockAddress>(FnUser)) {
53+
// Block addresses are illegal to call.
5354
continue;
55+
}
5456
if (const auto *Call = dyn_cast<CallBase>(FnUser)) {
55-
if (!Call->isCallee(&U))
57+
if ((!Call->isCallee(&U) || U.get() != F) &&
58+
!Call->getFunction()->getName().ends_with("$exit_thunk")) {
59+
// Passing a function pointer to a call may lead to an indirect
60+
// call. As an exception, ignore ARM64EC exit thunks.
5661
return true;
62+
}
5763
} else if (isa<Instruction>(FnUser)) {
5864
// Consider any other instruction to be an escape. This has some weird
5965
// consequences like no-op intrinsics being an escape or a store *to* a
6066
// function address being an escape.
6167
return true;
62-
} else if (const auto *C = dyn_cast<Constant>(FnUser)) {
63-
// If this is a constant pointer cast of the function, don't consider
64-
// this escape. Analyze the uses of the cast as well. This ensures that
65-
// direct calls with mismatched prototypes don't end up in the CFG
66-
// table. Consider other constants, such as vtable initializers, to
67-
// escape the function.
68-
if (C->stripPointerCasts() == F)
69-
Users.push_back(FnUser);
70-
else
71-
return true;
68+
} else if (const auto *G = dyn_cast<GlobalValue>(FnUser)) {
69+
// Ignore llvm.arm64ec.symbolmap; it doesn't lower to an actual address.
70+
if (G->getName() == "llvm.arm64ec.symbolmap")
71+
continue;
72+
// Globals (for example, vtables) are escapes.
73+
return true;
74+
} else if (isa<Constant>(FnUser)) {
75+
// Constants which aren't a global are intermediate values; recursively
76+
// analyze the users to see if they actually escape.
77+
Users.push_back(FnUser);
7278
}
7379
}
7480
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
; RUN: llc < %s -mtriple=arm64ec-pc-windows-msvc | FileCheck %s
2+
3+
declare void @called()
4+
declare void @escaped()
5+
define void @f(ptr %dst) {
6+
call void @called()
7+
store ptr @escaped, ptr %dst
8+
ret void
9+
}
10+
11+
!llvm.module.flags = !{!0}
12+
!0 = !{i32 2, !"cfguard", i32 1}
13+
14+
; CHECK-LABEL: .section .gfids$y,"dr"
15+
; CHECK-NEXT: .symidx escaped
16+
; CHECK-NOT: .symidx

0 commit comments

Comments
 (0)