Entendiendo el contador de ubicación de los scripts del enlazador GNU

Estoy trabajando en un proyecto universitario donde estoy escribiendo software desde cero para un microcontrolador Atmel SAM7S256. Esto es más profundo que otros MCU con los que he trabajado antes, ya que esta vez es necesario conocer los scripts de vinculador y el lenguaje ensamblador.

Realmente he estado examinando proyectos de ejemplo para los chips SAM7S a fin de comprender completamente cómo comenzar un proyecto SAM7 / ARM desde cero. Un ejemplo notable es el tutorial de Miro Samek, "Construyendo sistemas de BRAF de metal sencillo con GNU", encontradoaquí (de donde es el código en esta pregunta). También dediqué mucho tiempo a leer la documentación del enlazador y el ensamblador de sourceware.org.

Estoy muy contento de haber comprendido la siguiente secuencia de comandos de vinculador en su mayor parte. Solo hay una cosa relacionada con el contador de ubicación que no tiene sentido para mí. A continuación se muestra el script de enlace provisto con el tutorial anterior:

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_vectors)

MEMORY {                                       /* memory map of AT91SAM7S64 */
    ROM (rx)  : ORIGIN = 0x00100000, LENGTH = 64k
    RAM (rwx) : ORIGIN = 0x00200000, LENGTH = 16k
}

/* The sizes of the stacks used by the application. NOTE: you need to adjust */
C_STACK_SIZE   = 512;
IRQ_STACK_SIZE = 0;
FIQ_STACK_SIZE = 0;
SVC_STACK_SIZE = 0;
ABT_STACK_SIZE = 0;
UND_STACK_SIZE = 0;

/* The size of the heap used by the application. NOTE: you need to adjust   */
HEAP_SIZE = 0;

SECTIONS {

    .reset : {
        *startup.o (.text)  /* startup code (ARM vectors and reset handler) */
        . = ALIGN(0x4);
     } >ROM

    .ramvect : {                        /* used for vectors remapped to RAM */
        __ram_start = .;
        . = 0x40;
    } >RAM

    .fastcode : {
        __fastcode_load = LOADADDR (.fastcode);
        __fastcode_start = .;

        *(.glue_7t) *(.glue_7)
        *isr.o (.text.*)
        *(.text.fastcode)
        *(.text.Blinky_dispatch)
        /* add other modules here ... */

        . = ALIGN (4);
        __fastcode_end = .;
    } >RAM AT>ROM

    .text : {
        . = ALIGN(4);
        *(.text)                                   /* .text sections (code) */
        *(.text*)                                 /* .text* sections (code) */
        *(.rodata)           /* .rodata sections (constants, strings, etc.) */
        *(.rodata*)         /* .rodata* sections (constants, strings, etc.) */
        *(.glue_7) /* glue arm to thumb (NOTE: placed already in .fastcode) */
        *(.glue_7t)/* glue thumb to arm (NOTE: placed already in .fastcode) */

        KEEP (*(.init))
        KEEP (*(.fini))

        . = ALIGN(4);
        _etext = .;                         /* global symbol at end of code */
    } >ROM

    .preinit_array : {
        PROVIDE_HIDDEN (__preinit_array_start = .);
        KEEP (*(SORT(.preinit_array.*)))
        KEEP (*(.preinit_array*))
        PROVIDE_HIDDEN (__preinit_array_end = .);
    } >ROM

    .init_array : {
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array*))
        PROVIDE_HIDDEN (__init_array_end = .);
    } >ROM

    .fini_array : {
        PROVIDE_HIDDEN (__fini_array_start = .);
        KEEP (*(.fini_array*))
        KEEP (*(SORT(.fini_array.*)))
        PROVIDE_HIDDEN (__fini_array_end = .);
    } >ROM

    .data : {
        __data_load = LOADADDR (.data);
        __data_start = .;
        *(.data)                                          /* .data sections */
        *(.data*)                                        /* .data* sections */
        . = ALIGN(4);
        _edata = .;
    } >RAM AT>ROM

    .bss : {
        __bss_start__ = . ;
        *(.bss)
        *(.bss*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;                     /* define a global symbol at bss end */
        __bss_end__ = .;
    } >RAM

    PROVIDE ( end = _ebss );
    PROVIDE ( _end = _ebss );
    PROVIDE ( __end__ = _ebss );

    .heap : {
        __heap_start__ = . ;
        . = . + HEAP_SIZE;
        . = ALIGN(4);
        __heap_end__ = . ;
    } >RAM

    .stack : {
        __stack_start__ = . ;

        . += IRQ_STACK_SIZE;
        . = ALIGN (4);
        __irq_stack_top__ = . ;

        . += FIQ_STACK_SIZE;
        . = ALIGN (4);
        __fiq_stack_top__ = . ;

        . += SVC_STACK_SIZE;
        . = ALIGN (4);
        __svc_stack_top__ = . ;

        . += ABT_STACK_SIZE;
        . = ALIGN (4);
        __abt_stack_top__ = . ;

        . += UND_STACK_SIZE;
        . = ALIGN (4);
        __und_stack_top__ = . ;

        . += C_STACK_SIZE;
        . = ALIGN (4);
        __c_stack_top__ = . ;

        __stack_end__ = .;
    } >RAM

    /* Remove information from the standard libraries */
    /DISCARD/ : {
        libc.a ( * )
        libm.a ( * )
        libgcc.a ( * )
    }
}

A lo largo del ejemplo (como en las secciones .ramvect, .fastcode y .stack) hay definiciones de símbolos como__ram_start = .;. Estas direcciones son utilizadas por el código de ensamblaje de inicio y el código C de inicialización para inicializar las ubicaciones correctas en la RAM de la MCU.

Lo que tengo un problema de comprensión, es cómo estas definiciones de símbolos dan como resultado que se asignen los valores correctos. Esto sucede, el script es correcto, pero no entiendo cómo.

De la manera que lo entiendo, cuando usa el contador de ubicación dentro de una sección, solo contiene un desplazamiento relativo de la dirección de memoria virtual (VMA) de la sección en sí.

Así por ejemplo, en la línea__ram_start = .;, Esperaría que a __ram_start se le asigne un valor de 0x0, ya que se le asigna el valor del contador de ubicación al comienzo de la sección .ramvect. Sin embargo, para que el código de inicialización funcione correctamente (lo que hace), __ram_start debe asignarse como 0x00200000 (la dirección para el comienzo de la RAM).

Habría pensado que esto solo funcionaría como estaba previsto si la línea fuera__ram_start = ABSOLUTE(.); o__ram_start = ADDR(.ramvect);.

Lo mismo va para__fastcode_start y__stack_start__. No todos pueden definirse como dirección 0x0, de lo contrario el programa no funcionaría. Pero la documentación.enlazado aquí Parece sugerir que eso es lo que debería estar pasando. Aquí está la cita de la documentación:

Nota: . en realidad se refiere al desplazamiento de bytes desde el inicio del objeto que contiene actual. Normalmente esta es la instrucción SECTIONS, cuya dirección de inicio es 0, por lo tanto. Puede ser utilizado como una dirección absoluta. Si . se utiliza dentro de una descripción de sección, sin embargo, se refiere al desplazamiento de bytes desde el inicio de esa sección, no a una dirección absoluta.

Por lo tanto, los valores de los contadores de ubicación durante esas asignaciones de símbolos deben ser compensados ​​de la sección VMA correspondiente. Así que esos símbolos "_start" deberíantodos se está poniendo a 0x0. Lo que rompería el programa.

Así que obviamente me estoy perdiendo algo. Supongo que podría ser simplemente que la asignación del valor del contador de ubicación a un símbolo (dentro de una sección) resulte en que ABSOLUTE () se use de forma predeterminada. Pero no he podido encontrar una explicación clara en ninguna parte que confirme esto.

Gracias de antemano si alguien puede aclarar esto.

Respuestas a la pregunta(2)

Su respuesta a la pregunta