Forum: PC-Programmierung Operator new[] in Assembler c++


von Andre Kurth (Gast)


Lesenswert?

Hallo,

ich bin auf der Suche nach einer Erklärung zu einem Assembler-Code wenn 
ich ein Array oder eine Variable mit new Anlege. Bsp:

int *iVariable = new int[10];

sprich ich will ein Array mit 10 Elementen anlegen.

Assemblercode:
1
00CB14FE           push              28h
2
00CB1500           call              operator new[]
3
00CB1505           add               esp, 4
4
00CB1508           mov               dword ptr[ebp-110h], eax
5
00CB140E           mov               eax, dword ptr[ebp-110h]
6
00CB1514           mov               dword ptr [iVariable], eax

So jetzt meine Fragen:

Wie wird wo welcher Speicher angelegt? (Größe? Heap? Stack?)
Was passiert bei Zeile 3 esp, 4?
Warum wird (Zeile 4 & 5) zuerst das Register in einen Pointer kopiert 
und sofort danach der Pointer wieder in das Register zurückkopiert?

Ich weiß Assembler wird heute so gut wie nie benutzt aber wir sollen das 
mal gesehen haben und verstehen was da passiert. Nach etlichen Stunden 
der google/wikipedia/usw. Suche probier ich mein Glück mal hier in dem 
Forum.

Ich hoffe ihr könnt mir dabei helfen.

MfG und vielen Dank im Vorraus

von Peter II (Gast)


Lesenswert?

Andre Kurth schrieb:
> Was passiert bei Zeile 3 esp, 4?

also wenn du schon asm lernen willst dann musst du schon etwas selber 
dafür tun.

 add               esp, 4

was ist daran so schwer zu verstehen? auf Wert vom register esp werden 4 
aufaddiert.

von amateur (Gast)


Lesenswert?

In C werden üblicherweise Werte, beim Aufruf einer Funktion, auf den 
Stack abgelegt. Dann erfolgt der eigentliche Aufruf der Funktion.
Der Befehl: push 28h entspricht der Zahl 40 (10+4 Bytes).
Die eigentliche Speicherreservierung erfolgt in der Funktion "new".
Ein Zeiger auf den so reservierten Speicherbereich wird in irgendeinem 
Register, wahrscheinlich eax, zurückgeliefert.
Also wie genau der Speicher reserviert wird, ist aus den Codestücken 
nicht zu ersehen. Da müsstest Du dem call folgen.

von Karl H. (kbuchegg)


Lesenswert?

Das hat alles mit den Aufrufkonventionen zu tun.
Wenn die Funktion aufgerufen wird, dann schreibst du in C++

  int *iVariable = new int[10];

irgendwie müssen die 10 an die Funktion übergeben werden und irgendwie 
muss die Funktion ein Ergebnis liefern, welches nachher in die Variable 
bugsiert wird.
All das sind Schritte um die du dich auf C++ Ebene nicht kümmern musst. 
Aber irgendwo, irgendwann von irgendeinem Code müssen sie durchgeführt 
werden. Argumente wandern ja nicht irgendwie magisch zu den Funktionen 
und Returnwerte von Funktionen landen auch nicht irgendwie magisch in 
den Speicherzellen, die eine Variable repräsentieren (und die lokale 
Variable muss auch irgendwann mal von irgendeinem Code angelegt worden 
sein)

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

Die Fragestellung ist unstrukturiert, und die Überschrift verwirrend.
Grundsätzlich geht es um x86 Assembler. C++ ist die kompilierte Sprache. 
Du betrachtest also vom C++-Compiler generierten Binärcode. Dieser wird 
von vielen Faktoren bestimmt (Wahl des Compilers, Direktiven, 
Optimierungsgrad, ...).

Genug gemeckert, jetzt zur Sache. esp ist der Stackpointer. Lokale 
Variablen legen C-Compiler gerne auf dem Stack an. ebp ist das 
Base-Register. Es dient den Compilern typischerweise als Backup für den 
Stackpointer des höheren Kontexts. Für weitere Aussagen ist dein Auszug 
zu begrenzt.

Andre Kurth schrieb:

> Assemblercode:
>
>
1
> 00CB14FE           push              28h
2
> 00CB1500           call              operator new[]
3
> 00CB1505           add               esp, 4
4
> 00CB1508           mov               dword ptr[ebp-110h], eax
5
> 00CB140E           mov               eax, dword ptr[ebp-110h]
6
> 00CB1514           mov               dword ptr [iVariable], eax
7
>

von Andre Kurth (Gast)


Lesenswert?

hallo,

ich hätte meine Frage präziser stellen sollen:

Warum werden auf das Register ebp diese 4 byte dazuaddiert?
Wird dem Register dadurch gesagt das durch den operator new[] neuer 
Speicher angelegt worden ist?

Ich hab mich schon ziemlich intensiv mit dem thema assembler 
auseinandergesetzt, allerdings ist das mit dem Operator new[] noch nicht 
ganz klar.

Vor allen dingen die letzten 3 Befehlszeilen des Assemblercodes sind für 
mich noch rätselhaft.

MfG

von amateur (Gast)


Lesenswert?

Die dritte Zeile ist nötig, weil in der ersten Zeile ein Push steht.
Push "Zahl" legt die übergebene Zahl auf den Stapel ab. Dabei wird der
Stapelzeiger (esp) um 4 (4 Bytes) verringert.
Um den Stapel zu bereinigen, gibt es nach dem Call zwei Möglichkeiten:
1. Pop ebx
2. add esp,4
Die erste Möglichkeit zerstört aber das angegebene Register, welches 
dann, im obigen Falle, mit 40 geladen würde.

von Andre Kurth (Gast)


Lesenswert?

Also wenn ich das jetzt richtig verstanden habe wird erst der Speicher 
auf dem Stack angelegt (40 byte) und der Stackpointer wird um 4 
verringert, anschließend wird durch den Aufruf des Operators new dieser 
Speicher in dem new angelegt und darauffolgend der Stackspeicher um 4 
erhöht damit der Stack wieder leer is und der Speicher nur noch im new 
vorhanden ist?

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

28h ist der Parameter für die operator_new[] - Funktion. Jetzt müsste 
man die Calling-Convention wissen, also ob die Funktion ihr Argument 
selbst vom Stack holt, oder dort belässt. Ich schätze mal zweites. Das 
heisst, mit dem add-Befehl entfernt der Caller den Parameter wieder vom 
Stack.

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

Du hast nicht gepostet, wie die operator_new[]-Funktion aussieht. Ich 
bezweifle, dass dort 40 byte auf dem Stack reserviert werden. Dann 
könnte der Caller auch nicht einfach mit einem add-Befehl den alten 
Parameter vom Stack nehmen. Außerdem wird im Anschluss das Ergebnis der 
operator_new[]-Funktion im Speicher abgelegt. Ich vermute, die Funktion 
gibt einen Zeiger zu einem neu allokierten Speicherbereich zurück.

von Karl H. (kbuchegg)


Lesenswert?

Andre Kurth schrieb:
> Also wenn ich das jetzt richtig verstanden habe wird erst der Speicher
> auf dem Stack angelegt (40 byte) und der Stackpointer wird um 4
> verringert, anschließend wird durch den Aufruf des Operators new dieser
> Speicher in dem new angelegt und darauffolgend der Stackspeicher um 4
> erhöht damit der Stack wieder leer is und der Speicher nur noch im new
> vorhanden ist?


Die 4 haben im eigentlichen Sinne mit dem new nicht das geringste zu 
tun. Es ist schlicht und ergreifend eine Aufrufkonvention.

* Der Aufrufer legt seine Argumente auf den Stack
* Der Aufgerufene arbeitet mit diesen Werten, ändert daran aber nichts.
  Insbesondere hinterlässt er den Stack beim Return so, wie er ihn
  vorgefunden hat
* Der Aufrufer ist dafür zuständig, den Stack wieder aufzuräumen.


Der Aufrufer hat mit dem push 4 Bytes auf den Stack gelegt und er ist 
auch dafür zuständig, dass diese 4 Bytes wieder aus dem Stack 
verschwinden.

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

...und die zweite vorletzten Zeilen sagen aus, dass der Compiler Scheiße 
ist.

von Karl H. (kbuchegg)


Lesenswert?

Kan asta schrieb:
> ...und die zweite vorletzten Zeilen sagen aus, dass der Compiler Scheiße
> ist.

Oder dass der Optimizer nicht eingeschaltet war.

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

Auch ohne -OX würde ich soetwas nicht erwarten.

von Karl H. (kbuchegg)


Lesenswert?

Andre Kurth schrieb:
> Also wenn ich das jetzt richtig verstanden habe wird erst der Speicher
> auf dem Stack angelegt (40 byte)

C++ schreibt zwar nicht vor, wo ein bestimmter Speicher zu erzeugen ist, 
weil C++ kein KOnzept vopn 'Stack' oder 'Heap' hat, aber du kannst 
deinen A. darauf verwetten, dass ein new sicher nicht Speicher auf dem 
Stack reserviert.

> erhöht damit der Stack wieder leer is und der Speicher nur noch im new
> vorhanden ist?

Der 'Speicher' ist sowieso immer vorhanden. Der verschwindet ja nicht 
irgendwie magisch oder wird aus der dünnen Luft herbeigezaubert.
Aber es muss auch eine Verwaltung geben bzw. eine Identifikation, 
welcher Speicher denn eigentlich reserviert ist und welcher nicht. new 
kümmert sich um diese Dinge und liefert eine Adresse (eine Zahl), die 
zum Speicher führt, der in der benötigten Größe reserviert wurde.

Und um das auch gleich abzustellen:
new ist kein Assembler-Konstrukt! Da steckt genauso eine Funktion und 
damit genauso Code dahinter, den mal irgendwer geschrieben hat. 
Wahrscheinlich ist new selber in C++ geschrieben bzw. greift auf andere 
Funktionen zurück, die in C++ bzw. in C geschrieben wurden.

von Karl H. (kbuchegg)


Lesenswert?

Kan asta schrieb:
> Auch ohne -OX würde ich soetwas nicht erwarten.

Erwarten würde ich es auch nicht.
Es ist aber auch nichts, was mir jetzt groß Kopfzerbrechen machen würde.

von Andre Kurth (Gast)


Lesenswert?

Okay das hört sich schonmal verständlich an.

Danke für die Infos

Frage: Warum ist denn der Compiler in dem fall scheiße?^^

btw. benutze Microsoft Visual Studio 2010

Mfg

von amateur (Gast)


Lesenswert?

Manche Compiler legen die Ergebnisse auch auf dem Stapel ab.
Bei Klein-Weich wird hierzu oft ebp-relativ gearbeitet.
Die Zeile 4 und 5 bewirken, dass sowohl Register- als auch
Stapelrückgabewerte möglich sind.
Wahrscheinlich ist der Optimizer nicht aktiv.

Interessant sind also nur die Zeilen 1,2,3 und 6.

1. Größe des gewünschten Bereiches 10 Variablen a 4 Bytes = 40d/28h
   werden an Funktion (call new[]) übergeben.
2. Die eigentliche Funktion zur Speicherreservierung aufrufen.
3. Stapel bereinigen.
6. Zeiger auf reservierten Speicherbereich in "iVariable" ablegen.

von Karl H. (kbuchegg)


Lesenswert?

Andre Kurth schrieb:

> Frage: Warum ist denn der Compiler in dem fall scheiße?^^

Vergiss es.
Aus den 3 Zeilen Code kann man das nicht ableiten.

Für diese 'leichte' Ineffizienz mag es Gründe geben, die wir nicht 
wissen. Zb. wie der Debugger an den Returnwert einer Funktion rankommt, 
wenn der nicht (wie andere Variablen) im Speicher gespeichert wird, 
sondern nur in einem Register temporär gehalten wird.

Zb. wie das Expression-Parsing funktioniert

zb. wer die Entscheidung getroffen hat, welche Dinge gleich optimiert 
werden und welche Dinge auf spätere Optimizer-Stufen verschoben werden.


Ein Compiler ist nun mal kein triviales Programm. Speziell C++ Code ist 
unoptimiert ein Graus. Aber wenn der Optimizer loslegen darf, dann holt 
er da schon einiges raus.

von Andre Kurth (Gast)


Lesenswert?

amateur schrieb:
> Manche Compiler legen die Ergebnisse auch auf dem Stapel ab.
>
> Bei Klein-Weich wird hierzu oft ebp-relativ gearbeitet.
>
> Die Zeile 4 und 5 bewirken, dass sowohl Register- als auch
>
> Stapelrückgabewerte möglich sind.
>
> Wahrscheinlich ist der Optimizer nicht aktiv.
>
>
>
> Interessant sind also nur die Zeilen 1,2,3 und 6.
>
>
>
> 1. Größe des gewünschten Bereiches 10 Variablen a 4 Bytes = 40d/28h
>
>    werden an Funktion (call new[]) übergeben.
>
> 2. Die eigentliche Funktion zur Speicherreservierung aufrufen.
>
> 3. Stapel bereinigen.
>
> 6. Zeiger auf reservierten Speicherbereich in "iVariable" ablegen.


Super danke dir!
Jetzt wird das alles klarer :)

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

Andre Kurth schrieb:
> 00CB1508           mov               dword ptr[ebp-110h], eax
> 00CB140E           mov               eax, dword ptr[ebp-110h]

Andre Kurth schrieb:
> Ich hab mich schon ziemlich intensiv mit dem thema assembler
> auseinandergesetzt

Andre Kurth schrieb:
> Frage: Warum ist denn der Compiler in dem fall scheiße?^^

Kannst du dir die Frage selbst beantworten?

von Rolf M. (rmagnus)


Lesenswert?

Also mir kommen solche Konstrukte in unoptimiertem Code nicht besonders 
auffällig vor. Da würde ich das als ganz normal betrachten.

von Uwe (Gast)


Lesenswert?

http://de.wikipedia.org/wiki/Aufrufkonvention
Sind der Schlüssel für die Schnittstelle zwischen Assembler und 
Hochsprache

von Andre Kurth (Gast)


Lesenswert?

Hallo,

Danke an alle für die hilfreichen, vor allen dingen sehr schnellen, 
Antworten :)

MfG

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.