Navidem updated this revision to Diff 460215.
Navidem added a comment.
Update the rt test
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D133157/new/
https://reviews.llvm.org/D133157
Files:
clang/docs/SanitizerCoverage.rst
clang/include/clang/Basic/CodeGenOptions.def
clang/include/clang/Basic/CodeGenOptions.h
clang/include/clang/Driver/Options.td
clang/lib/CodeGen/BackendUtil.cpp
clang/lib/Driver/SanitizerArgs.cpp
compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_control_flow.cpp
llvm/include/llvm/Transforms/Instrumentation.h
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
llvm/test/Instrumentation/SanitizerCoverage/control-flow.ll
Index: llvm/test/Instrumentation/SanitizerCoverage/control-flow.ll
===================================================================
--- /dev/null
+++ llvm/test/Instrumentation/SanitizerCoverage/control-flow.ll
@@ -0,0 +1,22 @@
+; Test -sanitizer-coverage-control-flow.
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=3 -sanitizer-coverage-control-flow -S | FileCheck %s
+
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
+target triple = "x86_64-unknown-linux-gnu"
+define void @foo(ptr %a) sanitize_address {
+entry:
+ %tobool = icmp eq i32* %a, null
+ br i1 %tobool, label %if.end, label %if.then
+
+ if.then: ; preds = %entry
+ store i32 0, i32* %a, align 4
+ call void @foo(i32* %a)
+ br label %if.end
+
+ if.end: ; preds = %entry, %if.then
+ ret void
+}
+
+; CHECK: private constant [17 x ptr] [{{.*}}@foo{{.*}}blockaddress{{.*}}blockaddress{{.*}}blockaddress{{.*}}blockaddress{{.*}}blockaddress{{.*}}blockaddress{{.*}}blockaddress{{.*}}@foo{{.*}}null{{.*}}null], section "__sancov_cfs", comdat($foo), align 8
+; CHECK: @__start___sancov_cfs = extern_weak hidden global
+; CHECK-NEXT: @__stop___sancov_cfs = extern_weak hidden global
Index: llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
===================================================================
--- llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
+++ llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
@@ -75,11 +75,13 @@
const char SanCov8bitCountersInitName[] = "__sanitizer_cov_8bit_counters_init";
const char SanCovBoolFlagInitName[] = "__sanitizer_cov_bool_flag_init";
const char SanCovPCsInitName[] = "__sanitizer_cov_pcs_init";
+const char SanCovCFsInitName[] = "__sanitizer_cov_cfs_init";
const char SanCovGuardsSectionName[] = "sancov_guards";
const char SanCovCountersSectionName[] = "sancov_cntrs";
const char SanCovBoolFlagSectionName[] = "sancov_bools";
const char SanCovPCsSectionName[] = "sancov_pcs";
+const char SanCovCFsSectionName[] = "sancov_cfs";
const char SanCovLowestStackName[] = "__sancov_lowest_stack";
@@ -147,6 +149,10 @@
cl::desc("max stack depth tracing"),
cl::Hidden, cl::init(false));
+static cl::opt<bool> ClCollectCF("sanitizer-coverage-control-flow",
+ cl::desc("collect control flow for each function"),
+ cl::Hidden, cl::init(false));
+
namespace {
SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) {
@@ -193,6 +199,7 @@
!Options.Inline8bitCounters && !Options.StackDepth &&
!Options.InlineBoolFlag && !Options.TraceLoads && !Options.TraceStores)
Options.TracePCGuard = true; // TracePCGuard is default.
+ Options.CollectControlFlow |= ClCollectCF;
return Options;
}
@@ -212,6 +219,7 @@
PostDomTreeCallback PDTCallback);
private:
+ void createFunctionControlFlow(Function &F);
void instrumentFunction(Function &F, DomTreeCallback DTCallback,
PostDomTreeCallback PDTCallback);
void InjectCoverageForIndirectCalls(Function &F,
@@ -270,6 +278,7 @@
GlobalVariable *Function8bitCounterArray; // for inline-8bit-counters.
GlobalVariable *FunctionBoolArray; // for inline-bool-flag.
GlobalVariable *FunctionPCsArray; // for pc-table.
+ GlobalVariable *FunctionCFsArray; // for control flow table
SmallVector<GlobalValue *, 20> GlobalsToAppendToUsed;
SmallVector<GlobalValue *, 20> GlobalsToAppendToCompilerUsed;
@@ -378,6 +387,7 @@
Function8bitCounterArray = nullptr;
FunctionBoolArray = nullptr;
FunctionPCsArray = nullptr;
+ FunctionCFsArray = nullptr;
IntptrTy = Type::getIntNTy(*C, DL->getPointerSizeInBits());
IntptrPtrTy = PointerType::getUnqual(IntptrTy);
Type *VoidTy = Type::getVoidTy(*C);
@@ -502,6 +512,15 @@
IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator());
IRBCtor.CreateCall(InitFunction, {SecStartEnd.first, SecStartEnd.second});
}
+
+ if (Ctor && Options.CollectControlFlow) {
+ auto SecStartEnd = CreateSecStartEnd(M, SanCovCFsSectionName, IntptrPtrTy);
+ FunctionCallee InitFunction = declareSanitizerInitFunction(
+ M, SanCovCFsInitName, {IntptrPtrTy, IntptrPtrTy});
+ IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator());
+ IRBCtor.CreateCall(InitFunction, {SecStartEnd.first, SecStartEnd.second});
+ }
+
appendToUsed(M, GlobalsToAppendToUsed);
appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed);
return true;
@@ -671,6 +690,9 @@
}
}
+ if (Options.CollectControlFlow)
+ createFunctionControlFlow(F);
+
InjectCoverage(F, BlocksToInstrument, IsLeafFunc);
InjectCoverageForIndirectCalls(F, IndirCalls);
InjectTraceForCmp(F, CmpTraceTargets);
@@ -1028,3 +1050,43 @@
return "\1section$end$__DATA$__" + Section;
return "__stop___" + Section;
}
+
+void ModuleSanitizerCoverage::createFunctionControlFlow(Function &F) {
+ SmallVector<Constant *, 32> CFs;
+ IRBuilder<> IRB(&*F.getEntryBlock().getFirstInsertionPt());
+
+ for (auto &BB: F) {
+ // blockaddress can not be used on function's entry block.
+ if (&BB == &F.getEntryBlock())
+ CFs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy));
+ else
+ CFs.push_back((Constant *)IRB.CreatePointerCast(BlockAddress::get(&BB), IntptrPtrTy));
+
+ for (auto SuccBB : successors(&BB)) {
+ assert(SuccBB != &F.getEntryBlock());
+ CFs.push_back((Constant *)IRB.CreatePointerCast(BlockAddress::get(SuccBB), IntptrPtrTy));
+ }
+
+ CFs.push_back((Constant *)Constant::getNullValue(IntptrPtrTy));
+
+ for (auto &Inst: BB) {
+ if (CallBase *CB = dyn_cast<CallBase>(&Inst)) {
+ if (CB->isIndirectCall()) {
+ // TODO(navidem): handle indirect calls, for now mark its existence.
+ CFs.push_back((Constant *)IRB.CreateIntToPtr(ConstantInt::get(IntptrTy, -1), IntptrPtrTy));
+ }
+ else {
+ auto CalledF = CB->getCalledFunction();
+ if (CalledF && !CalledF->isIntrinsic())
+ CFs.push_back((Constant *)IRB.CreatePointerCast(CalledF, IntptrPtrTy));
+ }
+ }
+ }
+
+ CFs.push_back((Constant *)Constant::getNullValue(IntptrPtrTy));
+ }
+
+ FunctionCFsArray = CreateFunctionLocalArrayInSection(CFs.size(), F, IntptrPtrTy, SanCovCFsSectionName);
+ FunctionCFsArray->setInitializer(ConstantArray::get(ArrayType::get(IntptrPtrTy, CFs.size()), CFs));
+ FunctionCFsArray->setConstant(true);
+}
Index: llvm/include/llvm/Transforms/Instrumentation.h
===================================================================
--- llvm/include/llvm/Transforms/Instrumentation.h
+++ llvm/include/llvm/Transforms/Instrumentation.h
@@ -150,6 +150,7 @@
bool StackDepth = false;
bool TraceLoads = false;
bool TraceStores = false;
+ bool CollectControlFlow = false;
SanitizerCoverageOptions() = default;
};
Index: compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_control_flow.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_control_flow.cpp
@@ -0,0 +1,63 @@
+// Tests -fsanitize-coverage=control-flow.
+
+// REQUIRES: has_sancovcc,stable-runtime
+// UNSUPPORTED: i386-darwin, x86_64-darwin
+
+// RUN: %clangxx -O0 -std=c++11 -fsanitize-coverage=control-flow %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include <cstdint>
+#include <cstdio>
+
+uintptr_t *CFS_BEG, *CFS_END;
+
+extern "C" void __sanitizer_cov_cfs_init(const uintptr_t *cfs_beg,
+ const uintptr_t *cfs_end) {
+ CFS_BEG = (uintptr_t *)cfs_beg;
+ CFS_END = (uintptr_t *)cfs_end;
+}
+
+__attribute__((noinline)) void foo(int x) { /* empty body */
+}
+
+int main() {
+ int (*main_ptr)() = &main;
+ void (*foo_ptr)(int) = &foo;
+ int x = 10;
+
+ if (x > 0)
+ foo(x);
+ else
+ (*foo_ptr)(x);
+
+ printf("Control Flow section boundaries: [%p %p)\n", CFS_BEG, CFS_END);
+ uintptr_t *pt = CFS_BEG;
+ uintptr_t currBB;
+
+ while (pt < CFS_END) {
+ currBB = *pt;
+ pt++;
+ // Iterate over successors.
+ while (*pt) {
+ pt++;
+ }
+ pt++;
+ // Iterate over callees.
+ while (*pt) {
+ if (*pt == (uintptr_t)(*foo_ptr) && currBB != (uintptr_t)(*main_ptr))
+ printf("Direct call matched.\n");
+ if (*pt == -1 && currBB != (uintptr_t)(*main_ptr))
+ printf("Indirect call matched.\n");
+ pt++;
+ }
+ pt++;
+ }
+
+ printf("Finished!\n");
+ return 0;
+}
+
+// CHECK: Control Flow section boundaries
+// CHECK: Direct call matched.
+// CHECK: Indirect call matched.
+// CHECK: Finished!
\ No newline at end of file
Index: clang/lib/Driver/SanitizerArgs.cpp
===================================================================
--- clang/lib/Driver/SanitizerArgs.cpp
+++ clang/lib/Driver/SanitizerArgs.cpp
@@ -98,6 +98,7 @@
CoverageInlineBoolFlag = 1 << 15,
CoverageTraceLoads = 1 << 16,
CoverageTraceStores = 1 << 17,
+ CoverageControlFlow = 1 << 18,
};
/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
@@ -798,19 +799,19 @@
int InsertionPointTypes = CoverageFunc | CoverageBB | CoverageEdge;
int InstrumentationTypes = CoverageTracePC | CoverageTracePCGuard |
CoverageInline8bitCounters | CoverageTraceLoads |
- CoverageTraceStores | CoverageInlineBoolFlag;
+ CoverageTraceStores | CoverageInlineBoolFlag | CoverageControlFlow;
if ((CoverageFeatures & InsertionPointTypes) &&
!(CoverageFeatures & InstrumentationTypes) && DiagnoseErrors) {
D.Diag(clang::diag::warn_drv_deprecated_arg)
<< "-fsanitize-coverage=[func|bb|edge]"
- << "-fsanitize-coverage=[func|bb|edge],[trace-pc-guard|trace-pc]";
+ << "-fsanitize-coverage=[func|bb|edge],[trace-pc-guard|trace-pc],[control-flow]";
}
// trace-pc w/o func/bb/edge implies edge.
if (!(CoverageFeatures & InsertionPointTypes)) {
if (CoverageFeatures &
(CoverageTracePC | CoverageTracePCGuard | CoverageInline8bitCounters |
- CoverageInlineBoolFlag))
+ CoverageInlineBoolFlag | CoverageControlFlow))
CoverageFeatures |= CoverageEdge;
if (CoverageFeatures & CoverageStackDepth)
@@ -1085,7 +1086,8 @@
std::make_pair(CoverageNoPrune, "-fsanitize-coverage-no-prune"),
std::make_pair(CoverageStackDepth, "-fsanitize-coverage-stack-depth"),
std::make_pair(CoverageTraceLoads, "-fsanitize-coverage-trace-loads"),
- std::make_pair(CoverageTraceStores, "-fsanitize-coverage-trace-stores")};
+ std::make_pair(CoverageTraceStores, "-fsanitize-coverage-trace-stores"),
+ std::make_pair(CoverageControlFlow, "-fsanitize-coverage-control-flow")};
for (auto F : CoverageFlags) {
if (CoverageFeatures & F.first)
CmdArgs.push_back(F.second);
@@ -1332,6 +1334,7 @@
.Case("stack-depth", CoverageStackDepth)
.Case("trace-loads", CoverageTraceLoads)
.Case("trace-stores", CoverageTraceStores)
+ .Case("control-flow", CoverageControlFlow)
.Default(0);
if (F == 0 && DiagnoseErrors)
D.Diag(clang::diag::err_drv_unsupported_option_argument)
Index: clang/lib/CodeGen/BackendUtil.cpp
===================================================================
--- clang/lib/CodeGen/BackendUtil.cpp
+++ clang/lib/CodeGen/BackendUtil.cpp
@@ -215,6 +215,7 @@
Opts.StackDepth = CGOpts.SanitizeCoverageStackDepth;
Opts.TraceLoads = CGOpts.SanitizeCoverageTraceLoads;
Opts.TraceStores = CGOpts.SanitizeCoverageTraceStores;
+ Opts.CollectControlFlow = CGOpts.SanitizeCoverageControlFlow;
return Opts;
}
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -5472,6 +5472,10 @@
: Flag<["-"], "fsanitize-coverage-pc-table">,
HelpText<"Create a table of coverage-instrumented PCs">,
MarshallingInfoFlag<CodeGenOpts<"SanitizeCoveragePCTable">>;
+def fsanitize_coverage_control_flow
+ : Flag<["-"], "fsanitize-coverage-control-flow">,
+ HelpText<"Collect control flow of function">,
+ MarshallingInfoFlag<CodeGenOpts<"SanitizeCoverageControlFlow">>;
def fsanitize_coverage_trace_pc
: Flag<["-"], "fsanitize-coverage-trace-pc">,
HelpText<"Enable PC tracing in sanitizer coverage">,
Index: clang/include/clang/Basic/CodeGenOptions.h
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.h
+++ clang/include/clang/Basic/CodeGenOptions.h
@@ -483,7 +483,7 @@
bool hasSanitizeCoverage() const {
return SanitizeCoverageType || SanitizeCoverageIndirectCalls ||
SanitizeCoverageTraceCmp || SanitizeCoverageTraceLoads ||
- SanitizeCoverageTraceStores;
+ SanitizeCoverageTraceStores || SanitizeCoverageControlFlow;
}
};
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -281,6 +281,7 @@
CODEGENOPT(SanitizeCoverageInline8bitCounters, 1, 0) ///< Use inline 8bit counters.
CODEGENOPT(SanitizeCoverageInlineBoolFlag, 1, 0) ///< Use inline bool flag.
CODEGENOPT(SanitizeCoveragePCTable, 1, 0) ///< Create a PC Table.
+CODEGENOPT(SanitizeCoverageControlFlow, 1, 0) ///< Collect control flow
CODEGENOPT(SanitizeCoverageNoPrune, 1, 0) ///< Disable coverage pruning.
CODEGENOPT(SanitizeCoverageStackDepth, 1, 0) ///< Enable max stack depth tracing
CODEGENOPT(SanitizeCoverageTraceLoads, 1, 0) ///< Enable tracing of loads.
Index: clang/docs/SanitizerCoverage.rst
===================================================================
--- clang/docs/SanitizerCoverage.rst
+++ clang/docs/SanitizerCoverage.rst
@@ -331,6 +331,60 @@
void __sanitizer_cov_store8(uint64_t *addr);
void __sanitizer_cov_store16(__int128 *addr);
+
+Tracing control flow
+====================
+
+With ``-fsanitize-coverage=control-flow`` the compiler will create a table to collect
+control flow for each function. More specifically, for each basic block in the function,
+two lists are populated. One list for successors of the basic block and another list for
+non-intrinsic called functions.
+
+**TODO:** in the current implementation, indirect calls are not tracked
+and are only marked with special value (-1) in the list.
+
+Each table row consists of the basic block address
+followed by ``null``-ended lists of successors and callees.
+The table is encoded in a special section named ``sancov_cfs``
+
+Example:
+
+.. code-block:: c++
+ int foo (int x) {
+ if (x > 0)
+ bar(x);
+ else
+ x = 0;
+ return x;
+ }
+
+The code above contains 4 basic blocks, let's name them A, B, C, D:
+
+.. code-block:: none
+
+ A
+ |\
+ | \
+ B C
+ | /
+ |/
+ D
+
+The collected control flow table is as follows:
+``A, B, C, null, null, B, D, null, @bar, null, C, D, null, null, D, null, null.``
+
+Users need to implement a single function to capture the CF table at startup:
+
+.. code-block:: c++
+
+ extern "C"
+ void __sanitizer_cov_cfs_init(const uintptr_t *cfs_beg,
+ const uintptr_t *cfs_end) {
+ // [cfs_beg,cfs_end) is the array of ptr-sized integers representing
+ // the collected control flow.
+ }
+
+
Disabling instrumentation with ``__attribute__((no_sanitize("coverage")))``
===========================================================================
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits