3

i am trying to access kernel linked list, the structure is

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;
....
....

};

using gdb, i am able to print this in following way:

(gdb)p *(qlcnic_wait_event_t *)(((struct my_struct *)dev_base->next->priv).wait_list)

Output is:

$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>

}

to iterate the list, i need to go to 'next' of wait_list and using 'container_of', get base the address. so i am using container_of Macro, code is

#!/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")

After doing (gdb) source container_of.py

The output is:

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.

why is it not working ?How to implement this container_of ?

Baijnath Jaiswal
  • 377
  • 5
  • 17

4 Answers4

7

The problem with your code is that cast() expects a gdb.Type at this point, not a string. Calling gdb.lookup_type() would fix that part.

Regarding offsetof/container_of: By far the most convenient way to make it work is to use gdb's macro facility. This is just because it's easier to access tha a function or command.

(gdb) macro define offsetof(_type, _memb) \
      ((long)(&((_type *)0)->_memb))
(gdb) macro define container_of(_ptr, _type, _memb) \
      ((_type *)((void *)(_ptr) - offsetof(_type, _memb)))

This might go into your .gdbinit.

(gdb) print offsetof(struct foo, bar)
...

To use this in Python, you could reimplement it in the same way you started, using gdb.Type/Field and casting operations, or just rely on the macro definition again:

(gdb) python print gdb.parse_and_eval("offsetof(struct foo, bar)")
...
dns
  • 81
  • 1
  • 2
0

You should put some debugging prints into this code to see what is going wrong. It looks ok to me on first reading, but you could see the type of the argument to "cast" quite easily with a print in "offset_of".

I thought this line was odd:

return int(str(element[field].address), 16)

It seems like you can just do this instead:

return int(element[field].address)
Tom Tromey
  • 21,507
  • 2
  • 45
  • 63
  • i tried with `return int(element[field].address)` but getting the same error. :-( it seems there is a problem in 'offset_of' function in line `element = gdb.Value(0).cast(typeobj)`. But i am not able to identify the exact problem.. – Baijnath Jaiswal Jun 17 '13 at 10:27
0

you should provide a pointer type to the calculated container type, sample code to illustrate the idea.

# 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())
Dyno Fu
  • 8,753
  • 4
  • 39
  • 64
0

The other answers have already pointed the OP about the right fix, so I'm just posting here a working version of the original code and an example in case is of use for someone else :)

container_of.py with the container_of definition:

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 for testing:

#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;
}

Command sequence for testing:

$ 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)
aleixrocks
  • 198
  • 1
  • 6