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.
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 🙂
Thanks Erich ! As always, your articles are clear and understandable, and provide excellent practical advice.
LikeLike
You are welcome!
LikeLike
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 !
LikeLike
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
LikeLike
Thank you Erich for the complementary info. Now it make sense to me.
Have a nice day !
LikeLike
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
LikeLike
Yes, you can export any linker symbols you like, you just have to add this to the linker scripts and define it there.
LikeLike
Thanks Erich,
but I would like the GNU linker to automatically compute memory section size.
I mean:
__MY_SECTION_SIZE is __MY_SECTION_END – __MY_SECTION_START, but I’d like this calculus to be computed by the linker, not the CPU.
In C I would like to define only:
extern int __MY_SECTION_SIZE;
extern int __MY_SECTION_END;
extern int __MY_SECTION_START;
Shall (and how?) I modify the linker script to achieve that?
Thanks again
Roberto
LikeLike
Ok, I’ll write a short article then.
LikeLike
Just seen it. 🙂
Thank you very much indeed!
LikeLike
Hi Roberto,
have a read here: https://mcuoneclipse.com/2017/09/06/using-the-gnu-linker-script-to-know-the-flash-and-ram-areas-in-the-application/
I hope this helps,
Erich
LikeLike
Pingback: Using the GNU Linker Script to know the FLASH and RAM Areas in the Application | MCU on Eclipse
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.
LikeLike
It happens on -Wall, see also this https://stackoverflow.com/questions/3154807/how-to-get-the-workaround-for-gcc-warning-the-address-of-xxx-will-never-be-nul
LikeLike
not sure how your pointer is declared, but volatile should fix it:
uint8_t *volatile p;
p= (uint8_t*volatile)&__MY_SECTION_SIZE;
if (p!=0) { /* will generate test as because it is a volatile pointer */
LikeLike
Hi Erich,
Thank you for the guide. It saved lot of time for me.
Regards,
Soma Konijeti
LikeLike
you are welcome! glad to hear it was helpful 🙂
LikeLike