gdb Python API:可以调用类/结构体方法吗

12 投票
4 回答
2394 浏览
提问于 2025-04-18 00:45

在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 个回答

0

这些方法同样适用于静态方法。

  1. 使用方便的变量。

    gdb.set_convenience_variable("tmp",
            gdb.parse_and_eval("c").address
            )
    gdb.execute("print $tmp->f()")
    
  2. 如果你知道类型,可以这样做:

    gdb.parse_and_eval("C::f")(
            gdb.parse_and_eval("c").address
    )
    

    (另外,你也可以使用变形名称 _ZN1C1fEv。可以用 nm --demangle 来处理这个,比如说)

  3. 如果你只知道目标函数的类型,可以这样做:

    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
    )
    
  4. 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

1

在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%的几率会失败。

3

这只是一个还没有人实现的功能。你可以看看它是否在bugzilla上,如果没有的话,可以提交一个bug。

一个常见的解决办法是把“this”这个参数的值放进一个字符串里,然后通过gdb.parse_and_eval来调用。这个方法通常有效,但当然不是最好的选择。

10

好的,我想我通过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);

撰写回答