You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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*。虽然编译时可能通过隐式转换或强制转换通过,但这会导致未定义行为,在某些场景下可能触发段错误。

解决方法

  1. 注册TargetLibraryAnalysis:在初始化FunctionAnalysisManager时,先注册TargetLibraryAnalysis,再注册BasicAA:
    llvm::FunctionAnalysisManager FAM;
    FAM.registerPass([] { return llvm::TargetLibraryAnalysis(); });
    FAM.registerPass([] { return llvm::BasicAA(); });
    
  2. 修正MemoryLocation构造:使用非const的参数引用构造MemoryLocation,避免类型转换导致的未定义行为:
    auto& firstarg = args.front();
    const auto memloc = llvm::MemoryLocation(&firstarg);
    

内容的提问来源于stack exchange,提问作者JadeFish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 10:15:53