如何从LLVM中的CallInst获取间接调用的函数名称

时间:2015-02-22 00:24:12

标签: c++ llvm llvm-clang

Function *fun = call->getCalledFunction();

getCalledFunction();如果是间接调用则返回null。如何获取函数的名称或指针的名称?

我发现Stack Overflow中与此问题相关的所有问题都谈到了直接调用的函数名称或指针类型。

我只想跟踪这样的案例:

void foo(){}
void goo(){}
void main(){
  int x = 1;
  void (*p)();
  if(x)
    p = &foo;
  else
    p = &goo;
  p(); // print the called function name
}

3 个答案:

答案 0 :(得分:2)

我遇到了同样的问题。阅读llvm源代码后,这是我的解决方案:

Function* fp = CI->getCalledFunction();
if (fp==NULL) {
    Value* v=CI->getCalledValue();
    Value* sv = v->stripPointerCasts();
    StringRef fname = sv->getName();
    errs()<<fname<<"\n";
 }

答案 1 :(得分:1)

我的建议是使用clang AST访客,而不是使用LLVM IR。

可以在此处找到AST访客的示例:

http://clang.llvm.org/docs/RAVFrontendAction.html

如果我们转储代码的AST(编译为C ++ - 在C中它将略有不同,因为func()的声明与func(...)相同):

$ clang++ -Xclang -ast-dump -fno-color-diagnostics -c funcptr.cpp

TranslationUnitDecl 0x50973b0 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x50978f0 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
|-TypedefDecl 0x5097950 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
|-TypedefDecl 0x5097d50 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
|-FunctionDecl 0x5097df0 <funcptr.cpp:1:1, col:12> col:6 used foo 'void (void)'
| `-CompoundStmt 0x5097e90 <col:11, col:12>
|-FunctionDecl 0x5097ed0 <line:2:1, col:12> col:6 used goo 'void (void)'
| `-CompoundStmt 0x5097f70 <col:11, col:12>
`-FunctionDecl 0x5097fe0 <line:3:1, line:14:1> line:3:5 main 'int (void)'
  `-CompoundStmt 0x50d98b0 <col:11, line:14:1>
    |-DeclStmt 0x50d9548 <line:5:1, col:9>
    | `-VarDecl 0x50d94d0 <col:1, col:8> col:5 used x 'int' cinit
    |   `-IntegerLiteral 0x50d9528 <col:8> 'int' 1
    |-DeclStmt 0x50d9678 <line:6:1, col:12>
    | `-VarDecl 0x50d9620 <col:1, col:11> col:8 used p 'void (*)(void)'
    |-IfStmt 0x50d9818 <line:7:1, line:10:7>
    | |-<<<NULL>>>
    | |-ImplicitCastExpr 0x50d96d0 <line:7:4> '_Bool' <IntegralToBoolean>
    | | `-ImplicitCastExpr 0x50d96b8 <col:4> 'int' <LValueToRValue>
    | |   `-DeclRefExpr 0x50d9690 <col:4> 'int' lvalue Var 0x50d94d0 'x' 'int'
    | |-BinaryOperator 0x50d9758 <line:8:2, col:7> 'void (*)(void)' lvalue '='
    | | |-DeclRefExpr 0x50d96e8 <col:2> 'void (*)(void)' lvalue Var 0x50d9620 'p' 'void (*)(void)'
    | | `-UnaryOperator 0x50d9738 <col:6, col:7> 'void (*)(void)' prefix '&'
    | |   `-DeclRefExpr 0x50d9710 <col:7> 'void (void)' lvalue Function 0x5097df0 'foo' 'void (void)'
    | `-BinaryOperator 0x50d97f0 <line:10:2, col:7> 'void (*)(void)' lvalue '='
    |   |-DeclRefExpr 0x50d9780 <col:2> 'void (*)(void)' lvalue Var 0x50d9620 'p' 'void (*)(void)'
    |   `-UnaryOperator 0x50d97d0 <col:6, col:7> 'void (*)(void)' prefix '&'
    |     `-DeclRefExpr 0x50d97a8 <col:7> 'void (void)' lvalue Function 0x5097ed0 'goo' 'void (void)'
    `-CallExpr 0x50d9888 <line:12:1, col:3> 'void'
      `-ImplicitCastExpr 0x50d9870 <col:1> 'void (*)(void)' <LValueToRValue>
        `-DeclRefExpr 0x50d9848 <col:1> 'void (*)(void)' lvalue Var 0x50d9620 'p' 'void (*)(void)'

在这里,我们可以很容易地看到我们通过p - &gt;来调用CallExpr ImplicitCastExpr - &gt; DeclRefExpr。所以你得到p。但是,当然,你必须解释导致p赋值的代码 - 在这种情况下这并不太难,但想象x没有赋予常量,它会除了“foogoo取决于x的价值”之外,几乎不可能说出任何其他内容。它可能仍然需要一些工作来回溯到p的分配,但应该是可行的。你可以查找函数指针的赋值(通过识别指针,使用getPointeeType然后使用isa<FunctionType>或类似的东西)。

要解析llvm,您必须访问每条指令并找到导致调用的负载 - 在使用优化时,通过将if (x) p = &foo; else p = &goo;替换为{{1}来反过来然后意识到p = &foo;始终设置为单个值,因此不需要通过函数指针调用[在这个简单的情况下]。但LLVM IR本身并不追踪数据的实际来源。一般来说,这是一个类似的原则,只是你离源代码更远,并且必须执行更多步骤来弄清楚被调用者是什么。

起点是函数或模块传递,将在LLVM文档的这一部分中进行描述。

http://llvm.org/docs/WritingAnLLVMPass.html

我没有看过所有的细节,但我会说p可能是一个很好的起点,因为它从Callee开始,你可以按照调用图的方式工作,而不是放下它。但也许不适合这种特殊情况,一切都在一个函数内发生 - 不确定。

就像我在评论中所说的那样,对于微不足道的情况,这可以做到,但是好运试图遵循任何依赖于用户数据或不能编译时可确定的函数的任何东西(当然是编译时)如果函数的结果是编译时可确定的,则可以确定 - 如果已知源和影响输出的所有输入都已知,则编译时可确定!)

答案 2 :(得分:0)

忘掉它:这是不可能的。这需要完美的别名分析。如果可能,LLVM将删除间接调用作为优化。

您提到的案例很简单:优化后的LLVM会将其转换为直接调用,并直接获得该函数。

如果x是一个全局变量,你可能会得到一个函数名的列表(这里是foo和goo),但是不知道哪一个实际上是叫做。但即便如此,LLVM应该能够进行选择性间接呼叫促销并暴露直接呼叫。