GCC: Warum konstante Variablen nicht in .rodata platziert werden

Lesezeit: 8 Minuten

Benutzer-Avatar
versternen

Ich habe immer geglaubt, dass GCC einen platzieren würde static const variabel zu .rodata Segmente (bzw .text Segmente für Optimierungen) einer ELF- oder einer solchen Datei. Aber das scheint nicht der Fall zu sein.

Ich verwende derzeit gcc (GCC) 4.7.0 20120505 (prerelease) auf einem Laptop mit GNU/Linux. Und es platziert eine statische konstante Variable an .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;
}

Also, ist das ein Bug oder ein Feature? Ich habe beschlossen, dies als Fehler bei Bugzilla zu melden, aber es wäre vielleicht besser, zuerst um Hilfe zu bitten.

Gibt es Gründe, warum GCC keine konstante Variable platzieren kann? .rodata?

AKTUALISIERT:

Wie getestet, eine konstante Variable mit einer expliziten Initialisierung (wie const int a = 0;) platziert werden würde .rodata von GCC, während ich die Variable nicht initialisiert habe. Daher könnte diese Frage später geschlossen werden – ich habe vielleicht keine richtige Frage gestellt.

Außerdem habe ich in meinen vorherigen Worten geschrieben, dass die Variable a im Abschnitt „.data“ platziert ist, was falsch ist. Es ist tatsächlich in platziert .bss Abschnitt da nicht initialisiert. Text oben ist jetzt korrigiert.

  • In C++ können Sie a initialisieren const Variable von einem Wert, der keine Kompilierzeitkonstante ist. Aber ich habe es überprüft, und GCC erlaubt das nicht als Erweiterung im C-Modus.

    – Kartoffelklatsche

    30. Mai 2012 um 14:59 Uhr

  • @Potatoswatter In C könnte man das auch legal schreiben: void test(int a){ const int b = a; /* ... */ }. Ich frage mich eigentlich, ob eine globale konstante Variable in einen schreibgeschützten Speicherbereich gestellt werden soll.

    – versternen

    30. Mai 2012 um 15:14 Uhr

  • FWIW, es geht in einen schreibgeschützten Abschnitt, wenn Sie es explizit initialisieren.

    – Matte

    30. Mai 2012 um 15:16 Uhr


  • Auch Programmierfehler in C++ tun nicht “Ausnahmen auslösen”. Ausnahmen gehören dazu Korrekt Programmierung, keine Debugging-Tools.

    – Kerrek SB

    30. Mai 2012 um 15:42 Uhr

  • @Potatoswatter: Nicht konstante Initialisierer sind in C für nicht statische Objekte (dh Objekte, die innerhalb einer Funktion ohne die static Stichwort): const int r = rand();. Nicht konstante Initialisierer sind für statische Objekte nicht zulässig. Das Vorhandensein oder Fehlen von const spielt keine Rolle; in C, const meint schreibgeschütztnicht “konstant”.

    – Keith Thompson

    15. September 2014 um 15:29 Uhr

Der Compiler hat es zu einem gemeinsamen gemacht, das mit anderen kompatiblen Symbolen zusammengeführt werden kann und das in bss gehen kann (kein Speicherplatz auf der Festplatte), wenn es ohne explizit initialisierte Definition endet. Es in Rodata zu setzen, wäre ein Kompromiss; Sie würden zur Laufzeit Speicher sparen (Gebühr festschreiben), aber mehr Speicherplatz auf der Festplatte verbrauchen (möglicherweise viel für ein riesiges Array).

Wenn Sie lieber in Rodata gehen möchten, verwenden Sie die -fno-common Option zu GCC.

  • -fno-common wird an den Assembler übergeben, aber nicht an den Compiler, denke ich, da nach -fno-common es generiert immer noch .comm a,4,4 in der asm-Datei. Auch wenn diesmal der Assembler zusammengeführt wird .bss hinein .dataSchreibzugriff auf a immer noch erfolgreich, was bedeutet, dass a platziert wird .data aber nicht .rodata. Das Problem ist meiner Meinung nach, dass es nicht initialisiert ist.

    – versternen

    30. Mai 2012 um 16:04 Uhr

Benutzer-Avatar
Braden Best

Warum GCC macht es? Ich kann diese Frage nicht wirklich beantworten, ohne die Entwickler selbst zu fragen. Wenn ich spekulieren darf, würde ich wetten, dass es mit Optimierung zu tun hat – Compiler nicht haben const erzwingen

Trotzdem denke ich, dass es besser ist, wenn wir uns die Sprache selbst ansehen, insbesondere undefiniertes Verhalten. Es gibt ein wenig Erwähnungen von undefiniertem Verhalten, aber keine davon geht in die Tiefe.

Ändern einer Konstante ist undefiniertes Verhalten. Const ist ein Vertragund das gilt insbesondere für C (und C++).

„Aber wenn ich const_cast die Konstante weg und ändere y trotzdem?” Dann hast du ein undefiniertes Verhalten.

Was undefiniertes Verhalten meint ist, dass der Compiler buchstäblich alles tun darf, was er will, und was auch immer der Compiler tut, wird nicht als Verstoß gegen den ISO 9899-Standard angesehen.

3.4.3

1 undefiniertes Verhalten

Verhalten, bei Verwendung eines nicht portierbaren oder fehlerhaften Programmkonstrukts oder fehlerhafter Daten, für die diese Internationale Norm keine Anforderungen stellt

2 ANMERKUNG Mögliches undefiniertes Verhalten reicht vom völligen Ignorieren der Situation mit unvorhersehbaren Ergebnissen über ein für die Umgebung charakteristisches Verhalten während der Übersetzung oder Programmausführung (mit oder ohne Ausgabe einer Diagnosemeldung) bis zum Abbruch einer Übersetzung oder Ausführung (mit Ausgabe einer Diagnosemeldung).

ISO/IEC 9899:1999, §3.4.3

Das bedeutet, dass alles, was der Compiler tut, technisch korrekt ist, weil Sie undefiniertes Verhalten aufgerufen haben nicht Sein falsch. Ergo ist es richtig, dass GCC nimmt …

static const int a = 0;

…und verwandle es in ein .rodata Symbol, während der Einnahme…

static const int a; // guaranteed to be zero

… und es in ein verwandeln .bss Symbol.

Im ersteren Fall jeder Versuch einer Änderung a–sogar per Proxy–wird typischerweise zu einer Segmentierungsverletzung führen, was dazu führt, dass der Kernel das laufende Programm zwangsweise beendet. Im letzteren Fall wird das Programm wahrscheinlich ohne Absturz ausgeführt.

Allerdings ist es nicht vernünftig zu erraten, welche der Compiler tun wird. Const ist ein Vertrag, und es liegt an Ihnen, dem Programmierer, diesen Vertrag aufrechtzuerhalten, indem Sie Daten, die als konstant gelten sollen, nicht ändern. Ein Verstoß gegen diesen Vertrag bedeutet undefiniertes Verhalten und alle damit verbundenen Portabilitätsprobleme und Programmfehler.

GCC kann also ein paar Dinge tun.

Es könnte Schreiben Sie das Symbol in .rodata, um es unter dem Betriebssystemkernel zu schützen

Es könnte Schreiben Sie das Objekt an einen Ort, an dem der Speicherschutz nicht gewährleistet ist. In diesem Fall …

Es könnte ändern Sie den Wert

Es könnte Ändern Sie den Wert und ändern Sie ihn sofort wieder zurück

Es könnte Löschen Sie den fehlerhaften Code vollständig mit der Begründung, dass sich der Wert nicht ändert (0 -> 0), im Wesentlichen optimieren …

int main(){
    int *p = &a;
    *p = 0;
    return 0;
}

…zu…

int main(void){
    return 0;
}

Es könnte schicken Sie sogar ein Modell T-800 rechtzeitig zurück, um Ihre Eltern vor Ihrer Geburt zu kündigen.

Alle diese Verhaltensweisen sind legal (na ja, legal im Sinne der Einhaltung des Standards), so dass der Fehlerbericht nicht gerechtfertigt war.

  • Ich bin mir nicht sicher, ob dies die Frage beantwortet, die war warum gcc verhält sich so wie es tut. Natürlich lässt der Standard ein breites Spektrum an Verhaltensweisen zu.

    – Keith Thompson

    15. Mai 2019 um 4:59 Uhr

  • Es gibt keine Möglichkeit, das „Warum“ endgültig zu beantworten, ohne die Entwickler von GCC selbst zu fragen. Wie auch immer, ich kann Geben Sie eine aus dem Standard selbst abgeleitete Begründung an, und undefiniertes Verhalten ist sicherlich ein wichtiger Blickwinkel. IMHO ist es riskant, sich auf solche Implementierungsdetails zu konzentrieren, da man auf diesem Wissen unangemessene Annahmen treffen und sich auf die Semantik einer bestimmten Implementierung verlassen könnte.

    – Braden Best

    15. Mai 2019 um 6:34 Uhr


Schreiben auf ein deklariertes Objekt const qualifiziert ist undefiniertes Verhalten: Alles kann passieren, sogar das.

Es gibt in C keine Möglichkeit, das Objekt selbst als unveränderlich zu deklarieren, Sie verbieten ihm nur die Veränderbarkeit durch den besonderen Zugriff, den Sie darauf haben. Hier haben Sie eine int*, daher ist eine Änderung in dem Sinne “erlaubt”, dass der Compiler nicht gezwungen ist, eine Diagnose auszugeben. Eine Umwandlung in C bedeutet, dass Sie wissen, was Sie tun.

  • Tatsächlich hat der obige Code NICHT explizit in die konstante qualifizierte Variable a geschrieben. Siehe den Kommentar von @ Mat oben, der meiner Meinung nach genau die Antwort sein wird, die ich brauche.

    – versternen

    30. Mai 2012 um 15:23 Uhr

  • @PengyuCHEN: Jens hat Recht, was du tust, ist undefiniertes Verhalten. Warum GCC diese Variable nicht in einen RO-Abschnitt einfügt, wenn Sie sie nicht initialisieren, aber wenn Sie dies tun, ist mir ein Rätsel.

    – Matte

    30. Mai 2012 um 15:29 Uhr

  • @Mat Ja, ich glaube, Jens hat Recht mit diesem undefinierten Verhalten, was ich meine, die Auswirkung dieses undefinierten Verhaltens soll zur Laufzeit, aber nicht zur Kompilierzeit sein.

    – versternen

    30. Mai 2012 um 15:36 Uhr

  • @PengyuCHEN. es schreibt explizit in dieses Objekt, nur dass das Ergebnis dieser Schreiboperation in diesem speziellen Programm nicht beobachtbar ist, da das Objekt danach nie ausgewertet wird. Der Compiler könnte das also optimieren, wenn die Variable nicht wäre const qualifiziert.

    – Jens Gustedt

    30. Mai 2012 um 15:50 Uhr

Gibt es Gründe, warum GCC keine konstante Variable in .rodata platzieren kann?

Ihr Programm wird vom Compiler optimiert (sogar in -O0 einige Optimierungen vorgenommen werden). Es erfolgt eine konstante Ausbreitung: http://en.wikipedia.org/wiki/Constant_folding

Versuchen Sie, den Compiler wie folgt zu täuschen (beachten Sie, dass dieses Programm noch technisch undefiniertes Verhalten hat):

#include <stdio.h>

static const int a;

int main(void)
{
    *(int *) &a = printf("");  // compiler cannot assume it is 0

    printf("%d\n", a);

    return 0;
}

1384280cookie-checkGCC: Warum konstante Variablen nicht in .rodata platziert werden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy