2

I want to be able to declare function so that they are added to a specific code section and then they are all executed before my start function. Something like this:

void __attribute__((__section__(".driverinit"))) disk_driver_init()  {
    dev_register(&diskDevice);
}

The purpose is that each of these would add their variable to an array so that the functions contained in it can be processed. I know I can add these functions all globally but I'm trying to avoid that if I can.

The problem is that as soon as the first one returns I don't know how to go to the next function in the set. Or possibly have the functions not generate a "ret" at the end and fall through. Not sure if that's possible.

Here's the disassembly, I would like it to run each function in the code section and then jump to _start at the end if that's possible somehow...

Disassembly of section .driverinit:

0000000000000000 <disk_driver_init>:
   0:   a9bf7bfd        stp     x29, x30, [sp, #-16]!
   4:   910003fd        mov     x29, sp
   8:   d0000040        adrp    x0, a000 <FLAG_APP_CMD+0x138>
   c:   9128e000        add     x0, x0, #0xa38
  10:   9400020f        bl      84c <dev_register>
  14:   d503201f        nop
  18:   a8c17bfd        ldp     x29, x30, [sp], #16
  1c:   d65f03c0        ret
  20:   14000002        b       28 <_start>
  24:   d65f03c0        ret

Disassembly of section .text.boot:

0000000000000028 <_start>:
  28:   d53800a0        mrs     x0, mpidr_el1
  2c:   92401c00        and     x0, x0, #0xff
  30:   b4000040        cbz     x0, 38 <startup_proc>

Can someone steer me in the right direction?

Rocky Pulley
  • 22,531
  • 20
  • 68
  • 106

3 Answers3

4

Instead of putting the functions themselves in a special section, put pointers to the functions in this special section:

void disk_driver_init()  {
    dev_register(&diskDevice);
}

void __attribute__((section(".driverinit")))(* const disk_driver_init_ptr)() = disk_driver_init;

You'd need something to mark the end of the section so have this in a file by itself and have it be the last object file you link with:

void __attribute__((section(".driverinit")))(* const driverinit_end)() = 0;

You can then call all the driver init functions with something like this:

void __attribute__((section(".driverinit")))(* const driverinit_start)() = 0;

void
call_driver_init_fns(void) {
    void (* const *fn)() = &driverinit_start + 1;
    while (fn != &driverinit_end) {
        (*fn)();
        fn++;
    }
}

Note that this object file needs to be first, or at least before any of the other object files that put function pointers in .driverinit when linking.

This is essentially how C++ constructors work. You can avoid the wasted space _start and _end variables take by using a custom linker script that provides symbols for the start and end of the .driverinit section. There's also the GCC constructor attribute, but if you're not using the standard runtime, as I'm guessing you're not, you'll have to figure how that works on your platform and implement the startup code yourself.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
2

What you have to do is to modify the "C runtime" (CRT), the start-up code that is executed before main() is called. I wrote some guide lines regarding how to write your own proper CRT for embedded systems here.

You can't reliably call any C code before the memory set-up is done: at least not before the stack pointer is set (ARM does this automatically by reading the default items at address 0 and beyond). Some code might need MMU initialization to be done, or .data and .bss initialization to be done.

How to do this is of course very system- and compiler-specific. Lower end Cortex M aren't that advanced to setup though, they don't use virtual memory etc, so from what I remember there shouldn't be much in the way of MMU setup, unless you for some reason use a custom memory map etc. You can just look at how your current CRT does things and do they same in your own.

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

The only case where code runs before a function, in some sense, is in C++ when constructors are fired for heap or local/automatic main object variables (not pointers, real objects on the heap as global or static objects, or automatic/local to subroutines including main() at their start)). In a C/Assembly world, something has to call functions and then call start(). In C, you can put function pointers in an array if they all have the same signature and walk the array running them. You can put a wrapper on start() or make start() call prep routines when it is called, possibly in response to NULL pointers left in place of uninitialized or discarded items.

  • that's essentially what I am trying to do. I need to add the functions to this array, in order to do so, I need to invoke the function that adds them to the array. I'm trying to avoid having all the functions included and referenced from a single file, if possible. – Rocky Pulley Jul 31 '20 at 23:23
  • @RockyPulley: I think you'd normally arrange for the linker to add them to that array at build-time (IDK how or I'd answer, but maybe assembling `.word funcname` into the right section), not run a function at run-time to modify the array of init function pointers. – Peter Cordes Jul 31 '20 at 23:52
  • You put the names in the array and the linker fills them in as addresses of the right code. Names are just pointers like any other pointer. I tell my students a pointer is just an unsigned long with a big attitude, in this case a function with a signature of argument types, number, position and a return type. So, 'strlen' is a pointer to a function that takes just a character pointer and returns a size_t: size_t (*strlen)( char * ), and you can put it in an array with other functions that take one argument of a char pointer and return a size_t. – David G. Pickett Aug 04 '20 at 20:47