6

I have this POC in C that saves a few structs in a custom section and then iterates over those structs, showing their content.

#include <stdio.h>

char a, b, c;

struct counter_info {
    int counter;
    char *name;
} __attribute__((packed));

#define __PUT_STUFF_IN_SECTION(_name)                   \
do{                                                     \
    static struct counter_info __counter_info_##_name   \
    __attribute((__section__("counters")))              \
    __attribute((__used__)) = {                         \
        .name = #_name,                                 \
        .counter = 0,                                   \
    };                                                  \
}while(0)

extern struct counter_info __start_counters;
extern struct counter_info __stop_counters;

int main(int argc, char **argv){
    printf("Start %p\n", &__start_counters);

    __PUT_STUFF_IN_SECTION(a);
    __PUT_STUFF_IN_SECTION(b);
    __PUT_STUFF_IN_SECTION(c);

    struct counter_info *iter = &__start_counters;
    for(; iter < &__stop_counters; ++iter){
        printf("Name: %s | Counter: %d.\n", iter->name, iter->counter);
    }
    printf("End %p\n", &__stop_counters);

    return 0;
}

Output:

Name: c | Counter: 0.
Name: b | Counter: 0.
Name: a | Counter: 0.

The output is as expected, so I'm trying to do that same thing in a kernel module:

hello-1.c

#include <linux/module.h>
#include <linux/kernel.h>

char a, b, c;

struct counter_info {
    int counter;
    char *name;
} __attribute__((packed));

#define __PUT_STUFF_IN_SECTION(_name)                   \
do{                                                     \
    static struct counter_info __counter_info_##_name   \
    __attribute((__section__("counters")))              \
    __attribute((__used__)) = {                         \
        .name = #_name,                                 \
        .counter = 0,                                   \
    };                                                  \
}while(0)

extern struct counter_info __start_counters;
extern struct counter_info __stop_counters;

int init_module(void){
    __PUT_STUFF_IN_SECTION(a);
    __PUT_STUFF_IN_SECTION(b);
    __PUT_STUFF_IN_SECTION(c);
    return 0;
}

void cleanup_module(void){
    struct counter_info *iter = &__start_counters;
    for(; iter < &__stop_counters; ++iter){
        printk(KERN_INFO "Name: %s | Counter: %d.\n", iter->name, iter->counter);
    }

}

Makefile:

obj-m += hello-1.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

But I get those warnings when I compile the module:

WARNING: "__stop_counters" [/media/sf_procmon/procmon_kmodule/test/hello-1.ko] undefined!
WARNING: "__start_counters" [/media/sf_procmon/procmon_kmodule/test/hello-1.ko] undefined!

My question is: Why isn't working and how am I supposed to use the section attribute inside a LKM?

EDIT:

I saw this answer Initialize global array of function pointers at either compile-time, or run-time before main() and I tried doing the same:

Makefile

ccflags-y := -Wl,-Tlinkerscript.ld

obj-m += hello-1.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

linkerscript.ld

SECTIONS
{
    .rel.rodata.counters : {
        PROVIDE(start_counters = .);
        *(counters)
        PROVIDE(stop_counters = .);
    }
}
INSERT AFTER .text;

but I keep getting the same warnings. I'm not sure if I did something wrong with the linker script or that's just not the solution for my problem.

EDIT:

I'm editing my question so hopefully somebody can give me a workaround. At compile time a few structs are declared and filled with data. Each struct is declared in a block so I can't access them by name and I cant' declare them outside. I also don't know the exact number of structs as it can change from compile to compile. What I need is a way to access them all (iterate over them). I actually don't care if the structs are going to be saved in a section or with some other magic, as far as I can iterate over them.

Community
  • 1
  • 1
alexandernst
  • 14,352
  • 22
  • 97
  • 197
  • In your kernel module example you are using the preprocessor attach like this (".name = #_name,"), but trying to use them like this ("__start_counters;"). You would have to reverse it for this to work ("__counters_start"). – Peter L. Sep 10 '13 at 00:14
  • @PeterL. ```.name``` has nothing to do with the section name. ```.name``` is just an item of the struct ```counter_info```. That said, do you think I should switch the words in ```start_counter``` in my linker script? – alexandernst Sep 10 '13 at 07:15
  • Perhaps the kernel loader doesn't support this attribute. One difference between kernel module loading and a program is that the former is dynamically adding code to the running kernel, so I suspect it's too late to add new sections. – ash Sep 10 '13 at 13:49
  • @ash Problem is that I can't really tell if it's supported or not. Can't find any docs to prove either option. – alexandernst Sep 10 '13 at 13:56
  • How about looking at the code which loads the module - both the userspace utilities (like insmod) and the kernel code? From what I've read, it sounds like the userspace equivalent is performed in the linker, which doesn't operate on kernel modules. – ash Sep 10 '13 at 14:07
  • Another thought - with more understanding of what is needed, perhaps an alternative can be suggested. – ash Sep 10 '13 at 14:08
  • @ash I really lack the knowledge to understand what's going on on the entire path from me loading the kernel to it being loaded and running. I'm open to suggestions about workarounds. Let me edit my question. – alexandernst Sep 10 '13 at 14:09

2 Answers2

5

This works for me:

Makefile

obj-m := example.o

example-y += hello.o
ldflags-y += -T$(M)/layout.lds

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

layout.lds

SECTIONS
{
    .counters : {
        __start_counters = . ;
        *(.counters)
        __stop_counters = . ;
    }
}

hello.c

#include <linux/module.h>
#include <linux/kernel.h>

char a, b, c;

asm (".section .counters, \"aw\"");

typedef struct {
    int counter;
    char *name;
} __attribute__((packed)) counter_info_t;

#define __PUT_STUFF_IN_SECTION(_name)                   \
do{                                                     \
    static counter_info_t __counter_info_##_name   \
    __attribute((unused,section(".counters"))) = {             \
        .name = #_name,                                 \
        .counter = 0,                                   \
    };                                                  \
}while(0)

extern counter_info_t __start_counters[];
extern counter_info_t __stop_counters[];

int init_module(void){
    __PUT_STUFF_IN_SECTION(a);
    __PUT_STUFF_IN_SECTION(b);
    __PUT_STUFF_IN_SECTION(c);
    return 0;
}

void cleanup_module(void){
    counter_info_t *iter = __start_counters;
    for(; iter < __stop_counters; ++iter){
        printk(KERN_INFO "Name: %s | Counter: %d.\n", iter->name, iter->counter);
    }

}

The point is to use ldflags-y variable.

Ilya Matveychikov
  • 3,936
  • 2
  • 27
  • 42
2

To solve the need to capture structs defined in different blocks of code where their number can vary and the references to them cannot be centralized, two ideas come to mind. First, which will be described below, is to use a registration method, and second is to have the build process scan the sources for these structs to collect their information in order to create a new source file with the necessary references.

Registration Method

  • Use a linked-list to hold the registered structs
  • On creating each struct, call a function to register it; a macro is a good choice to simplify the syntax and allow the method to change relatively easily.

For example:

struct reg_list_node
{
    struct counter_info  *counter;
    struct reg_list_node *next
};

void register_counter (counter_info *new_counter)
{
    // intentionally leaving out detail; allocate the new node and insert into the list
}

#define REGISTER_COUNTER(counter) register_counter(&counter)

Then, when counters are registered:

struct counter_info my_counter;
REGISTER_COUNTER(my_counter);

Oh, and this gets rid of the need for dynamic allocation (please watch the syntax of the macro - it may need tweeking):

struct reg_list_node
{
    struct counter_info  *counter;
    struct reg_list_node *next
} head;

void register_counter (reg_list_node *new_node)
{
    new_node->next = head;
    head = new_node;
}

#define REGISTER_COUNTER(cntr) { static struct reg_list_node counter_node; counter_node.counter = & cntr; register_counter(&counter_node); }
ash
  • 4,867
  • 1
  • 23
  • 33