Anfängerproblem mit einfachem Programm
-
Assemblerlerner schrieb:
Weiß denn keiner die Lösung?
Wenn Du Betriebssystem (uname -s -r), den Assembler (volle Kommandozeile) und den Linker (volle Kommandozeile) nicht angibst, stochert ein Helfer nur im Nebel.
Deshalb rate ich mal, dass Dein letztes 'ret' denkt, argc wäre eine Rücksprungadresse. Richtig wäre Dein Programm deshalb so:
section .text global _start _start: push ebp mov ebp, esp mov eax, [ebp+4] ; argc nach eax mov esp, ebp pop ebp mov ebx, eax ; Exitcode mov eax, 1 ; system-exit int 0x80 ; syscall
-
Ja, das ist jetzt aber linux-only mit Kernel-Interrupt 0x80.
Warum kann ich nicht die normale leave-Funktion verwenden?
-
Assemblerlerner schrieb:
Ja, das ist jetzt aber linux-only mit Kernel-Interrupt 0x80.
Warum kann ich nicht die normale leave-Funktion verwenden?
Ich ahnte es doch...
Gut getrollt, Posterviele grüße
ralph
-
Ich verstehe nicht?
Leave = mov ebp, esp pop ebp ret
?
Um ehrlich zu sein, ich verstehe nichtmal, was hier das Problem ist, ich hab den Code mit mehreren Assemblern ausprobiert, das Problem besteht weiterhin.
-
ret in Assembler ist NICHT Return x in C, sondern ein Rücksprungbefehl für den CALL Befehl. Der (near) - ret - Rücksprung greift auf die zwischengespeicherte Sprungmarke zurück, die auf dem Stack liegt.
siehe auch http://www.i8086.de/asm/8086-88-asm-ret.htmlEine Funktion mit einem "Ret" am Ende wird typischerweise im Hauptprogramm geCALLt, im Sinne von CALL printf ... oder ähnlich.
(davon ist hier aber nichts zu sehen, sondern nur der "ret" also der Rücksprung (nach ???))
-
Ok, danke für die Erklärung.
Nun also die Frage: Ist es möglich, das Problem zu lösen, ohne irgendwelche Kernel-Interrupts zu verwenden?
-
"ohne irgendwelche Kernel-Interrupts zu verwenden" ist sehr ungenau ausgedrueckt. Es gibt prinzipiell mehrere Moeglichkeiten. Manche mehr, manche weniger sinnvoll.
Generell laesst sich jedoch sagen, dass es nicht moeglich ist, das beschriebene Problem gaenzlich ohne die Verwendung von Systemaufrufen zu loesen. (Uebrigens egal in welcher Programmiersprache.) Du kannst die Interrupts jedoch zumindest mehr oder weniger aus deinen Quellcodes verbannen. Also einige Vorschlaege:1. Schreibe ein mit dem Ziel-System (Linux, Windows etc.) kompatibles Programm, das o.g. Binaercode in einen ausfuehrbaren Speicherbereich laedt, eine Ruecksprungadresse zu Code auf den Stack legt, welcher das Programm beendet (oder was auch immer), und dann zur Startadresse des oben geposteten Codes springt.
Keine Kernel-Interrupts im Programm-Code selbst verwendet.2. Benutze sysenter/syscall-Funktionen. Systemaufrufe/Linux (nicht systemunabhaengig, ich weiss. Nur der Vollstaendigkeit halber)
3. Binde kapselnde/abstrahierende Funktionsbibliotheken ein. QT vielleicht? Eigene funktionsbibliotheken? Diese muessen jedoch zwangslaeufig mit dem Ziel-System kompatibel sein und verwenden intern Systemaufrufe wie Interrupts.
4. Verwende Macros zur Kapselung. z.B.
%ifdef WINDOWS %macro Exit 0-1 0 push %1 call ExitProcess %endmacro ; Exit %endif %ifdef DOS %macro Exit 0-1 0 mov eax, 4C00h | %1 int 21h %endmacro ; Exit %endif ;... Exit 0
(Beispiel fuer Verwendung von Macros in NASM)
Edit:
Bliebe zu ergaenzen, dass die Methode, ein Programm durch ein einfaches "ret" am Programmende geordnet zu beenden, meines Wissens sowohl in DOS als auch in Windows funktioniert. Beide Systeme initialisieren beim Laden von Programmen den Stack entsprechend, so dass dadurch eine Funktion mit Systemaufruf zum Beenden des Programms aufgerufen wird (int 20h bei DOS z.B.).
-
Nobuo T schrieb:
Generell laesst sich jedoch sagen, dass es nicht moeglich ist, das beschriebene Problem gaenzlich ohne die Verwendung von Systemaufrufen zu loesen.
Einspruch, Euer Ehren! Ich weiß, dass ich unzulässigerweise gequotemardert habe, aber es geht ja eigentlich darum, etwas weitgehend Systemunabhängiges wie C in Assembler zu programmieren. Und das geht, wenn man GCC als Linker einsetzt:
; Name: test.asm ; Assemblieren: nasm -felf32 test.asm ; Linken: gcc -m32 test.o ; Aufrufen: ./a.out hallo ; C-Äquivalent: ; #include <stdio.h> ; int main( int argc, char *argv[] ) ; { ; printf ("%u\n",argc); ; printf ("%s\n",argv[0]); ; return 0; ; } extern printf section .data fmt1 db `%u\n`,0 fmt2 db `%s\n`,0 section .text global main main: push ebp mov ebp, esp mov eax, [ebp+8] ; argc push eax push fmt1 call printf add esp, 8 mov eax, [ebp+12] ; argv push dword [eax] ; argv[0] push fmt2 call printf add esp, 8 mov eax, [ebp+8] ; argc cmp eax, 2 jb .exit mov eax, [ebp+12] ; argv add eax, 4 ; argv[1] push dword [eax] push fmt2 call printf add esp, 8 .exit: mov esp, ebp pop ebp ret
Irgendwo sind zwar immer noch Systemaufrufe, aber nicht mehr im Quelltext. Dann bleibt natürlich die Frage, warum man nicht gleich in C programmiert.
viele grüße
ralph
-
Das kostet dich 10ct in die Smartass-Kasse. :p
-
Moin.
Nobuo T schrieb:
Bliebe zu ergaenzen, dass die Methode, ein Programm durch ein einfaches "ret" am Programmende geordnet zu beenden, meines Wissens sowohl in DOS als auch in Windows funktioniert. Beide Systeme initialisieren beim Laden von Programmen den Stack entsprechend, so dass dadurch eine Funktion mit Systemaufruf zum Beenden des Programms aufgerufen wird (int 20h bei DOS z.B.).
Unter DOS sollte dann aber der Stack von unserem Programm nicht korumpiert worden sein und bei Programmen mit dem Speichermodell Large und Huge für multiple Code Segmente muss CS sich im Segment des PSP befinden.
Weil DOS vor dem Start nur ein einzelnes WORD(OFFSET 0) als Rücksprung-Adresse auf den Stack pusht, aber keine Segmentadresse. Und so kann von einem anderem Code-Segment aus, wo vermutlich kein "int 20"-Befehl am Anfang eingetragen ist, der Rücksprung mit "ret" unser Programm so nicht wie beabsichtigt beenden, sondern es wird dann mit dem dortigen Code unser Programm weiter fortgesetz.
Die Segmentadresse des PSPs liesse sich aber über AH=62h INT 21h return in BX ermitteln.
Dirk