gdb Python API:可以调用类/结构体方法吗
在Python中,我有一个变量 var
,它的类型是 gdb.Value
,对应于一个C++的结构体。
这个结构体里有一个方法 void foo()
。
我可以评估这个表达式 var['foo']
。但是如果我尝试 var['foo']\()
,就会报错,提示如下:
RuntimeError: Value is not callable (not TYPE_CODE_FUNC)
我认为在我的情况下,值的类型是 gdb.TYPE_CODE_METHOD
(虽然不太确定,但 var['foo'].type.code
返回的是 16
)。
所以我想问的是:
Python的API支持调用类的方法吗?如果不支持,有没有其他解决办法?
谢谢!
4 个回答
这些方法同样适用于静态方法。
使用方便的变量。
gdb.set_convenience_variable("tmp", gdb.parse_and_eval("c").address ) gdb.execute("print $tmp->f()")
如果你知道类型,可以这样做:
gdb.parse_and_eval("C::f")( gdb.parse_and_eval("c").address )
(另外,你也可以使用变形名称
_ZN1C1fEv
。可以用nm --demangle
来处理这个,比如说)如果你只知道目标函数的类型,可以这样做:
cftype=gdb.parse_and_eval("C::f").type # ← this is known gdb.parse_and_eval("c")["f"].cast(cftype)( gdb.parse_and_eval("c").address )
gdb不允许某些灵活性,但也不能保证:
cftype=gdb.parse_and_eval("* (void (*) (void*)) 0").type gdb.parse_and_eval("c")["f"].cast(cftype)( gdb.parse_and_eval("c").address )
(如果是静态函数,你不需要把它“伪装”成 void const*
,也可以直接在C++代码中使用)
示例C++代码:
#include<iostream>
struct C{
int x;
void f() {
++x;
std::cout<<x<<"\n";
}
};
int main(){
C c{5};
c.f();
__builtin_trap();
}
另外,如果结构体是局部的,你需要使用一些在 https://stackoverflow.com/a/70254108/5267751 中描述的变通方法。
示例C++代码
int main(){
struct C{
int x;
void f() {
++x;
std::cout<<x<<"\n";
}
};
C c{5};
c.f();
__builtin_trap();
}
示例Python代码
gdb.parse_and_eval("'main::C::f'")(gdb.parse_and_eval("c").address)
另外可以使用 _ZZ4mainEN1C1fEv
。
在GDB中,方法调用目前还不支持。你可以查看这个链接了解更多信息:https://gdb.sourceware.narkive.com/KVTUIAvl/c-method-call-for-a-gdb-value-object-from-within-python
不过,函数调用是被支持的。如果你有机会创建一个函数包装器来调用你的方法:
void func(mystruct& arg) { arg.method(); }
那么你可以通过全局的GDB搜索找到这个函数,并进行调用:
sym = gdb.lookup_global_symbol('namespace::func(mystruct&)')
result = sym.value()(this.val)
这种方法不需要获取变量的地址,因为由于编译器的优化,这种方式有50%的几率会失败。
这只是一个还没有人实现的功能。你可以看看它是否在bugzilla上,如果没有的话,可以提交一个bug。
一个常见的解决办法是把“this”这个参数的值放进一个字符串里,然后通过gdb.parse_and_eval来调用。这个方法通常有效,但当然不是最好的选择。
好的,我想我通过Tom的建议和另一种解决办法,终于做到了我想要的事情。
我之所以需要额外的解决办法,是因为(正如我在上面的评论中提到的)我没有变量的名字,无法构造出像这样格式的字符串:myval.method()
,以便传给gdb.parse_and_eval
。
所以解决这个问题的方法是先获取变量的地址,然后将其转换为相应的类型,最后在字符串中添加方法调用。
在Python的gdb.Value中,类型和地址都是可以找到的。所以解决方案看起来像这样:
eval_string = "(*("+str(self.val.type)+"*)("+str(self.val.address)+")).method()"
return gdb.parse_and_eval(eval_string);