Skip to content

Commit f5d935c

Browse files
committed
[WinCFG] Handle constant casts carefully in .gfids emission
Summary: The general Function::hasAddressTaken has two issues that make it inappropriate for our purposes: 1. it is sensitive to dead constant users (PR43858 / crbug.com/1019970), leading to different codegen when debu info is enabled 2. it considers direct calls via a function cast to be address escapes The first is fixable, but the second is not, because IPO clients rely on this behavior. They assume this function means that all call sites are analyzable for IPO purposes. So, implement our own analysis, which gets closer to finding functions that may be indirect call targets. Reviewers: ajpaverd, efriedma, hans Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D69676
1 parent 52ea308 commit f5d935c

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/CodeGen/MachineOperand.h"
1919
#include "llvm/IR/Constants.h"
2020
#include "llvm/IR/Metadata.h"
21+
#include "llvm/IR/Instructions.h"
2122
#include "llvm/MC/MCAsmInfo.h"
2223
#include "llvm/MC/MCObjectFileInfo.h"
2324
#include "llvm/MC/MCStreamer.h"
@@ -41,11 +42,47 @@ void WinCFGuard::endFunction(const MachineFunction *MF) {
4142
MF->getLongjmpTargets().end());
4243
}
4344

45+
/// Returns true if this function's address is escaped in a way that might make
46+
/// it an indirect call target. Function::hasAddressTaken gives different
47+
/// results when a function is called directly with a function prototype
48+
/// mismatch, which requires a cast.
49+
static bool isPossibleIndirectCallTarget(const Function *F) {
50+
SmallVector<const Value *, 4> Users{F};
51+
while (!Users.empty()) {
52+
const Value *FnOrCast = Users.pop_back_val();
53+
for (const Use &U : FnOrCast->uses()) {
54+
const User *FnUser = U.getUser();
55+
if (isa<BlockAddress>(FnUser))
56+
continue;
57+
if (const auto *Call = dyn_cast<CallBase>(FnUser)) {
58+
if (!Call->isCallee(&U))
59+
return true;
60+
} else if (isa<Instruction>(FnUser)) {
61+
// Consider any other instruction to be an escape. This has some weird
62+
// consequences like no-op intrinsics being an escape or a store *to* a
63+
// function address being an escape.
64+
return true;
65+
} else if (const auto *C = dyn_cast<Constant>(FnUser)) {
66+
// If this is a constant pointer cast of the function, don't consider
67+
// this escape. Analyze the uses of the cast as well. This ensures that
68+
// direct calls with mismatched prototypes don't end up in the CFG
69+
// table. Consider other constants, such as vtable initializers, to
70+
// escape the function.
71+
if (C->stripPointerCasts() == F)
72+
Users.push_back(FnUser);
73+
else
74+
return true;
75+
}
76+
}
77+
}
78+
return false;
79+
}
80+
4481
void WinCFGuard::endModule() {
4582
const Module *M = Asm->MMI->getModule();
4683
std::vector<const Function *> Functions;
4784
for (const Function &F : *M)
48-
if (F.hasAddressTaken())
85+
if (isPossibleIndirectCallTarget(&F))
4986
Functions.push_back(&F);
5087
if (Functions.empty() && LongjmpTargets.empty())
5188
return;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s
2+
3+
; Check how constant function pointer casts are handled.
4+
5+
declare void @unprototyped(...)
6+
7+
define i32 @call_unprototyped() {
8+
call void bitcast (void (...)* @unprototyped to void ()*)()
9+
ret i32 0
10+
}
11+
12+
; CHECK-LABEL: call_unprototyped:
13+
; CHECK: callq unprototyped
14+
; CHECK: xorl %eax, %eax
15+
; CHECK: retq
16+
17+
declare void @escaped_cast()
18+
19+
define i32 @escape_it_with_cast(i8** %p) {
20+
store i8* bitcast (void ()* @escaped_cast to i8*), i8** %p
21+
ret i32 0
22+
}
23+
24+
declare void @dead_constant()
25+
26+
!llvm.module.flags = !{!0}
27+
!0 = !{i32 2, !"cfguard", i32 1}
28+
29+
!dead_constant_root = !{!1}
30+
!1 = !DITemplateValueParameter(name: "dead_constant", value: i8* bitcast (void ()* @dead_constant to i8*))
31+
32+
; CHECK-LABEL: .section .gfids$y,"dr"
33+
; CHECK-NEXT: .symidx escaped_cast
34+
; CHECK-NOT: .symidx
35+

0 commit comments

Comments
 (0)