Is writing to a section not defined in linker file allowed?

In my linker file, I have the following memory sections.

MEMORY
{
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k

    mram (rwx) : ORIGIN = 0xA0000010, LENGTH = 1M
}

The actual address of the mram peripheral starts at 0xA0000000. In a C file I can write to a specific memory address as

 (*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;

Will this cause any problems?


Solution 1:

The space specified in the linker script defines where and what the linker can locate in the defined memory regions. It will not locate anything to the "hole" you have left at 0xA0000000 to 0xA000000F because it is not aware of it.

In that sense it is "safe" in that the linker will not attempt to use that space. It is entirely in the control of your code - you have taken responsibility for that region by not giving it to the linker. And indeed the statement:

(*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;

will write a 32 bit value to that location. The point is neither the compiler nor the linker will prevent you from doing what you will in that region.

What is less plausible is LENGTH = 1M. That would make your mram 0x100010 bytes long (i.e. 1M + 0x10). That is a problem because the linker is free to locate objects in the region 0x100000 to 0x10000F. The consequences of that depend on your hardware, but quite possibly it will wrap into the region 0x100000 to 0x10000F that you have attempted to hide from the linker. I would imagine that you need LENGTH = 1M - 0x10 or LENGTH = 0x0FFFF0.

Now while you can absolve the linker from managing that region in order to manage it in your code, it may not be the best approach. It would be better to create a linker symbol at the required absolute address.

So given:

MEMORY
{
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k

    mram (rwx) : ORIGIN = 0xA0000000, LENGTH = 1M
} 

Your would create a linker symbol in mram:

SECTIONS
{
    ...

    .mram :
    {
        reserved_mram = 0 ; /* address is zero offset from .mram1 */
        . += 0x10 ;         /* create 16 byte "hole" at address reserved_mram */

        ...  /* other mram linker assignment follows */

    } < mram

    ...
}

Then in your code you should be able to declare:

extern uint32_t reserved_mram[] ;  // 4 x 32-bit word array.

And through reserved_mram you can access the memory at 0xA00000000 symbolically and the code is always in sync with the linker script so you can relocate the space easily without introducing a conflict.

Of course there is no bounds checking and no size information - you still need to confine your access to reserved_mram[0] to reserved_mram[3].

You might alternatively create a separate symbol for each location (with meaningful names specific to your application):

    .mram :
    {
        reserved_mram1 = 0 ;
        . += 4 ;
        reserved_mram2 = . ;
        . += 4 ;
        reserved_mram3 = . ;
        . += 4 ;
        reserved_mram4 = . ;
        . += 4 ;

        ...  /* other mram linker usage follows */

    } < mram

Then in your code:

extern uint32_t reserved_mram1 ;
extern uint32_t reserved_mram2 ;
extern uint32_t reserved_mram3 ;
extern uint32_t reserved_mram4 ;

Another alternative; you might create an independent section for the region, then create variables within it using __attribute__((section(.xxx))) directives in the code. e.g:

MEMORY
{
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k

    mram1 (rwx) : ORIGIN = 0xA0000000, LENGTH = 0x10
    mram2 (rwx) : ORIGIN = 0xA0000010, LENGTH = 0xFFFF0
} 

SECTIONS
{
    ...

    .mram1 :
    {
        *(.mram1) 
    } < mram1

    ...
}

Then in your code:

uint32_t my_mram_data __attribute__ ((section (".mram1"))) ;

The variable my_mram_data will be created somewhare in .mram1, but the linker decides where. The advantage here is that you can create arbitrary variables in the code without modifying the linker script, and if you attempt to allocate to .mram1 more data than is available you will get a linker error.

Note that linker script syntax is arcane and varies between linkers - I am unassuming this relates to the GNU linker? But my linker foo is strictly on demand (i.e. I figure it out when I need to) and I make no claim that any of the above is complete or correct, or even the only possible solutions - regard it as illustrative, and refer to the linker documentation for accurate information.