2010-06-12 8 views
3

En utilisant C++ et GCC, puis-je déclarer une variable externe qui utilise une adresse spécifique en mémoire? Quelque chose commeVariable externe à une adresse spécifique

int key __attribute__((__at(0x9000))); 

cette option spécifique AFAIK ne fonctionne que sur les systèmes embarqués. Si une telle option est disponible sur la plate-forme x86, comment puis-je l'utiliser?

Répondre

10

Option facile:

Define

int * const key = (int *)0x9000; 

et se réfèrent à *key ailleurs (ou utiliser une référence).

Option Pointerless:

Toutes les ont des adresses spécifiques externs! Ces adresses peuvent ne pas être connues avant l'heure de liaison, mais elles doivent être résolues par la suite. Si vous déclarez extern int key;, vous devez fournir une adresse pour le symbole key au moment de la liaison. Cela peut être fait en utilisant un script de liaison (voir Using ld) ou à la ligne de commande de l'éditeur de liens, en utilisant l'option --defsym.

Si vous exécutez gcc, vous pouvez utiliser l'indicateur -Xlinker pour transmettre l'option à l'éditeur de liens. Dans votre exemple,

gcc -o outfile -Xlinker --defsym -Xlinker key=0x9000 sourcefile.c 

Le programme suivant, ainsi compilé, sorties 0x9000.

#include <stdio.h> 
extern int key; 
int main(void) { 
    printf("%p\n", &key); 
    return 0; 
} 

Si vous avez une collection de variables que vous voulez être dans une région de la mémoire, une méthode plus appropriée pourrait consister à utiliser des sections de sortie comme suggéré par Nikolai, peut-être conjointement avec un script personnalisé ld.

+0

Bon point sur le pointeur 'const'. –

+1

C'est ce que je cherchais. J'inverse un programme en utilisant l'injection DLL. Dans mon code DLL, je dois accéder aux variables qui appartiennent au processus cible. Afin d'y accéder SANS pointeurs, j'avais besoin d'un moyen de donner une variable externe à une adresse spécifique. Merci! – AndiNo

+0

Très bien, alors si vous voulez changer les adresses de variables autour pour une raison quelconque, vous n'avez pas besoin de recompiler votre code, seulement relier :) – Artelius

0

Cela ne fonctionnera pas pour les applications de bureau (pour les E/S mappées en mémoire dans les lecteurs de périphériques, cela peut être logique) car la mémoire est virtualisée sur la MMU sur les plates-formes x86. Vous ne saurez donc pas quelle adresse physique sera située quelque part dans l'espace virtuel.

Quel serait le cas d'utilisation à côté de tester les E/S de la mémoire ou un autre hack? Sur x86 dans l'espace utilisateur chaque cellule mémoire est équivalente ... Pour accéder aux variables, utilisez leur nom dlsym() est votre ami sous Linux, GetProcAddr() sous Windows. La spécification de l'adresse n'est pas prévue AFAIK.

Même fournir une adresse de chargement préférée pour une bibliothèque ou une DLL partagée n'aidera pas, car elle peut être déplacée à un autre endroit en cas de chevauchement avec d'autres bibliothèques partagées. La fonction de randomisation d'adresse dans les systèmes d'exploitation modernes le rend presque imprévisible (quel est le but pour éviter les attaques de dépassement de tampon reproductibles)

4

Je ne trouve pas cet attribut dans les documents GCC. Cela n'a aucun sens pour les programmes d'usage général car de nombreux systèmes modernes fournissent address space layout randomization. Le mieux que vous pouvez demander, je suppose, est de mettre une variable dans une section spécifique, comme dans

int init_data __attribute__ ((section ("INITDATA"))); 

Aussi, si vous connaissez adresse [virtuelle] d'une variable, pourquoi ne pas seulement l'accès par pointeur :

int* pkey = (int*)0x9000; 
*pkey = 0xdeadbeef; 
+0

Vous oubliez que gcc est aussi souvent utilisé pour la programmation du noyau où cela serait très agréable. Souvent, les périphériques mappés en mémoire importants se trouvent à un emplacement particulier. –

+1

Dans le noyau, cela n'a de sens que dans le code d'amorçage lorsque la mémoire physique est mappée dans l'espace d'adressage virtuel du noyau. Même alors, voulez-vous vraiment coder en dur les adresses physiques dans votre code source (par opposition aux décalages vers une adresse de base configurable)? –

+0

Il est encore parfois utile, par ex. pour les UTCB dont les adresses virtuelles sont souvent fixes. Et pour les noyaux * simples *, sans mémoire virtuelle ou avec un simple mappage linéaire du virtuel au physique. Le seul avantage que je peux penser est que les symboles peuvent être résolus au moment de la liaison (comme indiqué dans ma réponse), de sorte que vous n'avez pas besoin de recompiler votre fichier objet juste pour changer une adresse. – Artelius

-2

Nombre de systèmes d'exploitation de bureau modernes utilisent la mémoire virtuelle, ce qui signifie que l'adresse que vous avez tout est vide de sens jusqu'à ce que vous donnez à l'OS, ce qui rend une adresse mémoire spécifique sans valeur. Il n'y a aucun moyen et aucun avantage à cela sur desktop/x86.

0

Je pense que le compilateur IAR C utilise une extension non standard qui ressemble à

char foo @ 0x9000; 

pour cela, au moins pour leur compilateur MSP430. Je n'ai jamais beaucoup utilisé GCC pour le MSP430, mais je pense qu'il a peut-être aussi supporté cela pour qu'il puisse être compatible avec le compilateur IAR.

Si vous voulez faire quelque chose qui fonctionnera sur tous les compilateurs sans vous tromper avec l'éditeur de liens, vous devrez travailler un peu plus fort à l'avant.

#define CONST_ADDR_VAR(type, name, address) type *const name##_addr =(type *)address 

Après avoir appelé que pour chaque variable que vous souhaitez vous préciser devrez également faire #define var (* VAR_ADDR)

Ce serait bien si cela aurait pu être combiné avec la macro précédente, mais définir une macro dans une macro n'est pas standard. Je pense cependant qu'il existe un moyen de le faire avec le préprocesseur de GCC.

Si vous voulez singe autour avec votre éditeur de liens alors vous pourriez probablement trouver un moyen de dire où ces variables vivaient et simplement les utiliser comme extern dans vos programmes C.

Vous pouvez également utiliser le __attribute__((section(...))) de GCC pour ce faire, mais vous pourriez avoir besoin d'une section différente pour chaque variable dont vous souhaitez spécifier l'adresse. Il y avait d'autres choses qui semblaient un peu confuses à ce sujet, et il faudrait que vous disiez au linker où se trouvaient ces sections de toute façon.

http://www.ohse.de/uwe/articles/gcc-attributes.html#var-section

1

Depuis vous avez marqué cela avec C++, vous pouvez facilement utiliser le placement nouveau. Ce qui est agréable et portable:

// an object type T at address 0x9000 
T* t = new(reinterpret_cast<void*>(0x9000)) T; 

Il est évident que ce ne sera pas un mondial depuis new peut être utilisé en dehors d'une fonction. Mais vous pourriez facilement avoir une fonction que vous appelez le plus tôt possible pour initialiser certains globals de cette façon.

3

Vous pouvez simplement utiliser une macro:

#define KEY (*(int*)0x9000) 

de sorte que toute écriture KEY écriture à cet emplacement de mémoire, et tout lit à KEY lire cet emplacement de mémoire.

Si cet emplacement de mémoire pourrait changer hors de votre contrôle (par exemple, si elle représente un registre du matériel ou une sorte d'E/S mappée de mémoire), vous devez déclarer volatile:

#define KEY (*(volatile int *)0x9000) 

Cette volonté forcez le compilateur à relire la valeur de la mémoire chaque fois que vous la lisez et réécrivez-la dans la mémoire chaque fois que vous l'écrivez, au lieu de la mettre éventuellement en cache dans un registre.

+0

Si vous n'aimez pas les macros, utilisez un pointeur const comme dans ma réponse. – Artelius

Questions connexes