Accessing GNU Linker Script Symbols from C/C++

With the GNU compiler and linker I can place variables into custom sections (see “Defining Variables at Absolute Addresses with gcc“). This article is about how to get the section start and end address so I can for example access that range in my code. Or in general ways: how to use symbols defined in the linker script accessible in the C source code.

Using Linker Script Symbols in Source Code

Using Linker Script Symbols in Source Code

Outline

The GNU Linker (ld) is very powerful, and with its script language nearly anything can be scripted. In “Defining Variables at Absolute Addresses with gcc” I used it to allocate variables into a special section. How can I know the start end the end address of such a section, so I can use it in my sources?

Custom Sections

I have several variables placed into a custom section “.mySection” in my sources:

unsigned char __attribute__((section (".mySection"))) buf[12];
unsigned char __attribute__((section (".mySection"))) myOtherVar;

Linker Script Extra Symbols

In the GNU ld linker script I have put them into the m_data like this:

/* placing my named section at given address: */
.mySectionBlock  :
{
  __MY_SECTION_START = .;  /* create symbol for start of section */
  KEEP(*(.mySection)) /* keep my variable even if not referenced */
  __MY_SECTION_END = .; /* create symbol for end of section */
} > m_data

I have added two extra symbols: __MY_SECTION_START and __MY_SECTION_END, just at the beginning and the end of the are.

The syntax

NAME = .;

means: “Assign to the symbol NAME the current address”. The current address is noted with the dot (“.”). That way the linker creates this two symbols marking the start and end address. I can verify this in the linker map file:

.mySectionBlock
                0x1fff0064       0x10 load address 0x000006b0
                0x1fff0064                __MY_SECTION_START = .
 *(.mySection)
 .mySection     0x1fff0064       0x10 ./Sources/main.o
                0x1fff0064                buf
                0x1fff0070                myOtherVar
                0x1fff0074                __MY_SECTION_END = .

Accessing Linker Symbols from C/C++

In my sources, I have to add a declaration of these linker created symbols:

extern int __MY_SECTION_START, __MY_SECTION_END;

Because these are really symbols, and not normal variables (with memory associated), I have to use the address (&) operator to get their address or location:

    uint8_t *p, *end;

    p = (uint8_t*)&__MY_SECTION_START;
    end = (uint8_t*)&__MY_SECTION_END;

With that I have the start and end address of that memory section. Below is an example program which initialize the section memory with a 0, 1, 2, 3, 4, …:

static int i = 0;

unsigned char __attribute__((section (".mySection"))) buf[12];
unsigned char __attribute__((section (".mySection"))) myOtherVar;

extern int __MY_SECTION_START, __MY_SECTION_END;

int main(void)
{
    uint8_t *p, *end;

    i = 0;
    p = (uint8_t*)&__MY_SECTION_START;
    end = (uint8_t*)&__MY_SECTION_END;
    while(p<end) {
      *p++ = i++; /* initialize with pattern */
    }
    for (;;) {
    }
    /* Never leave main */
    return 0;
}

Summary

I can define symbols in my linker script. I can use them in my code. For this, I have to declare them as ‘extern’ and using the address operator to get the address of such a symbol.

Happy Linking 🙂

Links

21 thoughts on “Accessing GNU Linker Script Symbols from C/C++

  1. Hi Erich,

    Could you please elaborate a little about the difference between accessing the variables directly from the C code (i.e. buf[i] = i++;) and accessing them through their absolute address (your example) ? I can’t think of a situation where it would be useful, but I’m sure it has some advantages !

    Thank you for all your posts, they are very helpful !

    Like

    • Hi Carl,
      accessing it throught the normal C/C++ symbol is the usual way you should do it. Because that way the compiler ensures proper alignment and optimization (if any).
      If you access them in a ‘raw’ way, then you have to care about this yourself (e.g. proper pointer alignment).
      Accessing it with their absolute addresses makes sense for cases where the program has not much if any information about that symbol. Things like a bootloader or a crash recovery program where it needs to read from raw memory without much information.
      I hope this helps,
      Erich

      Like

  2. Hi Erich,
    thanks a lot as always.
    You are an ever lasting mine! 🙂

    Anyway: do you know if the GNU linker exports the size of segments/sections as other ones do?

    I mean: does __MY_SECTION_SIZE exist?
    Referring to your example
    uint8_t *p;
    p = (uint8_t*)&__MY_SECTION_SIZE;

    Thank you very much again
    Roberto

    Like

  3. Pingback: Using the GNU Linker Script to know the FLASH and RAM Areas in the Application | MCU on Eclipse

  4. Erich,

    Thanks for this!
    One issue I face (I’m using gcc for e200) is the fact that compiler knows an address of a sym cannot be NULL, thus it does not emit code (even on O0) for following piece of code:

    p = (uint8_t*)&__MY_SECTION_SIZE;
    […]
    if (p != 0) //code for this test will NOT be generated
    {…}

    Any solution you already know for this, please?

    Thanks,
    Cristian.

    Like

  5. .mySectionBlock :
    {
    KEEP(*(.mySection*))
    } > m_data

    unsigned char __attribute__((section (“.mySectionA”))) buf[12];
    unsigned char __attribute__((section (“.mySectionB”))) myOtherVar;

    How to get start and end address of mySectionA or mySectionB ?

    Like

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.