Skip to content

Commit 7e28ecb

Browse files
author
git apple-llvm automerger
committed
Merge commit 'f5d935c16777' from llvm.org/master into apple/master
2 parents 01bc501 + f5d935c commit 7e28ecb

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)