插桩功能由ModuleAddressSanitizer::instrumentModule
实现。这是个模块插桩,主要以全局变量插桩为主,也包括ASAN其它功能中所需的模块插桩功能。这里主要关注全局变量的处理。
在此之前,编译器前端(parseSanitizerKinds()
)会识别-fsanitize=address
标识,在Sanitizer类型集合中记录当前是AddressSanitzer。CodeGen会开启一个将全局变量元数据传给ASan的功能(SanitizerMD->reportGlobalToASan()
),这些元数据记录了源码位置、是否动态初始化等信息。
主要步骤包括:
- 将原来的全局变量转变成一个结构体,内部包含了原来的全局变量和一个右Redzone,将原来被使用的全局变量替换(
replaceAllUsesWith
)成新的结构体内的全局变量,之后将所有的新全局变量结构体通过调用__asan_register_globals
进行poison - 构建
asan.module_ctor
(ctor
函数),优先级为1。IR代码如下。里面除了有__asan_register_globals
,还有必要的ASAN初始化函数__asan_init
define internal void @asan.module_ctor() {
call void @__asan_init()
call void @__asan_version_mismatch_check_v8()
call void @__asan_register_globals(i64 ptrtoint ([4 x { i64, i64, i64, i64, i64, i64, i64, i64 }]* @0 to i64), i64 4)
ret void
}
- 构建
asan.module_dtor
define internal void @asan.module_dtor() {
call void @__asan_unregister_globals(i64 ptrtoint ([4 x { i64, i64, i64, i64, i64, i64, i64, i64 }]* @0 to i64), i64 4)
ret void
}
- 将所有原来的
ctor
函数内的首尾插入__asan_before/after_dynamic_init
define internal void @_GLOBAL__sub_I_a.cpp() #0 section ".text.startup" {
entry:
call void @__asan_before_dynamic_init(i64 ptrtoint ([6 x i8]* @___asan_gen_.14 to i64))
call void @__cxx_global_var_init()
call void @__asan_after_dynamic_init()
ret void
}
关于运行时具体注册全局变量,请看《ASAN Runtime源码分析(二)——注册全局变量》