web-dev-qa-db-fra.com

Comment placer une variable à une adresse absolue donnée en mémoire (avec GCC)

RealView ARM C Compiler supporte plaçant une variable à une adresse mémoire donnée en utilisant l'attribut variable at(address):

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

GCC a-t-il un attribut de variable similaire?

26
Bas van Dijk

Je ne sais pas, mais vous pouvez facilement créer une solution de contournement comme celle-ci:

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

Ce n'est pas exactement la même chose, mais dans la plupart des situations un parfait substitut. Cela fonctionnera avec n'importe quel compilateur, pas seulement GCC.

Si vous utilisez GCC, je suppose que vous utilisez également GNU ld (bien que ce ne soit pas une certitude, bien sûr) et ld prend en charge le placement de variables où vous le souhaitez .

J'imagine que laisser l'éditeur de liens faire ce travail est assez courant.

Inspiré par la réponse de @rib, j'ajouterai que si l'adresse absolue est pour un registre de contrôle, j'ajouterais volatile à la définition du pointeur. S'il ne s'agit que de RAM, cela n'a pas d'importance.

21
Prof. Falken

Vous pouvez utiliser les attributs de section et un ld script de l'éditeur de liens pour définir l'adresse souhaitée pour cette section. C'est probablement plus compliqué que vos alternatives, mais c'est une option.

15

Vous avez répondu à votre question, dans votre lien ci-dessus, il indique:

Avec le compilateur GCC GNU), vous ne pouvez utiliser que des définitions de pointeurs pour accéder aux emplacements de mémoire absolus. Par exemple:

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

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

7
rib
    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]);
4
user6409471

Exemple de script de l'éditeur de liens exécutable minimal

La technique a été mentionnée à: https://stackoverflow.com/a/4081574/895245 mais maintenant je vais maintenant fournir un exemple concret.

principal c

#include <stdio.h>

int __attribute__((section(".mySection"))) myvar = 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;
}

link.ld

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

GitHub en amont .

Compiler et exécuter:

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

Production:

adr 0x12345678
val 0x9abcdef0
val 0x0

On voit donc qu'il a été mis à l'adresse souhaitée.

Je ne trouve pas où cela est documenté dans le manuel GCC, mais la syntaxe suivante:

gcc link.ld main.c

semble ajouter le script de l'éditeur de liens donné à celui par défaut qui serait utilisé.

-fno-pie -no-pie est requis, car la chaîne d'outils Ubuntu est désormais configurée pour générer des exécutables PIE par défaut, ce qui conduit le noyau Linux à placer l'exécutable sur une adresse différente à chaque fois, ce qui dérange notre expérience. Voir aussi: Qu'est-ce que l'option -fPIE pour les exécutables indépendants de la position dans gcc et ld?

TODO: la compilation génère un avertissement:

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

Est-ce que je fais quelque chose de mal? Comment s'en débarrasser? Voir aussi: Comment supprimer l'avertissement: link.res contient des sections de sortie; avez-vous oublié -T?

Testé sur Ubuntu 18.10, GCC 8.2.0.