检查函数参数是否是 LLVM 中的全局变量

Checking if a function argument is a global variable in LLVM

这个问题类似于几年前 posted 的问题 (Tracing global variables use in llvm),但没有人回答(我也无法实施 post 中建议的评论).我想详细说明这个问题和 post 我的解决方案,看看我是否能得到一些见解。

所以让我们假设我有一个如下所示的简短测试代码:

char a[10];
void foo(char *buf, int test) {
  printf("Foo %p\n", buf);
  buf[1] = '1';
}
int main() {
  foo(a, 17);
}

这被翻译成 LLVM IR,如下所示(为简洁起见进行了删减):

@test_global = dso_local global i32 17, align 4
@a = dso_local global [10 x i8] zeroinitializer, align 1

define dso_local void @foo(i8* %0, i32 %1) #0 {
  %3 = alloca i8*, align 8
  ...
  store i8* %0, i8** %3, align 8

define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  ...
  store i32 0, i32* %1, align 4
  call void @foo(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @a, i64 0, i64 0), i32 17)

所以问题本身一开始很容易解决。我只需要检查函数 @foo 中的指令 store i8* %0, i8** %3, align 8%0 是否指向全局内存?因为 %0call 函数 call void @foo(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @a, i64 0, i64 0), i32 17) 中的第一个参数,所以我认为这是最简单的方法。

但是,我对实现这种检查需要做的事情有点迷茫。我一直在研究解决这个问题,但我能做的最好的事情就是有一个看起来像这样的 LLVM pass:

void checkGlobal(Function &F, AAResults &AA) {
  SetVector<Value *> Ptrs; // worklist of pointers
  for (auto &arg : F.args()) {
    if (arg.getType()->isPointerTy()){
      Ptrs.insert(&arg);
    }
  }
  for (auto item : Ptrs) {
    for (auto user : item->users()) {
      if (auto storeI = dyn_cast<StoreInst>(user)) {
            errs() << *storeI->getOperand(0) << " " << *storeI->getPointerOperand() << "\n";
            // this will find the store i8* %0, i8** %3, align 8 
            // LLVM IR instruction with respective operands
      }
    }     
  }
}

如果我需要对 @foo 函数中的指针 i8* %0 使用别名分析,我将别名分析结果 AAResults 传递到此函数中(如果需要,请告诉我是可能的)。

我觉得我至少在朝着正确的方向解决这个问题,但目前停滞不前;我也尝试过从 LLVM 模块级别解决这个问题,方法是像这样获取全局变量结果:

auto &list = M.getGlobalList();
for (auto &gv : list) {
  for (auto use : gv.users()) {
    errs() << *use << "\n"; 
  }
}

上面的代码将 return 类似于:i8* getelementptr inbounds ([10 x i8], [10 x i8]* @a, i64 0, i64 0) for @foo 函数来查看我是否可以使用此信息来分配哪个函数具有作为参数传入的全局变量和手动解决问题。但是,这种方法不可靠且非常弱。

感谢您的任何建议,如果我的问题中有任何不清楚的地方,请告诉我。

从某种意义上说,函数的目的是将代码分成做某事的部分和提供操作数据的部分。这意味着函数内的代码不知道所提供的是否是全局变量,设计使然。

当代码按设计运行时,这可能会很烦人 ;) 您有几个可能的解决方法。

首先,如果无法从模块外部调用函数,您可以连接调用该函数的每个 argument of the function to an operand of the CallInst/InvokeInst 指令,并查看这些操作数是否为全局变量。这可能是一个递归过程,您必须考虑如果某些调用者提供全局变量而其他调用者不提供全局变量,或者如果信息不完整,因为您不一定能决定特定调用指令是否调用您的函数,会发生什么情况.

另一个是运行时看。一般各个平台都会把全局变量定位在地址的特定部分space,你可以看看那是什么。如果你有一个指针,你可以将它转换为 int 并将它与链接器放置全局变量的地址 space 的开始和结束进行比较。