Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ELF section in LKM

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.

like image 372
alexandernst Avatar asked Sep 07 '13 12:09

alexandernst


2 Answers

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.

like image 90
Ilya Matveychikov Avatar answered Oct 12 '22 13:10

Ilya Matveychikov


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); }
like image 34
ash Avatar answered Oct 12 '22 14:10

ash