GCC: pourquoi des variables constantes ne sont pas placées dans .rodata
J'ai toujours cru que GCC serait static const
variable .rodata
segments (ou à .text
segments pour des optimisations) d'un ELFE ou tel fichier. Mais il semble pas le cas.
Je suis actuellement à l'aide gcc (GCC) 4.7.0 20120505 (prerelease)
sur un ordinateur portable avec GNU/Linux. Et c'est une constante statique variable .bss
segment:
/*
* this is a.c, and in its generated asm file a.s, the following line gives:
* .comm a,4,4
* which would place variable a in .bss but not .rodata(or .text)
*/
static const int a;
int main()
{
int *p = (int*)&a;
*p = 0; /* since a is in .data, write access to that region */
/* won't trigger an exception */
return 0;
}
Donc, est-ce un bug ou une fonctionnalité? J'ai décidé de déposer ce qu'un bug sur bugzilla, mais il pourrait être préférable de demander de l'aide en premier.
- Il que GCC ne pouvez pas placer une variable const dans .rodata
?
Mise à JOUR:
Testé, une constante variable avec une initialisation explicite(comme const int a = 0;
) serait placé dans .rodata
par GCC, alors que j'ai quitté la variable non initialisée. Ainsi, cette question pourrait être fermé plus tard, je n'ai pas de présenter une bonne question, peut-être.
Aussi, dans mon précédent mots, j'ai écrit que la variable a est placé dans '.les données de la section, ce qui est incorrect. C'est en fait placé dans .bss
section depuis pas initialisé. Le texte ci-dessus est maintenant corrigé.
- En C++, vous pouvez initialiser un
const
variable à partir d'une valeur qui n'est pas une constante de compilation. Mais j'ai vérifié, et GCC ne permet pas que comme une extension en C en mode. - En C, vous pouvez également écrire ce légalement:
void test(int a){ const int b = a; /* ... */ }
. Je suis en train de me demander si une constante globale de la variable doit être des lieux dans une mémoire en lecture seule de la région. - FWIW, il est en lecture seule section si vous initialiser explicitement.
- Oh.. je suis.. tellement stupide, et, grandement reconnaissant!
- Il y a bien toujours quelque chose de louche. AFAICT, en initialisant à 0 ne change pas le programme, qui static var doit être initialisé à zéro si vous n'avez pas explicitement de le faire. Si quelque chose se passe. (clang met dans un RO section indépendamment de l'initialisation.)
static const int a;
n'est même pas légal, je pense - les constantes doivent avoir un initialiseur.- Aussi, les erreurs de programmation en C++ ne sont pas "déclencher des exceptions". Les Exceptions font partie de la programmation correcte, pas des outils de débogage.
- Enfin, dans le meilleur des variables statiques de durée de stockage qui sont initialisées dans la statique de la phase d'initialisation sont admissibles pour les mettre dans un segment en lecture seule. "L'expression constante" est le terme pertinent.
- RE
static const int a;
peut ne pas être légal: n'est-elle pas constituer une tentative de définition et de se comporter comme au §6.9.2 (c'est à dire tant qu'il n'y a pas de conflit, se comporte comme si elle avait un zéro de l'initialiseur)? - C est pas du C++.
static const int a;
est parfaitement légal C. - Non constants initialiseurs sont parfaitement légales en C pour les non-objets statiques (c'est à dire, les objets définis à l'intérieur d'une fonction sans la
static
mot-clé):const int r = rand();
. Non constants initialiseurs ne sont pas autorisés pour les objets statiques. La présence ou l'absence deconst
n'a pas d'importance; en C,const
moyens en lecture seule, pas de "constante".
Vous devez vous connecter pour publier un commentaire.
Le compilateur a fait d'elle une commune, qui peut être fusionné avec d'autres compatible avec les symboles, et qui peut aller dans le bss (à ne pas prendre de l'espace sur le disque) si il se retrouve avec pas explicitement initialisée définition. Mettre dans rodata serait un compromis: il vous suffit d'économiser de la mémoire (commit charge) au moment de l'exécution, mais permettrait d'utiliser plus d'espace sur le disque (potentiellement beaucoup pour un tableau énorme).
Si vous préférez aller dans rodata, utilisez le
-fno-common
option de GCC.-fno-common
est passé à l'assembleur, mais pas le compilateur, je pense, car après-fno-common
il génère encore.comm a,4,4
dans le fichier asm. Aussi, bien que cette fois l'assembleur fusionne.bss
en.data
, l'accès en écriture à un succès, ce qui signifie un est placé dans.data
mais pas.rodata
. Le problème je pense, c'est que ce n'est pas initialisé.l'écriture à un objet qui a été déclaré
const
qualifié est un comportement indéfini: tout peut arriver, même que.Il n'existe aucun moyen dans C à déclarer l'objet lui-même d'être unmutable, vous ne l'interdisent à être mutables par le particulier, l'accès que vous avez pour elle. Ici vous avez une
int*
, de sorte que la modification est "autorisé" dans le sens que le compilateur n'est pas contraint d'émettre un diagnostic. Faire un cast en C signifie que vous supposez que de savoir ce que vous faites.const
qualifié.Pourquoi GCC t-il? Ne peux pas vraiment répondre à cette question sans se poser les développeurs eux-mêmes. Si j'ai le droit de spéculer, je ferais le pari qu'il a à faire avec l'optimisation--compilateurs ne pas ont à appliquer const.
Cela dit, je pense que c'est mieux si l'on regarde la langue elle-même, en particulier un comportement indéfini. Il y a un quelques mentions de comportement indéfini, mais aucun d'entre eux aller en profondeur.
De la modification d'une constante est un comportement indéfini. Const est un contrat, et c'est particulièrement vrai dans C (et C++).
Ce comportement indéfini signifie est que le compilateur a le droit de faire littéralement ce qu'il veut, et quel que soit le compilateur décide de faire ne sera pas considérée comme une violation de la norme ISO 9899 standard.
Ce que cela signifie est que, parce que vous avez invoqué un comportement indéfini, tout ce que le compilateur n'est techniquement correcte, par voie de pas être incorrect. Ergo, il est correct pour GCC à prendre...
...et de le transformer en un
.rodata
symbole, tout en prenant......et le transformer en un
.bss
symbole.Dans le premier cas, toute tentative de modification de
a
- même par procuration--généralement d'une violation de segmentation, provoquant le noyau à la force de tuer le programme en cours. Dans ce dernier cas, le programme sera probablement courir sans s'écraser.Cela dit, il n'est pas raisonnable, à vous de deviner lequel le compilateur va faire. Const est un contrat, et c'est à vous, le programmeur, de respecter ce contrat par pas de modification de données qui est supposée être constante. Violation de ce contrat à un comportement indéfini, et tous les problèmes de portabilité et de programme, et les bugs qui vont avec.
Donc GCC capable de faire quelques choses.
Il pourrait écrire le symbole .rodata, en lui donnant une protection en vertu du noyau de système d'exploitation
Il pourrait écrire l'objet à un endroit où la protection de la mémoire n'est pas garanti, auquel cas...
Il pourrait modifier la valeur
Il pourrait changer la valeur et de le modifier immédiatement en arrière
Il pourrait complètement supprimer le code malveillant sous prétexte que la valeur ne change pas (
0 -> 0
), essentiellement de l'optimisation......à...
Il pourrait même envoyer un modèle de T-800 dans le temps pour mettre fin à vos parents avant de vous êtes né.
Tous ces comportements sont légales (bien, juridique dans le sens de se conformer à la norme), de sorte que le rapport de bug n'était pas justifiée.
Votre programme est optimisé par le compilateur (même dans
-O0
certaines optimisations sont faites). La constante de propagation est fait: http://en.wikipedia.org/wiki/Constant_foldingEssayer de tromper le compilateur comme ceci (notez que ce programme est toujours techniquement un comportement indéfini):
.data
. Je ne pense pas que cela dépend de la façon dont nous l'accès, mais sur la façon dont nous le déclarer. Voir @Mat commentaire ci-dessus, qui, je pense, doit être exactement la réponse que j'ai besoin.-O0
, il n'est pas optimisée pour moi.int main(void) {}
) que les comportements observables du programme n'est pas modifié. En fait dans ma compilation de Pengyu du programmea
est même mis dans.bss
que ça prend moins de place dans la finale binaire que dans.rodata
..data
seulement parce que je vois que.comm
derective, et dans ce cas, puisque je n'ai pas initialiser un, il doit être mis en.bss
. Mes paroles avait tort à propos de ça.printf
.