ASAN Pass【源码分析】(三)——初始化

前言

LLVM ASAN【源码分析】(一)》中通过已有资料简单分析了下ASAN结构。

LLVM ASAN【源码分析】(二)》描述了调试环境的准备。

Clang12前使用Legacy Pass Manager启动ASAN,而Clang14开始采用New Pass Manager。

Clang父进程创建子进程完成编译任务。

从Clang父进程Main函数开始

线程刚启动时,记录栈底地址。作为LLVM工具执行一次性初始化。检查标准文件描述符。配置支持目标架构。设置命令行分词器。设置诊断功能。待Clang Driver解析完命令行后,ExecuteCompilation() Fork子进程执行具体编译任务。

// clang/tools/driver/driver.cpp
int main(...) {
	...
	Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
	...
}

进入Clang子进程Main函数

子进程带着解析后的命令,完成配置后使用cc1(clang默认编译器)开始编译。(代码如下)

// clang/tools/driver/driver.cpp
int main(...) {
	...
	return ExecuteCC1Tool(Args);
	...
}

调用cc1编译器

// clang/tools/driver/driver.cpp
static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV) {
  ...
  return cc1_main(makeArrayRef(ArgV).slice(1), ArgV[0], GetExecutablePathVP);
  ...
}

创建编译器实例,配置调用功能、前端选项和诊断功能,执行前端动作。

// clang/tools/driver/cc1_main.cpp
int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
	...
	Success = ExecuteCompilerInvocation(Clang.get());
	...
}

解析前端选项,加载所需插件,编译器实例创建并执行前端任务。

// clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
bool ExecuteCompilerInvocation(CompilerInstance *Clang) {
	...
	bool Success = Clang->ExecuteAction(*Act);
	...
}

配置前端任务,读取源文件并准备处理。

// clang/lib/Frontend/CompilerInstance.cpp
bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
  ...
  if (llvm::Error Err = Act.Execute()) { ... }
  ...
}

执行CodeGen动作。

// clang/lib/Frontend/FrontendAction.cpp
llvm::Error FrontendAction::Execute() {
  ...
  ExecuteAction(); //函数为虚函数,当前指向clang::CodeGenAction::ExecuteAction,类继承关系为class CodeGenAction : public ASTFrontendAction : public FrontendAction
  ...
}

由于当前文件语言不是IR,ASTFrontendAction执行抽象语法树分析

// clang/lib/CodeGen/CodeGenAction.cpp
void CodeGenAction::ExecuteAction() {
  ...
  this->ASTFrontendAction::ExecuteAction();
  ...
}

编译器实例创建Sema结构体以分析抽象语法树。

// clang/lib/Frontend/FrontendAction.cpp
void ASTFrontendAction::ExecuteAction() {
  ...
  ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats, CI.getFrontendOpts().SkipFunctionBodies);
}

AST分析中会收集状态,配置语法结构体和分析器结构体。ASTConsumer处理顶级声明的词法分析,使用AST上下文处理转译单元(进入后端)。

// clang/lib/Parse/ParseAST.cpp
void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
	...
	Consumer->HandleTranslationUnit(S.getASTContext());
	...
}	

AST分析、IR生成及模块链接后,发射后端输出。

// clang/lib/CodeGen/CodeGenAction.cpp
// clang::BackendConsumer::HandleTranslationUnit
// 继承关系 class BackendConsumer : public ASTConsumer
void HandleTranslationUnit(ASTContext &C) override {
	...
	EmitBackendOutput(...);
	...
}

构建汇编帮助工具实例,根据代码生成选项,使用LegacyPassManager发射二进制。

// clang/lib/CodeGen/BackendUtil.cpp
void clang::EmitBackendOutput(...) {
	...
	AsmHelper.EmitAssemblyWithNewPassManager(...);
	AsmHelper.EmitAssembly(...);
   	...
}

开始处理Pass

Legacy

配置环境选项,分别构建模块和函数粒度的Pass管理器。

// clang/lib/CodeGen/BackendUtil.cpp
void EmitAssemblyHelper::EmitAssembly(BackendAction Action, std::unique_ptr<raw_pwrite_stream> OS) {
    ...
    CreatePasses(PerModulePasses, PerFunctionPasses);
   	...
}

PMBuilder开始填充模块和函数粒度的Pass管理器,也就是开始注册各个Pass。最后Pass管理器会调用createAddressSanitizerFunctionPass/createModuleAddressSanitizerLegacyPassPass来构建AddressSanitizerLegacyPass/ModuleAddressSanitizerLegacyPass类对象,构造函数会进行真正的初始化。

与此同时,由于AddressSanitizerLegacyPass依赖ASanGlobalsMetadataWrapperPass(事实上ModuleAddressSanitizerLegacyPass也依赖,但AddressSanitizerLegacyPass先初始化),因此会初始化ASanGlobalsMetadataWrapperPass类,将处理顶级声明时提取的全局变量信息保存到"llvm.asan.globals"标记的GlobalsMetadata类中,供ASAN后续针对全局变量插桩Redzone使用。

New

  1. 对目标机器、选项、PassBuilder、Pass插件及Pass配置注册。
  2. PassBuilder构造函数依据shouldPopulateClassToPassNames(当前false)记录Pass/Analysis名和类名
  3. PassBuilder注册几类Pass/Analysis管理器【Module Analyses ManagerCGSCC Analyses ManagerFunction Analyses Manager(登记了Alias Analyses ManagerTarget Library Analysis)、Loop Analyses Manager
  4. 根据选项(当前false)开启BoundsCheckingPass
  5. addSanitizers()中注册用于添加Pass的Lambda函数
  6. 当前优化级别为O0,进入buildO0DefaultPipeline
// clang/lib/CodeGen/BackendUtil.cpp
void EmitAssemblyHelper::EmitAssemblyWithNewPassManager(
	...
    MPM = PB.buildO0DefaultPipeline(Level, IsLTO || IsThinLTO);
   	...
}

添加最基本的ModulePass,执行注册过的各类Optimizer回调函数(当前均为空),调用前面addSanitizers()注册的Lambda函数。

// llvm/lib/Passes/PassBuilder.cpp
ModulePassManager PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level, bool LTOPreLink) {
	...
	for (auto &C : OptimizerLastEPCallbacks)
	    C(MPM, Level);
    ...
}

addSanitizers及其中注册的Lambda函数如下所示。添加ModuleSanitizerCoveragePass,添加Sanitizer.def中定义的MemorySanitizerKernel MemorySanitizerThreadSanitizerAddressSanitizer(关注)、Kernel AddressSanitizerHardware-assisted AddressSanitizerKernel Hardware-assisted AddressSanitizerDataFlowSanitizer

ASanPass这个Lambda函数中,它对ASanGlobalsMetadataAnalysisModuleAddressSanitizerPassAddressSanitizerPass(该函数Pass添加时转为模块Pass)三类均以ModulePass的形式进行添加,添加时会调用对应的构造函数。

// clang/lib/CodeGen/BackendUtil.cpp
static void addSanitizers(..., PassBuilder &PB) {
  PB.registerOptimizerLastEPCallback([&](ModulePassManager &MPM,
                                         OptimizationLevel Level) {
    ...
    auto ASanPass = [&](SanitizerMask Mask, bool CompileKernel) {
      if (LangOpts.Sanitize.has(Mask)) {
        ...
        MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
        MPM.addPass(ModuleAddressSanitizerPass(
            CompileKernel, Recover, ModuleUseAfterScope, UseOdrIndicator,
            DestructorKind));
        MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass(
            CompileKernel, Recover, UseAfterScope, UseAfterReturn)));
      }
    };
    ASanPass(SanitizerKind::Address, false);
    ASanPass(SanitizerKind::KernelAddress, true);
	...
  });
}

ASAN Pass初始化(Legacy)

AddressSanitizerLegacyPassModuleAddressSanitizerLegacyPass类构造函数内调用初始化函数,根据配置通过PassRegistry注册自身,包括:

  1. PassInfo注册到PassInfoMapPassInfoStringMap
  2. 通知可能存在的监听者
  3. 将自身加入ToFree列表,表示后续需要释放
explicit AddressSanitizerLegacyPass(...) : ... {
    initializeAddressSanitizerLegacyPassPass(*PassRegistry::getPassRegistry());
  }

返回并准备运行Pass

Legacy

返回EmitAssembly(),运行Pass

// clang/lib/CodeGen/BackendUtil.cpp
void EmitAssemblyHelper::EmitAssembly(BackendAction Action, std::unique_ptr<raw_pwrite_stream> OS) {
    ...
    PerModulePasses.run(*TheModule);
   	...
}

最后进入AddressSanitizerLegacyPass/ModuleAddressSanitizerLegacyPassrunOnFunction/runOnModule

New

  1. 返回EmitAssemblyWithNewPassManager()
  2. 目前版本仍然使用Legacy Pass Manager进行代码生成。
  3. 所有Pass添加完毕,开始运行它们。
// clang/lib/CodeGen/BackendUtil.cpp
void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( BackendAction Action, std::unique_ptr<raw_pwrite_stream> OS) {
    ...
	MPM.run(*TheModule, MAM);
  	...
}

后续

Pass运行源码调试放到《LLVM ASAN【源码分析】(四)》中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值