When an application normally runs on a desktop machine it's generally running using virtual memory in a virtual application-specific address space. From the applications point of view it can manipulate its own memory however it wants - and it's the operating system that then translates that into safe operations on the actual memory (for instance to insure that the applications don't touch any memory region they shouldn't)

On a microcontroller by default there is no operating system to manage the memory and the memory is shared with other functionality - some addresses are reserved for peripherals, other addresses are for interrupts and reset bits, the stack and heap are allocated in some device-specific place and there is also a split between ROM and RAM.

Because of these new limitations we can't just start executing code at address zero or drop in a main() function somewhere randomly and start there, we need to tell the linker what the code layout is though a custom linker script. First we tell it which parts correspond to ROM and RAM and what will be their respective sizes. ROM (Read Only Memory) is where the code and constants live, and RAM (Random Access Memory) is where the stack and heap live - the stuff that's dynamic.

MEMORY { rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K } PROVIDE (_stack = ORIGIN(ram) + LENGTH(ram));

Even give that, code on a microcontroller doesn't by default start at the first address of ROM and go from there. On a desktop program you generally have an entry point (a main() ) and an exit point (ex: exit 0 ). But a better way to think about the way a microcontroller works is it's as a machine that recieves interrupt signals from external inputs and the chip responds by running code and then returns to whatever it was doing before. These interrupting inputs can be a clock running out, a peripheral wanting attention, an attached debugger wanting to pause everything, etc. There is no real starting point nor an "exit"

So instead of a main() or something, the first thing in ROM is a vector table - a table of pointers to the different interrupt handlers. These handlers are the code that is run when each interrupt happens. The first interrupt handler in this table will be special and it's the one that is triggered when the system is powered on, the user presses the reset button, or the code runs out of things to do. It's appropriately called the reset handeler

EXTERN (vector_table); ENTRY (reset_handler);

These two are just symbols for the linker - and it will look for them later in the actual code it's linking. So we need to not forget to define them :)

Lastly we need to tell the linker that we want those vectors first in the ROM and aligned at the byte level (b/c the micro reads things in byte sized chucks)

SECTIONS { .text : { *(.vectors) *(.text*) . = ALIGN(4); } >rom }

So the chip doesn't need to hunt for the vector table. It's always in the same spot at the start of the ROM - and the reset handler is as well.