attribution of anonymous members during constant structure initialisation

62 Views Asked by At

Assume the following simple structure in C:

struct entry {
    int id;
    const char *name;
};

Nothing special. Say I'd like to initialise an array of members globally. One would do:

const static struct entry entries[] = {
        {0, "foo", },
        {1, "bar", },
        {},
};

Nothing special. Now, say I'd like to place the entries array in the special section special. One would do:

const static struct entry entries[] __attribute__((__section__(".special"))) = {
        {0, "foo", },
        {1, "bar", },
        {},
};

Nothing special. Here comes my issue. I'd like to place the strings foo and bar in that section, too. Possible, but I want to do it without the use of intermediate variables.

The point is, that string literals are nothing else than pointers to the stored string placed in .rodata. I don't want to place them in .rodata, I want to place them somewhere else. But without the use of intermediate variables.

During code, one can use:

#define SPECIAL_STR(X) ({static const char __c[] __attribute__((__section__(".special")) = (X); &__c[0];})

With the usage:

void routine(void) {
    printf(SPECIAL_STR("foo"));
}

This will enforce a string to be located inside the section. But this fancy macro can only be used inside routines.

How can I place the strings foo and bar in the special section, without the need for intermediate variables? I don't want to have intermediate variables, as they make everything cumbersome.

What I would like to have is a macro like SPECIAL_STR that works as follows:

const static struct entry entries[] __attribute__((__section__(".special"))) = {
        {0, SPECIAL_STR("foo"), },
        {1, SPECIAL_STR("bar"), },
        {},
};

PS: I'd also like to avoid #pragma rodata-name, if possible :-)

2

There are 2 best solutions below

0
Lundin On

The obvious solution is to swap const char *name; for const char name [MAX_SIZE];. Then you don't have to care about where/if the string literals are stored, since a copy of them will get uploaded into your special segment. It also sounds like what you are trying to make is an application with deterministic behavior, so having variably-sized strings might not be something you actually need?

0
nielsen On

I do not think that what is asked can be accomplished 100%.

However, it is possible to initialize the array with named variables without having to manually synchronize two lists. This solution comes with the price of using X macro which will make the code obscure to read for people not familiar with that construct.

Anyway, doing this, the intermediate variables can be automatically defined and used for the array initialization as follows:

struct entry {
    int id;
    const char *name;
};

// Define the list of entries in the array
#define ENTRIES \
  X(0, "foo") \
  X(1, "bar")

// Automatically generate string variables for the names
#define X(ID, NAME) const char __attribute__((__section__(".special"))) SPECIAL_STRING_##ID[] = NAME;
ENTRIES
#undef X

// Finally, initialize the array
#define X(ID, NAME) {ID, SPECIAL_STRING_##ID},
const static struct entry entries[] __attribute__((__section__(".special"))) = {
        ENTRIES
        {},
};
#undef X

Changes to the entries array can then be done only by modifying the list in the ENTRIES macro.