2016-04-25 8 views
1

Ich und ein paar Freunde fummeln an einem sehr seltsamen Thema. Wir haben einen Crash in unserer Anwendung innerhalb eines kleinen Assembler-Teils (der den Prozess beschleunigt) entdeckt. Der Fehler verursacht wurde, indem er mit dem Stackpointer Hantieren und nicht am Ende zurückzusetzen, sah es wie folgt aus:Verständnis ASM. Warum funktioniert das in Windows?

push  ebp 
mov   ebp, esp 

; do stuff here including sub and add on esp 

pop   ebp 

Wenn es richtig sollte es wie folgt geschrieben werden:

push  ebp 
mov   ebp, esp 

; do stuff here including sub and add on esp 
mov   esp,ebp 
pop   ebp 

Nun, was unser Mindbreak ist: Warum funktioniert das in Windows? Wir haben den Fehler gefunden, als wir die Anwendung auf Linux portiert haben, wo wir auf den Absturz gestoßen sind. Weder bei Windows noch bei Android (mit dem NDK) traten Probleme auf und wir hätten diesen Fehler nie gefunden. Gibt es eine Stackpointer-Wiederherstellung? Gibt es einen Schutz gegen Missbrauch des Stackpointers?

+0

Änderungen sind auf 'esp' aus balancieren soll? Wenn 'add's und' pop' die "sub's" und 'push's ausbalancieren, dann' esp == ebp' schon, dann sollten Sie das 'mov esp, ebp' weglassen (weil es nicht benötigt wird). Wenn es abstürzt, dann balancieren sie offensichtlich nicht aus. Verwenden Sie den gleichen Assembler unter Windows und Linux? Verschiedene Assembler werden die gleiche Quelle für unterschiedlichen Code zusammenstellen. (z.B. in NASM, 'mov reg, symbol 'ist' mov reg, imm32', aber in MASM ist es eine Last.) Die ABIs sind auch für 32 Bit leicht unterschiedlich. –

+0

Ist dieser Assembler in einer separaten Datei oder inline? –

+0

@PeterCordes: Nein, sie balancieren nicht aus. Unter Linux ist mein Stackpointer komplett abgehört – Nidhoegger

Antwort

1

Die ebp ESP-Verwendung, heißt ein Stack-Frame, und ihr Zweck ist es, Variablen auf dem Stapel zuzuordnen, und danach haben Sie eine schnelle Möglichkeit, den Stapel vor der ret Anweisung wiederherzustellen. Alle neuen Versionen der x86-CPU können diese Anweisungen zusammen komprimieren, indem sie stattdessen Anweisungen zum Betreten/Verlassen verwenden.

esp ist der tatsächliche Stack-Zeiger, der von der CPU verwendet wird, wenn Push/Pop/Call/Ret ausgeführt wird. Ebp ist ein Benutzer-manipulierter Basiszeiger, mehr oder weniger alle Compiler verwenden dies als einen Stapelzeiger für lokalen Speicher.

Wenn die Anweisung mov esp, ebp fehlt, verhält sich der Stack fehlerhaft, wenn es die CPU pop ebp erreicht, wenn die CPU die pop ebp erreicht, aber nur dann.

+1

'-fomit-frame-pointer' ist seit mindestens einem Jahr der Standard in gcc und clang für 32bit x86. Es war die Standardeinstellung in x86-64 für länger. 'enter' /' leave' waren neu in 80186, also unterstützen * alle * 32bit-fähige x86-CPUs sie, nicht nur neue. 'enter' ist jedoch viel zu langsam um es zu benutzen. 'leave' ist es wert, benutzt zu werden, wenn der' mov esp, ebp' Teil seiner Operation benötigt wird, ansonsten einfach 'pop ebp', wenn der Funktionskörper' esp' bereits mit einem 'add' wiederherstellt, so dass er andere Regs vorher 'knallen' kann 'ebp'. –

0

es scheint, die Compiler Pflege Ihres Stapels nimmt in den Fenstern:
Der einzige Weg, ich ist vorstellen kann:
Microsoft Visual C besondere Pflege von Funktionen nimmt die B {__ stdcall}. Da die Anzahl der Parameter zur Kompilierzeit bekannt ist, codiert der Compiler die Bytezahl des Parameters im Symbolnamen selbst.
Die __stdcall-Konvention wird hauptsächlich von der Windows-API verwendet und ist etwas kompakter als __cdecl. Der Hauptunterschied besteht darin, dass jede gegebene Funktion eine fest codierte Menge von Parametern hat, und diese kann nicht von Anruf zu Anruf variieren, wie es in C möglich ist (keine "variadischen Funktionen").
sehen:
http://unixwiz.net/techtips/win32-callconv-asm.html
und:
https://en.wikipedia.org/wiki/X86_calling_conventions

+0

Wenn seine Funktion die Argumente nicht mit "ret 8" oder ähnlichem abspielt, gibt es keine Möglichkeit, dass ein "__stdcall" funktioniert. Ein Missverhältnis zwischen Anrufer und Angerufenem würde hier Bruch verursachen, nicht verhindern. Ich denke nicht, dass es plausibel ist, dass dies auch nur einen Fall erklärt, in dem man Glück hatte und in einem bestimmten Fall Code kaputt ging. –

+0

ja, ich stimme zu, in der Montage Mismatch zwischen Anrufer und Angerufener würde hier Bruch verursachen. –