Cómo colocar una variable en una dirección absoluta dada en la memoria (con GCC)

7 minutos de lectura

avatar de usuario
Bas van Dijk

El compilador RealView ARM C apoya colocar una variable en una dirección de memoria dada usando el atributo variable at(address):

int var __attribute__((at(0x40001000)));
var = 4;   // changes the memory located at 0x40001000

¿Tiene GCC un atributo de variable similar?

  • escribí un artículo, donde enumero los medios para hacerlo. Podría ser útil para algunos.

    – a3f

    8 mayo 2016 a las 21:13

avatar de usuario
Profesor Falken

No lo sé, pero puedes crear fácilmente una solución como esta:

int *var = (int*)0x40001000;
*var = 4;

Que no es exactamente lo mismo, pero en la mayoría de las situaciones un sustituto perfecto. Funcionará con cualquier compilador, no solo con GCC.

Si usa GCC, supongo que también usa código GNU (aunque no es una certeza, claro) y ld tiene soporte para colocar variables donde los quieras.

Me imagino que dejar que el enlazador haga ese trabajo es bastante común.

Inspirado por la respuesta de @rib, agregaré que si la dirección absoluta es para algún registro de control, agregaría volatile a la definición del puntero. Si es solo RAM, no importa.

  • @roe: es un truco bastante estándar que se puede usar en controladores de dispositivos para hardware con registros de control asignados a memoria fija. En una aplicación de usuario estándar, no tiene ninguna utilidad que se me ocurra.

    – JeremyP

    1 de noviembre de 2010 a las 9:46

  • @JeremyP, en dispositivos integrados, especialmente aquellos sin MMU, es muy común dejar que las “aplicaciones de usuario” golpeen el hardware.

    – Profesor Falken

    1 de noviembre de 2010 a las 9:47

  • @JeremyP; ese era más o menos mi punto, la pregunta no indica si se puede acceder a la memoria de esta manera, o si necesita que el compilador tome ciertas acciones para que esto suceda.

    – falstro

    1 de noviembre de 2010 a las 9:59

  • @Prof.Falken: Creo que ciertas cosas son desafortunadas; Los compiladores solían exponer el comportamiento subyacente del hardware para la mayoría de las formas de UB, mientras que hoy en día existe una mayor tendencia a realizar “optimizaciones” basadas en UB. solía ser eso (x<<n) | (x>>(32-n)) era la forma canónica de hacer una “rotación a la izquierda”, y solía funcionar en el 99,999 % de las plataformas donde x podía ser un tipo de 32 bits sin firmar (no conozco excepciones aparte de las configuradas para un modo excepcionalmente pedante) pero algunas de los compiladores actuales examinarán esa expresión e inferirán que x no puede ser cero. Históricamente sólo hubo…

    – Super gato

    27/10/2015 a las 14:31


  • Cambiaría el tipo del puntero a int *constpor lo que el compilador puede optimizar la desreferencia.

    – a3f

    8 mayo 2016 a las 21:17

Podrías usar el atributos de la sección y un viejo secuencia de comandos del enlazador para definir la dirección deseada para esa sección. Esto es probablemente más complicado que sus alternativas, pero es una opción.

  • Tenga en cuenta que este enfoque en realidad reservar espacio para la variable, en lugar de simplemente asumir que existe en la dirección especificada. En muchos casos, esto es lo que quieres.

    usuario1896626

    29 de enero de 2015 a las 6:03


  • Un enlace más directo para los atributos de la sección: gcc.gnu.org/onlinedocs/gcc/… y ctrl+f para “sección”

    – Ponkadoodle

    31 de julio de 2015 a las 20:56

  • Desafortunadamente, a veces las herramientas intentan simplificar las cosas ocultando/generando automáticamente el script del enlazador, por lo que es difícil modificarlo (te estoy mirando Arduino y ATMEL Studio). Me encantaría encontrar una manera de anclar una variable a una dirección fija y hacer que asigne el espacio en un segmento existente usando solo código. :/

    – gran josh

    11 de noviembre de 2018 a las 18:18

  • He proporcionado un ejemplo de script de enlazador ejecutable mínimo en: stackoverflow.com/questions/4067811/…

    – Ciro Santilli Путлер Капут 六四事

    10 de enero de 2019 a las 12:21

avatar de usuario
Ciro Santilli Путлер Капут 六四事

Ejemplo de script de enlazador ejecutable mínimo

La técnica se mencionó en: https://stackoverflow.com/a/4081574/895245 pero ahora proporcionaré un ejemplo concreto.

C Principal

#include <stdio.h>

int myvar __attribute__((section(".mySection"))) = 0x9ABCDEF0;

int main(void) {
    printf("adr %p\n", (void*)&myvar);
    printf("val 0x%x\n", myvar);
    myvar = 0;
    printf("val 0x%x\n", myvar);
    return 0;
}

enlace.ld

SECTIONS
{
  .mySegment 0x12345678 : {KEEP(*(.mySection))}
}

GitHub ascendente.

Compilar y ejecutar:

gcc -fno-pie -no-pie -o main.out -std=c99 -Wall -Wextra -pedantic link.ld main.c
./main.out

Producción:

adr 0x12345678
val 0x9abcdef0
val 0x0

Entonces vemos que se puso en la dirección deseada.

No puedo encontrar dónde está documentado esto en el manual de GCC, pero la siguiente sintaxis:

gcc link.ld main.c

parece agregar el script del enlazador dado al predeterminado que se usaría.

-fno-pie -no-pie es necesario, porque la cadena de herramientas de Ubuntu ahora está configurada para generar ejecutables PIE de forma predeterminada, lo que lleva al kernel de Linux a colocar el ejecutable en una dirección diferente cada vez, lo que interfiere con nuestro experimento. Consulte también: ¿Qué es la opción -fPIE para ejecutables independientes de la posición en gcc y ld?

TODO: la compilación produce una advertencia:

/usr/bin/x86_64-linux-gnu-ld: warning: link.ld contains output sections; did you forget -T?

¿Estoy haciendo algo mal? Cómo deshacerse de él? Ver también: Cómo eliminar la advertencia: link.res contiene secciones de salida; te olvidaste -T?

Probado en Ubuntu 18.10, GCC 8.2.0.

  • Santilli: Justo ahora, no puedes evitar la advertencia del enlazador. Vea mi respuesta a stackoverflow.com/questions/20185268/…

    – hermank

    15 de enero de 2019 a las 9:46

avatar de usuario
costilla

Respondiste a tu pregunta, en tu enlace de arriba dice:

Con el compilador GNU GCC, puede usar solo definiciones de puntero para acceder a ubicaciones de memoria absolutas. Por ejemplo:

#define IOPIN0         (*((volatile unsigned long *) 0xE0028000))
IOPIN0 = 0x4;

Por cierto http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Variable-Attributes.html#Variable%20Attributes

    extern const uint8_t dev_serial[12];
    asm(".equ dev_serial, 0x1FFFF7E8");
/* or    asm("dev_serial = 0x1FFFF7E8"); */
    ...

    for (i = 0 ; i < sizeof(dev_serial); i++)
        printf((char *)"%02x ", dev_serial[i]);

  • ¿Qué agrega esto a las respuestas existentes?

    – perro naranja

    1 de junio de 2016 a las 12:17

  • Si bien este fragmento de código puede resolver la pregunta, incluyendo una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y es posible que esas personas no conozcan los motivos de su sugerencia de código. Por favor, también trate de no sobrecargar su código con comentarios explicativos, ¡esto reduce la legibilidad tanto del código como de las explicaciones!

    –Ashish Ahuja

    1 de junio de 2016 a las 13:06

  • Esto realmente agrega algo que las otras respuestas no. Al usar este enfoque, puede colocar una matriz de longitud fija mientras aún permite sizeof() para obtener el tamaño de la matriz.

    – Tom Carpintero

    22 de marzo de 2018 a las 8:44

En GCC puede colocar la variable en una sección específica:

__attribute__((section (".foo"))) static uint8_t * _rxBuffer;

o

static uint8_t * _rxBuffer __attribute__((section (".foo")));

y luego especifique la dirección de la sección en GNU Linker Memory Settings:

.foo=0x800000 

  • ¿Qué agrega esto a las respuestas existentes?

    – perro naranja

    1 de junio de 2016 a las 12:17

  • Si bien este fragmento de código puede resolver la pregunta, incluyendo una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y es posible que esas personas no conozcan los motivos de su sugerencia de código. Por favor, también trate de no sobrecargar su código con comentarios explicativos, ¡esto reduce la legibilidad tanto del código como de las explicaciones!

    –Ashish Ahuja

    1 de junio de 2016 a las 13:06

  • Esto realmente agrega algo que las otras respuestas no. Al usar este enfoque, puede colocar una matriz de longitud fija mientras aún permite sizeof() para obtener el tamaño de la matriz.

    – Tom Carpintero

    22 de marzo de 2018 a las 8:44

avatar de usuario
Yahya Tawil

Tuve un problema similar. Quería asignar una variable en mi sección definida en un desplazamiento especial. Al mismo tiempo, quería que el código fuera portátil (sin dirección de memoria explícita en mi código C). Así que definí la sección de RAM en el script del enlazador y definí una matriz con la misma longitud de mi sección (.noinit sección tiene una longitud de 0x0F).

uint8_t no_init_sec[0x0f] __attribute__ ((section (".noinit")));

Esta matriz mapea todas las ubicaciones de esta sección. Esta solución no es adecuada cuando la sección es grande, ya que las ubicaciones no utilizadas en la matriz asignada serán un espacio desperdiciado en la memoria de datos.

¿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