这分析得不错
执行前端任务前,读取源文件时会构建CodeGenModule
类(生成跨函数的状态方便后续IR生成),进而创建并初始化SanitizerMetadata
(生成元数据供后续Pass使用)。
// clang/lib/Frontend/CompilerInstance.cpp
bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
...
if (Act.BeginSourceFile(*this, FIF)) {
if (llvm::Error Err = Act.Execute()) {...}
}
...
}
然后在处理转译单元前,会处理顶级声明。CodeGenModule
解析时,每当遇到一个字符串文本/全局变量,就交由SanitizerMetadata
插入到名为"llvm.asan.globals"
的元数据中。
// clang/lib/Parse/ParseAST.cpp
void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
...
Consumer->HandleTopLevelDecl(ADecl.get());
...
Consumer->HandleTranslationUnit(S.getASTContext());
...
}
ASanGlobalsMetadataWrapperPass
这在《ASAN Pass【源码分析】(三)》就已经讲述
ASanGlobalsMetadataWrapperPass::runOnModule
时初始化生成全局元数据GlobalsMetadata
供ASAN后续使用。具体说来,它会从"llvm.asan.globals"
提取步骤b存好的信息(如包括"
、待编译文件的全局变量、"
)。
AddressSanitizer
出动AddressSanitizerLegacyPass::runOnFunction
首先获取ASanGlobalsMetadataWrapperPass
准备好的全局元数据GlobalsMetadata
,然后将其交给刚初始化的AddressSanitizer
进行具体插桩。
AddressSanitizer
初始化时会保存当前函数所在模块的上下文信息,查询当前平台架构和位数,然后设置ShadowMap,包括offset。
Mapping.Offset = (kSmallX86_64ShadowOffsetBase & (kSmallX86_64ShadowOffsetAlignMask << Mapping.Scale));
首先确保函数不是外部定义的,不是asan-debug-func
,不是__asan_
开头的ASAN Runtime函数。
(通过getOrInsertFunction
函数)初始化一些回调函数。包括__asan_report_{exp_,}{load,store}{_n,N,2^n}{_noabort,}
、__asan_{exp_,}{load,store}{_n,N,2^n}{_noabort,}
、{__asan_,}memmove
、{__asan_,}memcpy
、{__asan_,}memset
、__asan_handle_no_return
、__sanitizer_ptr_cmp
、__sanitizer_ptr_sub
,有些平台下会额外插入__asan_shadow
数组变量以指向ShadowMap。
判断是否需要设置动态起址的(当前为否)。
将llvm.localescape
里的allocas
标记为不感兴趣,即不对其插桩。
判断当前函数内的每个BasicBlock里的每个指令的操作符是不是感兴趣的,标准是:
LoadInst/StoreInst/AtomicRMWInst/AtomicCmpXchgInst
指令、涉及SIMD或者非指针传参的CallInst
,存在某个内存操作(MOP)。instrumentMop
计数一下Load/Store指令,最终进入AddressSanitizer::instrumentAddress
。
将memset/memmove/memcpy
替换成asan的wrapper(有__asan_
前缀的)
使用Alloca指令将函数传参分配为栈变量。
遍历函数,搜集alloca、ret、lifetime相关的指令。
初始化回调函数声明,包括__asan_stack_{malloc,free}_#
、__asan_{,un}poison_stack_memory
、__asan_set_shadow_{0x00, 0xf1, 0xf2, 0xf3, 0xf5, 0xf8}
、__asan_alloca_{,un}poison
。它们具体内容在ASAN Runtime中。
动态Alloca处理
静态Alloca处理
0xf8
,额外创建指令在llvm.lifetime.start
时标记有效部分位0x0,在llvm.lifetime.end
时再将有效部分标记为无效0xf8
。__asan_set_shadow_
函数来设置,否则,计算连续8字节应该设置怎么样的ShadowByte,通过插入一条store指令来完成8个ShadowByte的设置(避免逐个Byte设置导致开销过大)。ModuleAddressSanitizer
出动