【问题标题】:How can I get Function Name of indirect call from CallInst in LLVM如何从 LLVM 中的 CallInst 获取间接调用的函数名称
【发布时间】:2017-06-22 21:33:53
【问题描述】:
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
}

【问题讨论】:

  • 当你说“间接函数”时,你指的是函数指针还是成员函数?你在哪里看这个?如果通过函数指针,我相当确定你找不到函数本身的名称。是否可以确定指针的名称,我不确定。拥有一些 IR 或来源 [甚至两者都更好] 来了解您到底要解决什么问题,这真的很有帮助。
  • 是的,函数指针。我正在尝试打印所有调用的函数,包括通过函数指针调用的函数。
  • 如果不遵循所有代码回到指针的原点,这可能是不可能的(这反过来又变成了“停止问题” - 数据或无法确定的函数结果可能控制函数指针是否设置为 A或 B 或 C....)。您也许可以说“这个函数[调用函数指针 P] 由 F1、F2 和 F3 调用,这可能会将函数指针设置为 A、B 或 C”——但这是一项艰苦的工作。您肯定需要整个代码的调用流程图 [当然不适用于在 TU 之外设置的函数]
  • 我怎样才能得到pinter的名字?
  • 不确定,这就是为什么我要求在 LLVM IR 或 C/C++ 中提供一个示例,以便我可以查看您要跟踪的内容 - 如果我我可能会想出一些完全不同的东西以我的 Pascal 编译器为例。但是知道函数指针只是战斗的一小部分。考虑到函数指针可能是函数的参数...

标签: c++ llvm llvm-clang


【解决方案1】:

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

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

【讨论】:

  • 这对我不起作用。我仍然得到 fname 的 NULL 值。有什么建议吗?
  • 当您有直接呼叫但通过位转换时,此方法有效。不用于间接调用。
【解决方案2】:

我的建议是使用 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)'

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

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

起点是函数或模块传递,这将在 LLVM 文档的本节中描述。​​

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

我还没有查看所有细节,但我想说CallGraphSCCPass 可能是一个很好的起点,因为它从被调用者开始,你可以在调用图中按自己的方式工作,而不是下来。但可能不适合这种特殊情况,所有事情都发生在一个函数中 - 不确定。

就像我在 cmets 中所说的那样,对于琐碎的情况,这可以做到,但祝你好运尝试遵循任何依赖于用户数据或无法在编译时确定的函数(当然是编译时如果函数的结果是编译时可确定的,则可确定 - 如果源已知且所有影响输出的输入都已知,则它是编译时可确定的!)

【讨论】:

    【解决方案3】:

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

    你提到的情况很琐碎:优化后的LLVM会变成直接调用,你直接得到函数。

    如果x 是一个全局变量,你也许能够得到一个函数名的list(这里是foo 和goo),但不知道实际调用的是哪个。但即便如此,LLVM 也应该能够进行选择性的间接调用提升并暴露直接调用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-14
      • 1970-01-01
      • 2017-05-29
      • 1970-01-01
      • 2011-09-09
      • 1970-01-01
      • 2017-09-15
      • 1970-01-01
      相关资源
      最近更新 更多