我正在尝试访问内核链表,结构是
struct my_struct {
struct my_hardware_context ahw;
struct net_device *netdev;
struct pci_dev *pdev;
struct list_head mac_list;
struct list_head wait_list;
....
....
};
使用gdb,我可以通过以下方式打印:
(gdb)p *(qlcnic_wait_event_t *)(((struct my_struct *)dev_base->next->priv).wait_list)
输出是:
$17 = {
list = {
next = 0x410026a14ff0,
prev = 0x410026a14ff0
},
comp_id = 0x0,
trigger = 0x0,
active = 0x0,
rsp_word = 0x0 <buses_init at vmkdrivers/src_9/vmklinux_9/linux/drivers/base/bus.c:1061>
}
迭代列表,我需要转到wait_list
的'next'并使用'container_of',获取地址的基础。所以我使用的是container_of宏,代码是
#!/usr/bin/env python
import gdb
long_type = None
def get_type(type_name):
t = gdb.lookup_type(type_name)
if t == None:
raise gdb.GdbError("cannot resolve type '%s'" % type_name)
return t
def get_long_type():
global long_type
if long_type == None:
long_type = get_type("long")
return long_type
def offset_of(typeobj, field):
element = gdb.Value(0).cast(typeobj)
return int(str(element[field].address), 16)
def container_of(ptr, typeobj, member):
return (ptr.cast(get_long_type()) - offset_of(typeobj, member)).cast(typeobj)
class ContainerOf(gdb.Function):
__doc__ = "Return pointer to containing data structure.\n" \
"\n" \
"$container_of(PTR, \"TYPE\", \"ELEMENT\"): Given PTR, return a pointer to the\n" \
"data structure of the type TYPE in which PTR is the address of ELEMENT.\n" \
"Note that TYPE and ELEMENT have to be quoted as strings."
def __init__(self):
super(ContainerOf, self).__init__("container_of")
def invoke(self, ptr, typename, elementname):
return container_of(ptr,
gdb.lookup_type(typename.string()).pointer(),
elementname.string())
ContainerOf()
ptr = gdb.parse_and_eval('(qlcnic_wait_event_t *)(((struct my_struct *)dev_base->next->priv).wait_list)').address
print '%s'%(ptr)
c = container_of(ptr,"qlcnic_wait_event_t","list")
执行(gdb) source container_of.py
输出结果为:
wait_list = {
list = {
next = 0x410026a14ff0,
prev = 0x410026a14ff0
},
comp_id = 0x0,
trigger = 0x0,
active = 0x0,
rsp_word = 0x0 <buses_init at /src_9/linux_9/drivers/base/bus.c:1061>
}
ptr = 0x410026a14ff0
Traceback (most recent call last):
File "container_of.py", line 64, in ?
next = container_of(ptr,"struct qlcnic_wait_event_s","list")
File "container_of.py", line 23, in container_of
return (ptr.cast(get_long_type()) - offset_of(typeobj, member)).cast(typeobj)
File "container_of.py", line 19, in offset_of
element = gdb.Value(0).cast(typeobj)
RuntimeError: Argument must be a type.
为什么它不起作用?如何实现这个container_of?
答案 0 :(得分:2)
代码的问题在于cast()需要gdb.Type 这一点,不是一个字符串。调用gdb.lookup_type()会修复该部分。
关于offsetof / container_of:到目前为止最方便的方式 使其工作是使用gdb的宏设施。这只是因为它更容易访问函数或命令。
(gdb) macro define offsetof(_type, _memb) \
((long)(&((_type *)0)->_memb))
(gdb) macro define container_of(_ptr, _type, _memb) \
((_type *)((void *)(_ptr) - offsetof(_type, _memb)))
这可能会进入你的.gdbinit。
(gdb) print offsetof(struct foo, bar)
...
要在Python中使用它,您可以以相同的方式重新实现它 你开始使用gdb.Type / Field和cast操作,或者 再次依赖宏定义:
(gdb) python print gdb.parse_and_eval("offsetof(struct foo, bar)")
...
答案 1 :(得分:0)
您应该在此代码中添加一些调试打印,以查看出现了什么问题。在第一次阅读时我看起来没问题,但你可以通过“offset_of”中的打印很容易地看到“cast”的参数类型。
我觉得这句话很奇怪:
return int(str(element[field].address), 16)
看起来你可以这样做:
return int(element[field].address)
答案 2 :(得分:0)
你应该为计算的容器类型提供一个指针类型,示例代码来说明这个想法。
# container_of(p, type, field) = (type*) (p - offset_of(type, field)
# offset_of(type, field) = field.bitpos / 8
container_address = int(str(dev_pointer), 16) - field.bitpos / 8
container_pointer = gdb.Value(container_address).cast(container_type.pointer())
答案 3 :(得分:0)
其他答案已经向 OP 指出了正确的修复方法,所以我只是在这里发布原始代码的工作版本和一个示例,以防其他人使用:)
container_of.py 和 container_of 定义:
def get_type(type_name):
t = gdb.lookup_type(type_name)
if t == None:
raise gdb.GdbError("cannot resolve type '%s'" % type_name)
return t
def offset_of(typeobj, field):
element = gdb.Value(0).cast(typeobj.pointer())
return int(str(element[field].address), 16)
# given a pointer to a member in a struct instance, get a pointer to the struct
# instance itself.
#
# arguments:
# ptr: gdb.Value
# typeobj: gdb.Type
# member: string
def container_of(ptr, typeobj, member):
charType = get_type("char")
return (ptr.cast(charType.pointer()) - offset_of(typeobj, member)).cast(typeobj.pointer())
example.c 用于测试:
#include <stdio.h>
#include <stdint.h>
struct list_entry {
struct list_entry *next;
struct list_entry *prev;
};
struct potato {
uint64_t first;
uint64_t second;
struct list_entry entry;
};
struct potato my_potato;
struct list_entry *reference = &my_potato.entry;
int main() {
printf("hello world!\n");
return 0;
}
用于测试的命令序列:
$ gcc -g ./example.c -o ./example
$ gdb ./example
$ source ./container_of.py
$ b main
$ run
$ pi # enter python interpreter
>>> ref = gdb.parse_and_eval('reference')
>>> potatoType = get_type('struct potato')
>>> my_potato = container_of(ref, potatoType, 'entry')
>>> # note here that we need the '.address' to get a pointer to the potato!
>>> assert(my_potato == gdb.parse_and_eval('my_potato').address)