Cómo obtener el valor de la variable definida en el script ld linker desde C

11 minutos de lectura

Estoy escribiendo un programa para ejecutar bare metal. Estoy tratando de obtener una variable de un script de enlace personalizado para usar en C, esto es lo que he intentado.

Desde C:

extern unsigned long* __START_OF_PROG_MEMORY;
volatile unsigned long *StartOfProgram = (unsigned long*) (&__START_OF_PROG_MEMORY);

Guión del enlazador:

SECTIONS
{
    . = 0x80000;
    PROVIDE(__START_OF_PROG_MEMORY = .);
    .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
    .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
    PROVIDE(_data = .);
    .data : { *(.data .data.* .gnu.linkonce.d*) }
    .bss (NOLOAD) : {
        . = ALIGN(16);
        __bss_start = .;
        *(.bss .bss.*)
        *(COMMON)
        __bss_end = .;
    }
    _end = .;
    PROVIDE(__END_OF_PROG_MEMORY = .);

   /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}
__bss_size = (__bss_end - __bss_start)>>3;

¿Es la forma correcta de obtener el contenido de la variable definida en el script del enlazador?

  • ¿Lo probaste y obtuviste algún error o simplemente lo preguntaste a ciegas?

    – Gaurav Pathak

    1 de febrero de 2018 a las 11:18

  • @Gaurav No, no obtuve errores, pero como esto se ejecuta sin sistema operativo, mis capacidades de depuración son limitadas.

    –Oliver fuerte

    1 de febrero de 2018 a las 22:03


Como obtener el valor de la variable definida en el
grapas gabriel

1. Documentación oficial para acceder a las variables linkerscript en su código fuente:

Ver ejemplos en la parte inferior de esta página:
https://sourceware.org/binutils/docs/ld/Source-Code-Reference.html

Por lo tanto, cuando utilice un símbolo definido por un script de vinculación en el código fuente, siempre debe tomar la dirección del símbolo y nunca intentar utilizar su valor. Por ejemplo, suponga que desea copiar el contenido de una sección de memoria llamada .ROM en una sección llamada .FLASH y el script del enlazador contiene estas declaraciones:

start_of_ROM   = .ROM;
end_of_ROM     = .ROM + sizeof (.ROM);
start_of_FLASH = .FLASH;

Entonces el código fuente en C para realizar la copia sería:

extern char start_of_ROM, end_of_ROM, start_of_FLASH;

memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);

Tenga en cuenta el uso de los operadores ‘&’. Estos son correctos. Alternativamente, los símbolos pueden tratarse como los nombres de vectores o matrices y luego el código volverá a funcionar como se esperaba:

[==> This is my preferred approach <==]:

extern char start_of_ROM[], end_of_ROM[], start_of_FLASH[];

memcpy (start_of_FLASH, start_of_ROM, end_of_ROM - start_of_ROM);

Tenga en cuenta que el uso de este método no requiere el uso de operadores ‘&’.

2. Su caso específico:

Entonces, si quisiera tomar el valor de la variable linkerscript __START_OF_PROG_MEMORY para usar en mi programa C, haría:

#include <stdint.h>

// linkerscript variable; NOT an array; `[]` is required to access a 
// linkerscript variable like a normal variable--see here: 
// https://sourceware.org/binutils/docs/ld/Source-Code-Reference.html
extern uint32_t __START_OF_PROG_MEMORY[];
// Read and use the `__START_OF_PROG_MEMORY` linkerscript variable
uint32_t start_of_program = (uint32_t)__START_OF_PROG_MEMORY;

3. Tenga en cuenta que si está haciendo esto para los microcontroladores STM32:

Otro truco para obtener la dirección del inicio de la memoria del programa (generalmente Flash, desde donde se almacena el inicio del programa) es simplemente obtener la dirección del g_pfnVectors matriz de tabla vectorial ISR global, que se define en su archivo de ensamblaje de inicio (por ejemplo: “startup_stm32f746xx.s“). Para hacer eso, haga lo siguiente:

// true array (vector table of all ISRs), from the startup assembly .s file
extern uint32_t g_pfnVectors[];  

// Get the starting address of where the application/program **is stored**
// **in flash memory**:

// (My preferred approach, as I find it more clear) Get the address of the 
// first element of this array and cast it to a 4-byte unsigned integer
uint32_t application_start_address = (uint32_t)&g_pfnVectors[0]; 
// OR (same thing as the line just above, just in a different way)
uint32_t application_start_address = (uint32_t)g_pfnVectors;

¡Voila! Es mágico :).

IMPORTANTE (muchos más detalles sobre los microcontroladores STM32):

  1. Ubicación de aplicación/programa almacenada en flash: hago no significar application_start_address ser el primer byte donde el programa comienza a correr (que es el contador de programa inicial (PC)), ni ¿Quiero decir que sea el primer byte donde comienza la memoria de la pila del programa? en RAM (que es el puntero de pila inicial (SP)). quiero decir que sea el primer byte en flash donde esta el programa almacenado. Gran diferencia aquí. En aras de la gestión de dos aplicaciones en destello memoria, para actualizaciones OTA (Over the Air), por ejemplo, estoy hablando de application_start_address siendo el primer lugar en destello donde esta el programa almacenado.

  2. Puntero de pila inicial (SP): si buscas el primer lugar en RAM donde el comienza la memoria de pilaesa ubicación de la dirección es almacenado en flash como el primera palabra (4 bytes) en el g_pfnVectors tabla vectorial global (nuevamente, generalmente en flash memoria), y se puede obtener, o leer, así:

    // Initial Stack Pointer (SP) value where the program stack begins.
    uint32_t initial_stack_ptr_location_in_ram = g_pfnVectors[0];
    

    Ver Manual de Programación PM0253pág. 42, Figura 10. Tabla de vectoresaquí (con algunas de mis notas adicionales en azul y resaltadas en amarillo):
    ingrese la descripción de la imagen aquí

  3. Contador de programa inicial (PC): y si está buscando el primer byte donde el programa comienza a correresa ubicación de la dirección es la Restablecer vector (que es una función de la forma void Reset_Handler(void)y se define en ensamblador aquí en el archivo de inicio) y esta dirección de función de 4 bytes es (normalmente) almacenado en flashcomo el 2da palabra (4 bytes) en el g_pfnVectors tabla vectorial global (nuevamente, qué tabla vectorial (matriz) suele en flash memoria; también: ver la imagen de arriba), y por lo tanto la dirección a la Reset_Handler() La función se puede obtener, o leer, de la g_pfnVectors matriz como esta:

    // The initial program run location (Program Counter (PC)), where the program 
    // begins to _run_, is the `Reset_Handler()` function, whose address is stored
    // as the 2nd word (index 1) of the `g_pfnVectors` array.
    uint32_t start_of_run_location_in_ram = g_pfnVectors[1];
    

    Vea la imagen de arriba, y el archivo de inicio .s y el script del enlazador .ld archivo de “carga” a continuación.

  4. Ensamblaje .s archivo de “inicio” y secuencia de comandos del enlazador .ld archivos de “carga”: y tenga en cuenta que el startup_stm32f767xx.s archivo de inicio coloca el g_pfnVectors matriz al comienzo de la .isr_vector sección, y la STM32F767ZITx_FLASH.ld script del enlazador almacena el .isr_vector sección como la primera cosa en FLASH. Esto significa que el primer byte de la aplicación, tal como se almacena en memoria flashes el primer byte del g_pfnVectors matriz de tabla vectorial global. Además, puede ver en el archivo de inicio anterior que el g_pfnVectors La matriz de tabla vectorial global almacena las siguientes palabras (4 bytes), en este orden:

    g_pfnVectors:
      .word  _estack
      .word  Reset_Handler
    
      .word  NMI_Handler
      .word  HardFault_Handler
      .word  MemManage_Handler
      .word  BusFault_Handler
      /* etc. etc. */
    

    Observe que el puntero de pila inicial (SP) se almacena como la primera palabra (4 bytes) y se establece como _estackque significa “final de la pila”, y es una dirección definido en el script del enlazador anterior. La segunda palabra es la dirección del Reset_Handler función, que se define aquí en el archivo de inicio y declarado aquí en el archivo de script del enlazador ser el punto de entrada del programa, o el comienzo del ubicación en tiempo de ejecución Del programa. la dirección de la Reset_Handler() función es por lo tanto el contador de programa inicial (PC). Así es como se establece como punto de entrada en el script del enlazador:

    /* Entry Point */
    ENTRY(Reset_Handler)
    
  5. Resumen: Repito, estamos hablando de 3 cosas separadas y distintas aquí:

    1. Ubicación del programa almacenado en flash: los start_of_programque es la ubicación de la dirección en flash donde esta el programa almacenado en flash. Léalo con:
      uint32_t application_start_address = (uint32_t)&g_pfnVectors[0];
      
    2. Puntero de pila inicial (SP): los initial_stack_ptr_location_in_ramque es la ubicación de la dirección en RAM donde comienza el puntero de pila, para que las variables se coloquen en tiempo de ejecución en el pila de programas. Léalo con:
      uint32_t initial_stack_ptr_location_in_ram = g_pfnVectors[0];
      
    3. Contador de programa inicial (PC): los start_of_run_location_in_ramque es la ubicación de la dirección (generalmente en flash, pero depende de la secuencia de comandos del enlazador y el archivo de inicio, ya que puede ejecutar todo el programa desde la RAM si lo desea, copiándolo desde la flash a la RAM al iniciar el programa, dentro de la parte superior de la archivo de inicio) donde el programa primero comienza a correr desdey en qué ubicación su Reset_Handler() se encuentra el vector (función “void(void)”). Para “reiniciar” su aplicación, debe hacer algunas cosas y luego llamar a esto Reset_Handler() función para comenzar a ejecutar su programa desde el principio. Lea la dirección de este Reset_Handler() función de la tabla vectorial global con:
      uint32_t start_of_run_location_in_ram = g_pfnVectors[1];
      
      1. Ir más lejos: o, si desea declarar esta dirección como un puntero de función y luego llamada es, puedes hacerlo así:
        typedef void (*void_void_func_t)(void);
        void_void_func_t reset_func = (void_void_func_t)g_pfnVectors[1];
        reset_func();
        

        O simplemente llame al Reset_Handler() func directamente:

        // Declare the existence of the function with a forward declaration 
        // since it's defined in the .s startup assembly file
        void Reset_Handler(void); 
        Reset_Handler();
        

        PERO: tenga en cuenta que no debe simplemente ir llamando a esta función de reinicio “willy nilly” cuando lo desee. Más bien, la documentación de STM32 indica en alguna parte que hay algunas cosas que debe hacer para preparar el chip para llamar al restablecimiento antes de llamar al restablecimiento. Entonces, haga esas pocas cosas primero, luego llame a la función de reinicio cada vez que desee reiniciar la aplicación. Tenga en cuenta también que otra forma (y probablemente más segura / fácil) de restablecer el microcontrolador es simplemente usar el perro guardián. Establezca el tiempo de espera del perro guardián al mínimo, apague todas las interrupciones y cosquillas del perro guardián, e ingrese un ciclo vacío infinito hasta que el perro guardián reinicie el chip.

Relacionados:

  • [My Q&A: See both the Question & Answer here for excellent info & more examples!] ¿El acceso al “valor” de una variable de secuencia de comandos del vinculador es un comportamiento indefinido en C?
  • [My Question] ¿Por qué los scripts del enlazador STM32 gcc descartan automáticamente todas las secciones de entrada de estas bibliotecas estándar: libc.a, libm.a, libgcc.a?

  • uint32_t application_start_address = (uint32_t)&g_pfnVectors[0]; está muy muy mal. No hace lo que piensas

    – 0___________

    18 mayo 2021 a las 21:01

  • @0___________, no estoy de acuerdo. Lo considero muy muy correcto. es lo mismo que uint32_t application_start_address = (uint32_t)g_pfnVectors;. Toma la dirección del inicio de la g_pfnVectors matriz, que es la tabla de vectores global para los diversos ISR de STM32, y la convierte en un número estándar con el que puedo hacer cálculos regulares o imprimir, según sea necesario, para poder realizar un seguimiento de las ubicaciones exactas de la dirección de inicio del programa en la memoria flash , ya que g_pfnVectors es primero. ¿Qué te hace decir que está “muy, muy mal”? Qué hacer Crees que creo ¿lo hace? ¿Cómo lo arreglarías o cambiarías?

    – Gabriel grapas

    18 mayo 2021 a las 22:30


  • La dirección de inicio de la aplicación no es la misma que la dirección de inicio de la tabla de vectores.

    – 0___________

    18 mayo 2021 a las 22:38

  • @0___________, seguro que lo es. los startup_stm32f767xx.s archivo de inicio coloca el g_pfnVectors matriz al comienzo de la .isr_vector sección, y la STM32F767ZITx_FLASH.ld script del enlazador almacena el .isr_vector sección como la muy primero cosa en FLASH.

    – Gabriel grapas

    18 mayo 2021 a las 23:04


  • los STM32F767ZITx_FLASH.ld el script del enlazador incluso tiene un comentario que dice: /* The startup code goes first into FLASH */y nuevamente, el archivo de inicio muestra que lo primero en el código de inicio es el g_pfnVectors matriz de tablas vectoriales. Entonces, g_pfnVectors es lo primero en flash, al comienzo de su aplicación.

    – Gabriel grapas

    18 mayo 2021 a las 23:07


1647714493 304 Como obtener el valor de la variable definida en el
yugr

Por lo general, se hace como

// Volatile is normally not needed but it seems you have a special case
extern unsigned char __START_OF_PROG_MEMORY[];
unsigned char * const StartOfProgram = &__START_OF_PROG_MEMORY;

(ver esta publicación en Binutils ML).

  • volátil no es necesario en absoluto. const unsigned char *StartOfProgram = __START_OF_PROG_MEMORY; es mucho mas correcto

    – 0___________

    18 mayo 2021 a las 21:02

  • @ 0___________ Traté de preservar el código del OP. Pero creo que su sugerencia es válida, así que hice los cambios.

    – yugr

    19 mayo 2021 a las 15:55


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad