A typical LD script consists of two sections - one that defines memory ("MEMORY") regions, and another that defines what to put in those memory regions ("SECTIONS").
To carve holes in an existing memory layout it is usually enough to just change the first section - the part that defines the layout of the memoy. Shrink and move existing blocks of memory and insert new ones.
For example you may have this at the moment:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000+0x2000, LENGTH = 0x00040000-0x2000 /* First 8KB used by bootloader */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
There is a hole already defined of size 0x2000 at the start of flash, and the sketch is loaded from 0x2000 onwards.
You may want to shrink and move that, and add a "user data" chunk. It's enough to just go:
MEMORY
{
USERDATA (rx) : ORIGIN = 0x2000, LENGTH = 0x1000
FLASH (rx) : ORIGIN = 0x3000, LENGTH = 0x00040000-0x3000
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
(I collapsed some of the maths cus it's pointless). Now the flash area (where the sketch goes) has been shrunk by 0x1000 and moved an extra 0x1000 up the memory map. A USERDATA chunk has been inserted which you can then use later on if you so wish.
To make things get put in there by the linker script (you don't need to change anything else to move the sketch data...) you can define a new section in the later part of the linker script:
.userdata :
{
KEEP(*(.userdata))
KEEP(*(.userdata.*))
} > USERDATA
That should then place anything that you put in a .userdata
section (or a subsection within that) within your sketch into the USERDATA chunk of flash - separate from your sketch.
(sketch)
const uint8_t __attribute__((section(".userdata"), used)) data[] = { 10, 100, 123, 177, 221, 49 };
However, if you plan to use it like EEPROM and only write to it using code in your sketch then you don't need to worry about anything like that - just carve out the hole. You can also provide some globals to help with your code, though:
(linker script)
PROVIDE(userdata_start = ORIGIN(USERDATA))
PROVIDE(userdata_length = LENGTH(USERDATA))
Then you can use those in your program to know where the USERDATA chunk is and how big it is (ideal if you ever choose to change it - no need to change your code, only the linker script).