LLVM Basic-aa pass调用aa.alias方法触发段错误的原因排查
LLVM Basic-aa 段错误问题排查
问题描述
作为LLVM新手,我尝试用LLVM提供的Basic-aa pass找出所有可能修改给定指针指向内存的store指令。部分输入运行正常,但另一部分输入会触发段错误,通过gdb调试确认错误由llvm::AliasResult实例aa的aa.alias方法导致。
代码实现
核心函数
// 找出所有可能修改指定内存位置的store指令 void findStoresToMemLoc(const llvm::MemoryLocation& memloc, llvm::Function& func, llvm::FunctionAnalysisManager& FAM){ auto& aa = FAM.getResult<llvm::BasicAA>(func); // 获取BasicAA分析结果 for (const auto& bb : func){ for (const auto& instr : bb){ // 遍历函数内所有指令 const llvm::StoreInst* store = llvm::dyn_cast<llvm::StoreInst>(&instr); if (store == nullptr){ // 跳过非store指令 continue; } const auto storeloc = llvm::MemoryLocation::get(store); // 获取store指令的内存位置 auto test = aa.alias(memloc, storeloc); // 别名判断,触发段错误 if (test != llvm::AliasResult::NoAlias){ const auto& dbgloc = store->getDebugLoc(); dbg(llvm::errs()<<"Found store at line:"<<dbgloc.getLine()<<"\n";) } } } } // 测试函数:以函数第一个指针参数为目标内存位置,调用findStoresToMemLoc void testFind(llvm::Function& func, llvm::FunctionAnalysisManager& FAM){ const auto& args = func.getArgumentList(); const auto& firstarg = args.front(); // 取第一个参数(指针类型) const auto memloc = llvm::MemoryLocation(llvm::dyn_cast<Value>(&firstarg)); findStoresToMemLoc(memloc, func, FAM); return; }
测试用例及结果
测试用例1(运行正常)
C代码
#include <stdlib.h> struct Node { int data; struct Node* left, *right; }; void insert(struct Node* h) { if (h == NULL){} h = malloc(sizeof(struct Node)); h->data = 0; // line 12 h->left = h->right = NULL; // line 13 }
运行输出
Found store at line:12 Found store at line:13 Found store at line:13
对应的LLVM IR
define void @insert(%struct.Node*) #0 !dbg !8 { %2 = alloca %struct.Node*, align 8 store %struct.Node* %0, %struct.Node** %2, align 8 call void @llvm.dbg.declare(metadata %struct.Node** %2, metadata !18, metadata !19), !dbg !20 %3 = load %struct.Node*, %struct.Node** %2, align 8, !dbg !21 %4 = icmp eq %struct.Node* %3, null, !dbg !23 br i1 %4, label %5, label %8, !dbg !24 ; <label>:5: ; preds = %1 %6 = call noalias i8* @malloc(i64 24) #3, !dbg !25 %7 = bitcast i8* %6 to %struct.Node*, !dbg !25 store %struct.Node* %7, %struct.Node** %2, align 8, !dbg !27 br label %8, !dbg !28 ; <label>:8: ; preds = %5, %1 %9 = load %struct.Node*, %struct.Node** %2, align 8, !dbg !29 %10 = getelementptr inbounds %struct.Node, %struct.Node* %9, i32 0, i32 0, !dbg !30 store i32 0, i32* %10, align 8, !dbg !31 %11 = load %struct.Node*, %struct.Node** %2, align 8, !dbg !32 %12 = getelementptr inbounds %struct.Node, %struct.Node* %11, i32 0, i32 2, !dbg !33 store %struct.Node* null, %struct.Node** %12, align 8, !dbg !34 %13 = load %struct.Node*, %struct.Node** %2, align 8, !dbg !35 %14 = getelementptr inbounds %struct.Node, %struct.Node* %13, i32 0, i32 1, !dbg !36 store %struct.Node* null, %struct.Node** %14, align 8, !dbg !37 ret void, !dbg !38 }
测试用例2(触发段错误)
C代码
#include <stdlib.h> struct Node { int data; struct Node* left, *right; }; void insert(struct Node* h) { struct Node* n = malloc(sizeof(struct Node)); n->data = 0; n->left = n->right = NULL; if (h == NULL){ h = n; } }
对应的LLVM IR
; Function Attrs: noinline nounwind uwtable define void @insert(%struct.Node*) #0 !dbg !8 { %2 = alloca %struct.Node*, align 8 %3 = alloca %struct.Node*, align 8 store %struct.Node* %0, %struct.Node** %2, align 8 call void @llvm.dbg.declare(metadata %struct.Node** %2, metadata !18, metadata !19), !dbg !20 call void @llvm.dbg.declare(metadata %struct.Node** %3, metadata !21, metadata !19), !dbg !22 %4 = call noalias i8* @malloc(i64 24) #3, !dbg !23 %5 = bitcast i8* %4 to %struct.Node*, !dbg !23 store %struct.Node* %5, %struct.Node** %3, align 8, !dbg !22 %6 = load %struct.Node*, %struct.Node** %3, align 8, !dbg !24 %7 = getelementptr inbounds %struct.Node, %struct.Node* %6, i32 0, i32 0, !dbg !25 store i32 0, i32* %7, align 8, !dbg !26 %8 = load %struct.Node*, %struct.Node** %3, align 8, !dbg !27 %9 = getelementptr inbounds %struct.Node, %struct.Node* %8, i32 0, i32 2, !dbg !28 store %struct.Node* null, %struct.Node** %9, align 8, !dbg !29 %10 = load %struct.Node*, %struct.Node** %3, align 8, !dbg !30 %11 = getelementptr inbounds %struct.Node, %struct.Node* %10, i32 0, i32 1, !dbg !31 store %struct.Node* null, %struct.Node** %11, align 8, !dbg !32 %12 = load %struct.Node*, %struct.Node** %2, align 8, !dbg !33 %13 = icmp eq %struct.Node* %12, null, !dbg !35 br i1 %13, label %14, label %16, !dbg !36 ; <label>:14: ; preds = %1 %15 = load %struct.Node*, %struct.Node** %3, align 8, !dbg !37 store %struct.Node* %15, %struct.Node** %2, align 8, !dbg !39 br label %16, !dbg !40 ; <label>:16: ; preds = %14, %1 ret void, !dbg !41 }
错误原因分析
1. BasicAA 依赖缺失
LLVM 4.0 的 BasicAA pass 依赖 TargetLibraryInfo 分析来识别标准库函数(如malloc)的行为(例如malloc返回的指针具有noalias属性)。如果代码中没有在FunctionAnalysisManager中注册TargetLibraryAnalysis,BasicAA 实例会引用无效的TargetLibraryInfo对象,在处理涉及标准库函数的代码路径时触发段错误。
测试用例2中存在对malloc返回指针的多次操作,触发了BasicAA中需要访问TargetLibraryInfo的逻辑,而测试用例1的代码路径未走到该逻辑,因此没有触发错误。
2. MemoryLocation 构造的潜在问题
在testFind函数中,构造MemoryLocation时使用了dyn_cast<Value>(&firstarg),但firstarg是const Argument&,转换后得到const Value*,而MemoryLocation的构造函数需要非const的Value*。虽然编译时可能通过隐式转换或强制转换通过,但这会导致未定义行为,在某些场景下可能触发段错误。
解决方法
- 注册TargetLibraryAnalysis:在初始化
FunctionAnalysisManager时,先注册TargetLibraryAnalysis,再注册BasicAA:llvm::FunctionAnalysisManager FAM; FAM.registerPass([] { return llvm::TargetLibraryAnalysis(); }); FAM.registerPass([] { return llvm::BasicAA(); }); - 修正MemoryLocation构造:使用非
const的参数引用构造MemoryLocation,避免类型转换导致的未定义行为:auto& firstarg = args.front(); const auto memloc = llvm::MemoryLocation(&firstarg);
内容的提问来源于stack exchange,提问作者JadeFish
相关产品推荐
相关产品推荐

