Betrachten Sie den folgenden c ++ Quellcode:
%Vor% Kompilationszeile: g++ main.cpp -o main -O0
Das Ausführen dieses Codes führt zu Segmentierungsfehlern bei Verwendung von gcc-4.8.4 und clang-3.6.0 unter Ubuntu 14.04. Das seltsame Verhalten ist, dass das Symbol% co_de% am Ende eines statisch zugewiesenen Arrays _end
zeigt, nicht am Anfang. Wenn wir _end
durch _end
ersetzen, funktioniert alles gut.
Wenn wir gcc außerdem bitten, einen Assembler-Code auszugeben, indem wir das Befehlszeilenargument -S angeben, gibt es keinen signifikanten Unterschied zwischen der Version mit "_end" und der Version mit einem anderen Array-Namen:
%Vor% Aber wenn wir objdump verwenden, um die ausführbaren Dateien zu dumpen und diff gegen sie auszuführen, werden wir sehen, dass in der end_
version die verwendete Adresse 4200 = 4 * 1050 Bytes weiter ist als benötigt:
Soweit ich weiß, kann der gcc-Compiler Variablen, die mit Unterstrichen beginnen, so behandeln, wie sie wollen, d. e. Dies ist eine schlechte Methode, um solche Symbole in Ihrem Code zu verwenden. Aber meine Frage ist: Was passiert hier wirklich? Warum wird _end
durch eine Adresse am Ende eines zugewiesenen Arrays ersetzt? Warum gibt es keinen Unterschied, wenn wir das Befehlszeilenargument "-S" verwenden, aber es gibt tatsächlich einen Unterschied in den erstellten Binärdateien? Nicht das gcc und clang verhalten sich in diesem Fall identisch, das ist mir auch fremd.
Token, die mit _
beginnen, sind reserviert und sollten nicht verwendet werden. Es scheint, dass _end
ein externes Symbol ist, das für unter Linux kompilierte Programme definiert ist und die erste Adresse nach dem Ende des nicht initialisierten Datensegments (auch als BSS-Segment bezeichnet) darstellt.
Hinweis: Auf einigen Systemen sind die Namen dieser Symbole vorangestellt Unterstriche, also: _etext, _edata und _end.
Quelle: Ссылка
Standardentwurf C99 N1256 7.1.3 "Reservierte Identifikatoren" sagt:
Alle Bezeichner, die mit einem Unterstrich beginnen, sind immer für die Verwendung als Bezeichner mit dem Dateibereich sowohl im normalen als auch im Tag-Namensraum reserviert.
Dann müssen wir das wissen:
So können Sie laut C99 den Bezeichner _end
nicht verwenden.
Ihre Implementierung
Um zu sehen, warum die Implementierung fehlschlägt, verwenden Sie:
%Vor%, um das verwendete Linkerskript zu sehen.
Unter Ubuntu 15.10 definiert es das Symbol% co_de% am Ende des Datenabschnitts:
%Vor%es ist also nicht verwunderlich, dass der Zugriff auf den Speicher, der weit voraus ist, segfault sein kann.