4

I'd like to define (and initialize) a number of instances of a struct across a number of *.c files, but I want them to gather at compile time into a single contiguous array. I've been looking into using a custom section and using the section's start and end address as the start and end of the array of structs, but I haven't quite figured out the details yet, and I'd rather not write a custom linker script if I can get away with it. Here's a summary of my first hack which didn't quite work:

// mystruct.h:
typedef struct { int a; int b; } mystruct;

// mycode1.c:
#include "mystruct.h"
mystruct instance1 = { 1, 2 } __attribute__((section(".mysection")));

// mycode2.c:
#include "mystruct.h"
mystruct instance2 = { 3, 4 } __attribute__((section(".mysection")));

// mystruct.c:
extern char __mysection_start;
extern char __mysection_end;
void myfunc(void) {
    mystruct * p = &__mysection_start;
    for ( ; p < &__mysection_end ; p++) {
        // do stuff using p->a and p->b
    }
}
makes
  • 6,438
  • 3
  • 40
  • 58
Isaac Sutherland
  • 3,082
  • 4
  • 28
  • 37

1 Answers1

2

In order to use a custom section, you must define its start address in a custom linker script. Copy your device's linker script and add the new section into its SECTIONS block:

/* in custom.gld */
mysection 0x2000 :
{
    *(mysection);
} >data

To make your objects go into this section, use the section attribute:

/* mycode1.c: */
#include "mystruct.h"
mystruct __attribute__((section("mysection"))) instance1 = { 1, 2 };

/* mycode2.c: */
#include "mystruct.h"
mystruct __attribute__((section("mysection"))) instance2 = { 3, 4 };

Now, to get the boundaries of your custom section, you can use the .startof.(section_name) and .sizeof.(section_name) assembler operators:

#include "mystruct.h"

char *mysection_start;
char *mysection_end;
size_t mysection_size;

int main(void)
{
    asm("mov #.startof.(mysection), W12");
    asm("mov #.sizeof.(mysection), W13");
    asm("mov W12, _mysection_start");
    asm("mov W13, _mysection_size");

    mysection_end = mysection_start + mysection_size;

    mystruct *p = (mystruct *)mysection_start;
    for ( ; (char *)p < mysection_end ; p++)
    {
        // do stuff using p->a and p->b
    }
}
makes
  • 6,438
  • 3
  • 40
  • 58
  • btw, may I ask why you want to do this? – makes Jul 18 '13 at 00:44
  • I need a single configuration UI for tweaking parameters belonging to several different modules, but I don't want to require every module to have an initialization procedure which registers its parameters with the configuration module. So I thought, can I "register" the parameters at compile time somehow? – Isaac Sutherland Jul 18 '13 at 14:23
  • 1
    I ended up defining section start and end symbols using the `__mysection_start = .;` and `__mysection_end = .;` in the linker script. Also, it turns out you can augment the platform-default linker script by adding your section directives to the linker commandline. – Isaac Sutherland Jul 18 '13 at 17:53
  • @Isaac, then this would be possible without a custom linker script, no? Section defined on command line, boundaries read using inline assembler? – makes Jul 20 '13 at 01:36
  • 1
    Sorry, my last sentence in that comment didn't come out right. What I meant to say was you could add a linker script file along with your object files on the linker command line to augment the default linking behaviour, not that you can put linker script language commands on the command line. – Isaac Sutherland Jul 21 '13 at 02:39
  • 1
    e.g. `gcc-ld.exe mycode1.o mycode2.o mylink.gld -T p33EP512MU810.gld -o dostuff` uses `p33EP512MU810.gld` as the main linker script, but `mylink.gld` augments it. That's enough for what I needed. My issue was that I needed to build my code for multiple targets and I didn't want to maintain multiple linker scripts for each target since the MPLABX IDE automatically uses the appropriate default linker script. Specifying an "augmenting" linker script in this way solved my problem. – Isaac Sutherland Jul 21 '13 at 02:43