简体   繁体   中英

Unexpected result when using container_of macro (Linux kernel)

I have a problem with using of container_of macro in the Linux kernel. I have the following code

#define container_of(ptr, type, member) ({ \
        const typeof( ((type *)0)->member) *__mptr = (ptr); \
        (type *)( (char *)__mptr - offsetof(type, member) );})


struct list_head
{
    struct list_head *prev;
    struct list_head *next;
};


struct fox
{
    unsigned long tail_length;
    unsigned long weight;
    unsigned int is_fantastic;

    /*Make this struct a node of the linked list*/
    struct list_head list;
};

I want to make fox structure a node of the linked list.

int main(void)
{
    struct list_head node_first = {.prev=NULL, .next=NULL};
    struct fox first_f = {.tail_length=3, .weight=4, .is_fantastic=0, .list=node_first};

    struct fox *second_f; 
    second_f = container_of(&node_first, struct fox, list);
    printf("%lu\n", second_f->tail_length);
    return 0;
}

I expected that I will see 3 in the terminal, since second_l points to the firstf_f structure, but I have 140250641491552 (some "random" value from the memory, as a think).

That's not how offsetof is meant to be used. Your supposed to pass a pointer to data held in a struct to retrieve a pointer to the overall struct (So a struct list_head** ). Instead you pass a copy of a pointer to the wrong value ( &node_first is different to &first_f.list (what you're supposed to pass), as it is a copy)

For an example of how it works, consider this struct list_head object:

(struct list_head) {   // At 0x1000
    .prev = 0,  // At 0x1000
    .next = 0   // At 0x1004
}

If you were to call container_of((struct list_head**) 0x1004, struct list_head, next) , you would get (struct list_head*) 0x1000 .

This example is a bit confusing because of the same types, so here's another example:

#include <stddef.h>
#include <stdio.h>

#define container_of(ptr, type, member) ({ \
        const typeof( ((type *)0)->member) *__mptr = (ptr); \
        (type *)( (char *)__mptr - offsetof(type, member) );})

struct holds_values {
    int n;
    char c;
};

struct holds_values* get_container(char* c_ptr) {
    // If this was a pointer to a `c` in `holds_values`
    // e.g., `&object.c`, that `char` is sizeof(int) bytes into
    // the object, so this should be mostly the same as
    // `(struct holds_values*) (c - sizeof(int))`
    return container_of(c_ptr, struct holds_values, c);
}

int main() {
    struct holds_values x = { .n = 5, .c = 'a' };

    char* ptr = &x.c;
    struct holds_values* y = get_container(ptr);
    printf("%d\n%d\n", y == &x, y->n);
}

Change

second_f = container_of(&node_first, struct fox, list);

to

second_f = container_of(&first_f.list, struct fox, list);

Explanation :

This has nothing to do with kernel or Linux, it's C and GCC.

The line

struct fox first_f = {.tail_length=3, .weight=4, .is_fantastic=0, .list=node_first};

copies the contents of node_first into first_f.list . first_f is NOT the container of node_first , it contains a copy of node_first .

If you want first_f to be the container of node_first you need to define:

struct fox first_f = {.tail_length=3, .weight=4, .is_fantastic=0, .list={.prev=NULL, .next=NULL}};
struct list_head *node_first = &first_f.list;

ie define node_first as a pointer into the member of first_f .

Note that you could define struct fox to have

struct fox
{
    // ...
    struct list_head *list;
};

instead of

struct fox
{
    // ...
    struct list_head list;
};

ie to have struct fox point to the list_head , and then your original main() would work.

But that's simply not the right way to use list_head , list_head is intended to be contained within other structs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM