¿Cómo sabe avr-gcc linker para poner la sección '.data's' en '0x800100' en lugar de '0x800060'?

2

Mirando la secuencia de comandos del vinculador de mi parte (ATMEGA168PD), la región data tiene un origen definido como que comienza en 0x800060 ...

MEMORY
{
  text   (rx)   : ORIGIN = 0, LENGTH = __TEXT_REGION_LENGTH__
  data   (rw!x) : ORIGIN = 0x800060, LENGTH = __DATA_REGION_LENGTH__
  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = __EEPROM_REGION_LENGTH__
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = __FUSE_REGION_LENGTH__
  lock      (rw!x) : ORIGIN = 0x830000, LENGTH = __LOCK_REGION_LENGTH__
  signature (rw!x) : ORIGIN = 0x840000, LENGTH = __SIGNATURE_REGION_LENGTH__
  user_signatures (rw!x) : ORIGIN = 0x850000, LENGTH = __USER_SIGNATURE_REGION_LENGTH__
}

La sección de salida .data no define explícitamente una dirección, sino que solo se refiere a la región data ...

  .data          :
  {
     PROVIDE (__data_start = .) ;
    *(.data)
     *(.data*)
    *(.gnu.linkonce.d*)
    *(.rodata)  /* We need to include .rodata here if gcc is used */
     *(.rodata*) /* with -fdata-sections.  */
    *(.gnu.linkonce.r*)
    . = ALIGN(2);
     _edata = . ;
     PROVIDE (__data_end = .) ;
  }  > data AT> text

... sin embargo, la sección de salida .data termina en la dirección 0x800100 en lugar de 0x800060 ...

00800100 l    d  .data  00000000 .data

Esta resulta ser la dirección correcta para el inicio del segmento .data en esta parte porque 0x800060-0x8000ff está reservado para algunos registros y no es un RAM de propósito general.

No hay ningún origen en el script del vinculador que especifique la dirección 0x800100 y no hay parámetros de línea de comando para el vinculador que le dirían este número mágico, específico de la parte. No hay otras secciones de salida en la región data que puedan empujar la sección de salida .data en la memoria a la ubicación 0x800100 ( .bss y .noinit siguen .data y están relacionadas con ella) .

También he notado que si cambio el nombre de la sección .data out a cualquier otra cosa, su origen se encuentra al principio de la región data en 0x800060 como se esperaba. De alguna manera, el enlazador sabe que una sección de salida específicamente llamada .data debe comenzar específicamente origin 0x800100 en lugar del comienzo de la región especificada en el script del vinculador.

Es como si en algún lugar hubiera .data = 0x800100; o -Tdata=0x80010 , ¡pero no puedo encontrarlo en ninguna parte!

Mi pregunta es: ¿cómo sabe el vinculador iniciar las direcciones .data en 0x800100 para esta parte?

    
pregunta bigjosh

2 respuestas

1

No pude averiguar de dónde venía el valor de 0x800100 para el origen de .data , así que como una solución, comenté la región data estándar y en su lugar definí una nueva región llamada dataX que comenzó al principio de la memoria RAM utilizable ...

MEMORY
{
  text   (rx)   : ORIGIN = 0, LENGTH = __TEXT_REGION_LENGTH__
 /* data   (rw!x) : ORIGIN = 0x800060, LENGTH = __DATA_REGION_LENGTH__ */
  dataX   (rw!x) : ORIGIN = 0x800100, LENGTH = __DATA_REGION_LENGTH__ 
  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = __EEPROM_REGION_LENGTH__
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = __FUSE_REGION_LENGTH__
  lock      (rw!x) : ORIGIN = 0x830000, LENGTH = __LOCK_REGION_LENGTH__
  signature (rw!x) : ORIGIN = 0x840000, LENGTH = __SIGNATURE_REGION_LENGTH__
  user_signatures (rw!x) : ORIGIN = 0x850000, LENGTH = __USER_SIGNATURE_REGION_LENGTH__
}

Luego cambié todas las secciones que solían referirse a la región data para referirme a esta nueva región al mismo tiempo que cambiaba el nombre de la sección de salida .data a .dataX para que no se bloquee el valor predefinido ...

  .dataX    : 
  {
     PROVIDE (__data_start = .) ;
    *(.data)
     *(.data*)
    *(.gnu.linkonce.d*)
    *(.rodata)  /* We need to include .rodata here if gcc is used */
     *(.rodata*) /* with -fdata-sections.  */
    *(.gnu.linkonce.r*)
    . = ALIGN(2);
     _edata = . ;
     PROVIDE (__data_end = .) ;

  }  > dataX AT> text


  __data_load_start = LOADADDR(.dataX);
   __data_load_end = __data_load_start + SIZEOF(.dataX);

  /* Global data not cleared after reset.  */

  .noinit    :  
  {
     PROVIDE (__noinit_start = .) ;
    *(.noinit*)
     PROVIDE (__noinit_end = .) ;
     _end = . ;
     PROVIDE (__heap_start = .) ;
  }  > dataX

Noté que la documentación ld dice ...

  

La dirección de carga de la sección se establece en la siguiente dirección libre en el   región, alineada con los requisitos de alineación de la sección.

... así que también saqué todos los cálculos de origen para todas estas secciones de entrada que se parecían a esto ...

.noinit ADDR(.bss) + SIZEOF (.bss) : AT (ADDR (.noinit))

... ya que estos cálculos son innecesarios (y de hecho se rompen cuando se cambia el orden de las secciones). En cambio, ahora el script solo permite que el enlazador asigne automáticamente cada sección a la siguiente dirección alineada en la región solicitada.

Todo funciona ahora correctamente, las secciones en la región data ahora comienzan siempre con el comienzo real de la RAM, y la secuencia de comandos no se rompe cuando se cambian las secciones.

Dicho esto, por curiosidad, todavía me encantaría entender de dónde proviene ese origen de 0x800100 para .data , si lo sabes.

    
respondido por el bigjosh
1

Lo encontré! Es en el archivo especificaciones del dispositivo que gcc aparentemente se activa automáticamente en función de la configuración -mmcu . En mi caso, el archivo ATMEGA168PB incluido ...

*link_data_start:
    -Tdata 0x800100

... que anula la región de memoria en el script del vinculador.

La documentación completa de estos archivos de especificaciones del dispositivo está aquí ...

enlace

    
respondido por el bigjosh

Lea otras preguntas en las etiquetas