24

I'm working in C on Linux. I've seen the usage of of the gcc __section__ attribute (especially in the Linux kernel) to collect data (usually function pointers) into custom ELF sections. How is the "stuff" that gets put in those custom sections retrieved and used?

jww
  • 97,681
  • 90
  • 411
  • 885
mgalgs
  • 15,671
  • 11
  • 61
  • 74
  • 1
    Possibly of interest: http://stackoverflow.com/questions/4152018/initialize-global-array-of-function-pointers-at-either-compile-time-or-run-time/4152185#4152185 – Ben Jackson May 14 '13 at 20:59
  • Better that an answer: http://mgalgs.github.io/2013/05/10/hacking-your-ELF-for-fun-and-profit.html – Oliv Dec 19 '18 at 22:24

5 Answers5

34

As long as the section name results in a valid C variable name, gcc (ld, rather) generates two magic variables: __start_SECTION and __stop_SECTION. Those can be used to retrieve the start and end addresses of a section, like so:

/**
 * Assuming you've tagged some stuff earlier with:
 * __attribute((__section__("my_custom_section")))
 */

struct thing *iter = &__start_my_custom_section;

for ( ; iter < &__stop_my_custom_section; ++iter) {
    /* do something with *iter */
}

I couldn’t find any formal documentation for this feature, only a few obscure mailing list references. If you know where the docs are, drop a comment!

If you're using your own linker script (as the Linux kernel does) you'll have to add the magic variables yourself (see vmlinux.lds.[Sh] and this SO answer).

See here for another example of using custom ELF sections.

AJM
  • 1,317
  • 2
  • 15
  • 30
mgalgs
  • 15,671
  • 11
  • 61
  • 74
  • You can also use Objdump -d – KrisSodroski May 14 '13 at 20:57
  • Note: I guessed the "valid C variable name" problem could be avoided using `asm("__start_invalid.c.variable.name")`, but it turns out it does not work. – o11c Jun 23 '16 at 07:33
  • 1
    Dumb question: How do you make the linker put the Stop symbol at the end of the actual section, and not right after the Start symbol? – MarcusJ Nov 13 '21 at 09:24
16

Collecting the information together from various answers, here is a working example of how to collect information into a custom linker section and then read the information from that section using the magic variables __start_SECTION and __stop_SECTION in your C program, where SECTION is the name of the section in the link map.

The __start_SECTION and __stop_SECTION variables are made available by the linker so explicit extern references need to be created for these variables when they are used from C code.

There are also some problems if the alignment used by the compiler for calculating pointer/array offsets is different than the alignment of the objects packed in each section by the linker. One solution (used in this example) is to store only a pointer to the data in the linker section.

#include <stdio.h>

struct thing {
    int val;
    const char* str;
    int another_val;
};
struct thing data1 = {1, "one"};
struct thing data2 = {2, "two"};

/* The following two pointers will be placed in "my_custom_section".
 * Store pointers (instead of structs) in "my_custom_section" to ensure
 * matching alignment when accessed using iterator in main(). */
struct thing *p_one __attribute__((section("my_custom_section"))) = &data1; 
struct thing *p_two __attribute__((section("my_custom_section"))) = &data2;

/* The linker automatically creates these symbols for "my_custom_section". */
extern struct thing *__start_my_custom_section;
extern struct thing *__stop_my_custom_section;

int main(void) {
    struct thing **iter = &__start_my_custom_section;
    for ( ; iter < &__stop_my_custom_section; ++iter) {
        printf("Have thing %d: '%s'\n", (*iter)->val, (*iter)->str);
    }
    return 0;
}
ChrisM
  • 181
  • 1
  • 5
  • 1
    What if the section names are not valid variable names in C – Todd Freed Apr 12 '19 at 02:53
  • 1
    @ToddFreed Do you have an example of a section name that is not a valid variable name in C? – ChrisM Apr 15 '19 at 20:54
  • 1
    ".note.gnu.build-id" is not a valid variable name. This is a section created by ld when the --build-id option is specified, see https://linux.die.net/man/1/ld – Todd Freed Apr 16 '19 at 16:34
  • 1
    Dumb question: How do you make the linker put the Stop symbol at the end of the actual section, and not right after the Start symbol? – MarcusJ Nov 13 '21 at 09:25
7

Linker can use the symbols defined in the code, and can assign their initial values if you use the exact name in the linker script:

_smysection = .;
*(.mysection)
*(.mysection*)
_emysection = .;

Just define a variable in C code:

const void * _smysection;

And then you can access that as a regular variable.

u32 someVar = (u32)&_smysection;
Aykut Kllic
  • 898
  • 9
  • 14
  • This was the answer I needed. I've been using linker sections for a while now but wanted to begin sub sectioning them (e.g. `.init.subsection`). The periods in the section name prevented the linker from proceed the generic `__start/stop` labels. But just providing the labels directly in the linker script was a solution to this problem. – sherrellbc Nov 19 '21 at 15:50
1

So the answer above, __start_SECTION and __stop_SECTION will work, however for the program to be able to use the information from the linker you to need to declare those variables as extern char* __start_SECTION. Enjoy!

extern char * __start_blobby;

...
printf("This section starts at %p\n", (unsigned int)&__start_blobby);
...
  • `extern __start_SECTION` works, I see `extern int __start_SECTION` and `extern char __start_SECTION` used the most. – yyny Jul 14 '20 at 22:55
  • @yyny in this case you need to take the address of the var e.g. `&__start_SECTION`. Otherwise you'll of course get the contents at the address and may find yourself very confused. Declaring the extern as a pointer (or array) prevents this. – sherrellbc May 14 '22 at 19:28
  • I think of it the other way around. If your variable has a pointer type, you might expect the section address to be in the variable value, whereas if your variable is a `char`, its more obvious that you have to take the address. Using an array type might be a good idea, though. – yyny May 16 '22 at 14:01
0

HI: like this.

extern const struct pseudo_ta_head __start_ta_head_section;
extern const struct pseudo_ta_head __stop_ta_head_section;    

const struct pseudo_ta_head *start = &__start_ta_head_section;
const struct pseudo_ta_head *end = &__stop_ta_head_section;
leesagacious
  • 182
  • 1
  • 8