Trato de aportar un granito de arena a la descripción de KillerJC.
El compilador maneja nombres de sectores "estandar" .text, .data, .bss donde se guarda el código compilado y los valores de las constantes empleadas en nuestro programa.
El compilador cuando toma un archivo .c genera un archivo objeto .o, y hace (según se le instruya con el archivo make, opciones, línea de comandos...) lo mismo para todos los .c generando para cada uno un .o
Luego entra a jugar el enlazador o linker, porque hasta este punto el compilador genera código/variables que tienen direcciones físicas de memoria no resueltas.
Por ejemplo si en mi archivo .c tengo:
#include <blablabla.h>
extern int unaVariableEnAlgunLugar;
//resto del programa.
El compilador dice "ajá... el programador me dice que en algún otro archivo .c está definida esa variable, por lo tanto no la agrego al conteo de memoria que hago para éste archivo c que estoy compilando ahora, me lo anoto como símbolo sin resolver".
Es decir, le deja la tarea al enlazador agregando una entrada en una tabla de símbolos sin resolver.
El enlazador junta todos los archivos .o que generó el compilador, todavía no tiene direcciones de memoria resueltas ni para las funciones ni para las variables o constantes, excepto que se haya especificado con un atributo especial.
Y además toma el archivo linker o linker script, que puede ser un .cmd o .ld ...o algo así

Y que tiene el archivo de linker script?:
1) las direcciones físicas (en hexadecimal 0xNNNNNN) de los vectores de interrupción (reset, periféricos: puertos, conversor AD, etc), también puede estar definido en los .h y no en linker script
2) los registros del micro (puertos, dirección de puertos, registros del uart/i2c/spi... de todos los registros de propósito especial SFR aunque a veces pueden estar en los .h y no en el linker script),
3) los segmentos de memoria (a partir de que dirección comienza la RAM y que longitud tiene, es decir, que rango de memoria; ídem para memoria flash, eeprom si la hubiera),
Nota: segmento de memoria != sector de memoria, el sector memoria .data va en el segmento de memoria RAM por ejemplo
4) la pila que es lo que usa C para llamar y retornar de las funciones, pasar parámetros
5) el heap si algún valiente se mete con memoria dinámica (malloc)
Y alguna cosa más que o bien no conozco, o se me pasó por alto (sector de inicialización como dijo KillerJC).
Eso es lo que hace el enlazador, resuelve todas las referencias a memoria que es nada más y nada menos decir que la variable "miVariable" tiene la dirección 0x1ab por ejemplo.
Se hace notar cuando tira un error "unresolved reference to...." que significa que no encontró una variable o función que yo le dije que iba a estar en otro archivo usando extern (o a través de un include).
En el linker script si uno quisiera se podría agregar un sector de memoria definido a mano para un propósito especial, por ejemplo una zona de datos de calibración que no quiero que cambie al actualizar el programa del micro, o un sector de memoria para un bootloader (de hecho si un micro usa un bootloader seguro su linker script para nuestro programa excluirá el sector de memoria que usa el bootloader, o por lo menos debería).
Bueno, eso es todo, no quería dejar en la oscuridad al enlazador/linker.