Hallo,
ich schreibe gerade einen Compiler für eine selbst entwickelte Sprache -
(sehr C nah). Als Referenz nutz ich den Microsoft-Compiler+Assembler,
ich weiß es gibt auch andere. Bei folgendem Code
1
intmain(intargc,char*argv[])
2
{
3
UINT64_Tbbbb;
4
bbbb=10;
5
intpreinPointer;
6
einPointer=&bbbb;
7
einPointer=11;
8
}
erzeugt folgenes assembly:
1
;UINT64_T bbbb;
2
;bbbb = 10;
3
mov qword ptr [bbbb],0Ah
4
5
;intpr einPointer;
6
;einPointer = &bbbb;
7
lea rax,[bbbb]
8
mov qword ptr [einPointer],rax
9
10
;einPointer = 11;
11
mov qword ptr [einPointer],0Bh
Der Pointer landet also der _TEXT Section. Woran macht das der Compiler
in dem Falle aus? Mein Hobel legt die (lokale?) Variable auf dem Stack
an...wie die Windows Calling Convention auch "empfiehlt".
Welche Bedingungen muss ich prüfen?
VG Jonas
fixStackPointer schrieb:> Der Pointer landet also der _TEXT Section.
Das steht in Widerspruch zu:
> Mein Hobel legt die (lokale?) Variable auf dem Stack
Ansonsten könnte es helfen, die Frage so zu formulieren, dass
Normalsterbliche ohne Kristallkugel sie verstehen.
A. K. schrieb:> fixStackPointer schrieb:> Der Pointer landet also der _TEXT Section.>> Das steht in Widerspruch zu:>> Mein Hobel legt die (lokale?) Variable auf dem Stack>> Ansonsten könnte es helfen, die Frage so zu formulieren, dass> Normalsterbliche ohne Kristallkugel sie verstehen.
"Mein Hobel" ist wohl der Eigenbau-Compiler des TE, der so die Aussage,
den Pointer auf den Stack legt.
Der gezeigte Assemblercode stammt wohl vom nicht näher spezifizierten
Microsoft-Compiler, der als Referenzimplementierung gilt.
Der TE möchte nun herausfinden welcher Compiler sich "richtig" verhält.
Kein Widerspruch.
Allerdings gibt der Assemblercode zu wenig Infos her.
Ich kann da nicht erkennen wo der Pointer liegt.
Ja es war schon spät...
Sorry, die Architekture ist x86_x64 im "echten" 64 bit Modus. Ich hab
das Verhalten jetzt so implementiert das alle nicht initalisierten
Variablen in DATA_ landen, alles was initialisiert werden muss in TEXT_
mit entsprechendem Initialiserungscode... sieht ganz gut aus. Vielen
Dank für eure Beiträge :)
fixStackPointer schrieb:> alles was initialisiert werden muss in TEXT_> mit entsprechendem Initialiserungscode..
Vielleicht nicht unbedingt empfehlenswert, falls der Code auf einem OS
laufen soll, das (per MMU) TEXT_ in ein read-only Segment steckt?
>Vielleicht nicht unbedingt empfehlenswert, falls der Code auf einem OS>laufen soll, das (per MMU) TEXT_ in ein read-only Segment steckt?
Stimmt, allerdings hab ich da auch ein andere Calling Convention. Ist
aber soweit abstrahiert, also leicht leicht erweiterbar..
Erstmal Windows CV, dann kommt System-V...
>Vielleicht nicht unbedingt empfehlenswert, falls der Code auf einem OS>laufen soll, das (per MMU) TEXT_ in ein read-only Segment steckt?
Ich versuche das Gerade nochmal genau nachzuvollziehen, ist ein bisschen
schwierig wenn man so tief im Detail steckt. Aber ich glaube ich
verstehe jetzt was du meinst, das Binary wird ja schlussendlich mal von
der Platte in den Ram geladen... allerdings muss das ja trotzdem
irgendwie deterministisch laufen...irgendeine Idee?
Er wächst und gedeiht...
Aber langsam lässt sich die Sache mit den SECTIONEN nicht mehr
ignorieren... Im Moment wird das nur ganz primitiv per "Scopelevel" oder
"Blocklevel" entschieden, ganz wie man das nennen mag. Also nur an Hand
der Position im Code, irgendwelche Qualifier ignoriere ich noch bewusst
um die Komplexität nicht explodieren zu lassen. Den ein oder anderen
könnte ich aber schon mal einbauen. static zum Beispiel, sollte ja immer
in DATA_ landen, oder?
Bei "richtigen" Betriebssystemen besteht ein Executable (mindestens) aus
3 Sections: TEXT, DATA und BSS.
In TEXT gehören m.E. nur Code und (höchstens) Daten, die nur gelesen
werden (Konstanten).
In DATA gehören (veränderliche) Daten, die einen Initialwert haben.
In BSS gehören (veränderliche) Daten, die beim Programmstart nicht oder
mit 0 initialisiert werden.
Der Loader (eines "vorsichtigen" Betriebssystems) wird das TEXT-Segment
(wenn die HW das zulässt) idealerweise in einen Speicherbereich laden,
der anschließend mittels MMU auf readonly gesetzt wird.
Schreibzugriffe lösen dann einen Fault aus, wenn sie versuchen
(unabsichtlich oder böswillig), Code zu manipulieren (schließt dann
natürlich selbstmodifizierenden Code aus, aber das ist meist sowieso
eine gute Idee).
Zu dem üblichen TEXT-DATA-BSS-Tripel kommt oft noch RODATA. Dort liegen
Konstanten, die im Gegensatz zu TEXT nicht ausführbar und im Gegensatz
zu DATA nicht schreibbar sein müssen.
Dazu kommt noch der Stack. Lokale Variablen gehören nicht nach DATA,
sondern auf den Stack.
Danke @Markus!!!
>Zu dem üblichen TEXT-DATA-BSS-Tripel kommt oft noch RODATA. Dort liegen>Konstanten, die im Gegensatz zu TEXT nicht ausführbar und im Gegensatz>zu DATA nicht schreibbar sein müssen.
Ok, RODATA hab ich auch schon mal gehört, gibt nun 4 Sectionen.
>Der Stack gehört aber nicht in die exec, den legt das OS zur Laufzeit an>und wenn er wächst werden mehr Pages per MMU zugemapped.
? Das ist ja gerade die Frage in welche SECTION? Auf den Stack bedeudet
es muss per push oder mov auf den stack geschoben werden, das ist
ausführbarer code und steht in TEXT_.
Mw E. schrieb:> Der Stack gehört aber nicht in die exec, den legt das OS> zur Laufzeit an und wenn er wächst werden mehr Pages> per MMU zugemapped.
Das ist richtig, setzt aber voraus, dass deine Hardware eine MMU und ein
OS hat. Das ist nicht überall gegeben.
Das Executable muss nicht wissen, wo der Stack liegt (weil darauf nur
über den Stackpointer zugegriffen wird), aber der Startup-Code muss den
Stackpointer initialisieren.
Deswegen muss man den Stack auch im Hinterkopf behalten. :-)
fixStackPointer schrieb:> Das ist ja gerade die Frage in welche SECTION?
Der Stack ist nur per Stackpointer zugreifbar, liegt also erstmal in
keiner SECTION. Zumindest nicht, wenn man sich das Executable anschaut.
> Auf den Stack bedeudet es muss per push oder mov auf den stack> geschoben werden, das ist ausführbarer code und steht in TEXT_.
(a) Nein, man darf auch SP-relativ auf den Stack zugreifen (Stichwort
"Stack Pointer", "Frame Pointer").
(b) In TEXT stehen auch die Load- und Store-Befehle für Daten. Deswegen
stehen die Daten selbst aber brav in DATA, RODATA oder BSS - und gehören
selbst nicht zu TEXT.
Wie gesagt, der Stack muss beim Start korrekt initialisiert werden und
darf die anderen Sectionen im Speicher nicht überlappen.
Im Executable taucht der Stack nicht auf.
Im Prozessor kann der Stack sein eigenes Segment bekommen (x86) oder
auch nicht (so ziemlich alle anderen Architekturen).
Im Compiler ist der Stack der Ort für sämtliche lokalen Variablen,
Register-Spill und andere Dinge.
fixStackPointer schrieb:>>Der Stack gehört aber nicht in die exec, den legt das OS zur Laufzeit an>>und wenn er wächst werden mehr Pages per MMU zugemapped.>> ? Das ist ja gerade die Frage in welche SECTION? Auf den Stack bedeudet> es muss per push oder mov auf den stack geschoben werden, das ist> ausführbarer code und steht in TEXT_.
???
Der Stack ist extra und die Befehle, diesen zu bedienen, stehen im
_TEXT.
Außerdem scheinst du bereits Optimierungen einzukalkulieren.
Normalerweise steht eine Konstante in RODATA, wird von da geholt und
dann genutzt.
Per Optimierung ist sie ggf. bereits Teil des Assemblerbefehls. Da
beginnen dann bereits die Sektionszuordnungen zu verschwimmen.
>Außerdem scheinst du bereits Optimierungen einzukalkulieren.>Normalerweise steht eine Konstante in RODATA, wird von da geholt und>dann genutzt.
Jep, es gibt schon ein compilerflag optfor: size | speed
Wollte ich eigentlich erst nicht machen, aber es fallen einem viel zu
viele Optimierungen ein, die alle zu verwerfen wäre irgendwie schade.
>Im Executable taucht der Stack nicht auf.
Naja, die Push-Pop Orgien werden ja in der executable gefeiert...XD
Vielen dank für eure Anregungen, aber ich glaub ich habs jetzt soweit
gerafft :D.
Zwischenstand: Bin gerade bei Arrays... :D It's fun :D
Guten Abend :D
Ui...gibt einiges neues:
Arrays sind fast fertig...
Mache jetzt einen "pre-compile" Lauf und ermittle dabei:
-wird eine variable initialisiert?
-wie oft wird sie geschrieben (geplante Optimierung: je mehr Zugriffe,
desto eher in Register halten)
-wann ist der letzte Zugriff auf die variable (Optimierung: Register
freigeben)
Damit ergeben sich nun (außer nicht implementierte Qualifier)
alle notwendigen Parameter um die richtige Section auszuwählen.
Da bastel ich gerade noch dran rum.
Desweiteren hab ich bindump.exe entdeckt...
Damit wird natürlich vieles klarer.
Sowas sieht schon gar nicht schlecht aus: relative
Stackpointer-Adressierung, Stack-Init-Optimierung...
So ... lange Nacht hinter mir, aber:
-Sectionen gibt es nun fünf.
Eine Frage dazu, warum gibt es zwei "gleiche Sectionen" pdata und idata?
Beide mit gleichen flags:
-read Only
-Initialized Data
static, const und extern wird nun ausgewertet. in verbindung mit
scope-und filelevel und dem vorherigen Ermitteln der Initialisierung und
der Schreibzugriffe versuche ich nun die "LifeTimeDuration" der Objekte
zu ermitteln...
lg
Arg... doofes c# oder doofer fixStackPointer:
CopyTo() erstellt keine deep-copy sondern nur eine shadow-copy...
hat mich einen Tag gekostet :(
Egal, weiter geht's :D, frohe Weihnachten!
Guck dir die Intel Intrinsics an und machs in C:
https://software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=AVX_512
Im makefile dann -native angeben mit er auch AVX nutzt.
Mit AVX2 hab ich meinen Mandelbrotzeichner beschleunigt.
S. R. schrieb:> Mw E. schrieb:>> Der Stack gehört aber nicht in die exec, den legt das OS>> zur Laufzeit an und wenn er wächst werden mehr Pages>> per MMU zugemapped.>> Das ist richtig, setzt aber voraus, dass deine Hardware eine MMU und ein> OS hat. Das ist nicht überall gegeben.
Ja gut, aber wo wir hier über x86 und eigen gebautem Compiler reden hab
ich ein unterliegendes OS sowie eine MMU angenommen ;)
Mw E. schrieb:> Ja gut, aber wo wir hier über x86 und eigen gebautem Compiler reden hab> ich ein unterliegendes OS sowie eine MMU angenommen ;)
Und vollkommen zurecht. :-)
fixStackPointer schrieb:> Wollte ich eigentlich erst nicht machen, aber es fallen einem viel zu> viele Optimierungen ein, die alle zu verwerfen wäre irgendwie schade.
Hast du dich mal mit dem Aufbau klassischer Compiler beschäftigt, also
SSA und sowas? In den Büchern stehen die viele grundlegende
Optimierungen schon drin. ;-)