Page 1 of 1

Init functions a la Linux...

Posted: Sun Jan 14, 2007 4:43 pm
by JJeronimo
I'm trying to create a macro that would add the specified function to a special section in the object file, so that a "standard" function can execute all the initialization functions... Somewhat like module_init does in Linux kernel modules...

I tried many versions but none if them worked...

This first one doesn't work because inline assembly can only have input and output registers if it's inside a function...

Code: Select all

#define	INITIALIZER(name, symbol);	__asm__ (       \
                      ".section .init\n"                                    \
                      ".asciiz \"" name "\"\n"                           \
                      ".long %0\n"                                        \
                      ".section .text\n"                                  \
                      :                                                         \
                      :  "n" (&symbol)                                   \
                      :                             )
One of the first I tried...

Code: Select all

#define CHARACTER_THAT_QUOTES	"

#define	INITIALIZER(name, symbol);	__asm__ (       \
                      ".section .init\n"                                    \
                      ".asciiz \"" name "\"\n"                           \
                      ".long $" CHARACTER_THAT_QUOTES symbol CHARACTER_THAT_QUOTES "\n"                                        \
                      ".section .text\n")
With the last one, INITIALIZER("This Function", funct_t_i_w_t_d) preprocesses as:

Code: Select all

__asm__ ( ".section .init\n" ".asciiz \"" "This Function" "\"\n" ".long $" " funct_t_i_w_t_d " "\n" ".section .text\n");
Which is more or less what I expected, and seems right... But them, if I try to produce assembly from that, gcc says:

Code: Select all

In file included from exp.c:1:
Aldr_mod.h:21:56: warning: backslash-newline at end of file
exp.c:10: error: missing terminating " character
exp.c:10: error: expected ‘)’ before ‘funct_t_i_w_t_d’
exp.c:10: error: missing terminating " character
I've also consulted linux include files but they are very confusing...

Do you know any simple solution that doesn't involve writing a separate (and smarter!) preprocessor?

Basically, what I want is a macro that adds to the section .init a string to be printed at execute time (before calling the function) and the address of the function (that will then be threated as a function pointer)...
And I would like to get read of that ".long" asm pseudo-op to something more portable (I'm not sure, but I suppose that the .long directive generates a doubleword in any architecture... what I really need is the address size)...

JJ

Posted: Sun Jan 14, 2007 9:27 pm
by nick8325
GCC has an extension for doing that. See the "section" attribute at http://gcc.gnu.org/onlinedocs/gcc/Varia ... butes.html.

I suppose something like

Code: Select all

struct init_function {
    const char * name;
    void (*function)(void);
};

#define INITIALIZER(name, symbol) struct init_function symbol##_init __attribute__((section(".init"))) = { name, symbol };
might do the trick (I haven't tested it, though).

Posted: Mon Jan 15, 2007 10:56 pm
by TheQuux
Ahh... This is fun.

In some header file:

Code: Select all

#define __init __attribute__((section(".initcall"),used ))
typedef void (*initfn)(void);
#define REGISTER_INIT(fn)  static initfn __init$##fn __init = fn;
Note: that will ONLY work under gcc... also, the used attribute seems to be half documented. It is documented for functions, but this is on a variable, where it isn't documented. So, YMMV

Anyways, in your linker script, include:

Code: Select all

    .initcall : AT(ADDR(.initcall) - 0xC0000000)
    {
        _init_start = .;
        *(.initcall);
        _init_end = .;
    }
and, in your startup fn,

Code: Select all

        for (initfn *fn = &_init_start; fn != &_init_end; fn++) {
                (*fn)();
        }
Now, when you have some function you want to call at boot (oh, say,

Code: Select all

void boot_func(){}
), you can just type

Code: Select all

REGISTER_INIT(boot_func);
and it will be called at boot.

Posted: Wed Jan 17, 2007 7:38 am
by JJeronimo
nick8325 wrote:GCC has an extension for doing that. See the "section" attribute at http://gcc.gnu.org/onlinedocs/gcc/Varia ... butes.html.

I suppose something like

Code: Select all

struct init_function {
    const char * name;
    void (*function)(void);
};

#define INITIALIZER(name, symbol) struct init_function symbol##_init __attribute__((section(".init"))) = { name, symbol };
might do the trick (I haven't tested it, though).
Thanks... It works...

JJ