Register verändern, ohne vorher zu retten



  • Ich möchte aber gerne, dass mein Programm oder meine Funktion normal beendet wird, ohne "illegale Speicherzugriff"-Fehlermeldungen u.ä. 😉



  • ein illegaler Speicherzugriff kommt nur dann, wenn du zB noch andere Sprachen in dein Programm einbettest die erwarten, dass man Register speichert. Dem Kernel ist es nämlich ziemlich wurscht ansonsten, welche Register wie belegt sind.

    btw. die Segmentregister _kannst_ du garnicht verändern 😉



  • zur Zeit rufe ich bloß eine Funktion der stdlib auf: die printf Funktion 😉
    Beim Beschreiben der Segment-Register kommt bei mir die "Speicherzugriffsfehler"-Fehlermeldung. Versucht habe ich so:

    movw %ax, %gs       # hier auch %ds, %es, %fs
    

    Danach gleich Speicherzugriffsfehler... also anscheinend darf man die Register nicht beschreiben.
    Register edi scheint gleich nach dem Start des Programm die Adresse des Einsprungpunkts des Programms zu enthalten. Ich dachte, vielleicht hat das seinen Sinn und das Register sollte man nicht benutzen?



  • movw %ax, %gs       # hier auch %ds, %es, %fs
    

    ist doch AT&T, oder?
    Dann versuchst du hier gs mit ax zu laden?? Was soll das für einen Sinn haben. Müsst das nicht 'ne Tripple-Fault geben!?

    Greetz, Swordfish



  • ja, ich habe versucht gs mit ax zu laden, aber nur so, probeweise, ob das sinnvoll ist oder nicht, sei zunächst dahingestellt. In jedem Fall wird das Programm abgebrochen und als einzige Ausgabe bekomme in meiner Konsole "Speicherzugriffsfehler".
    Was ist ein Tripple-Fault?



  • Das ist eine spezielle Ausnahme des 80x86 im Protected Mode. Kann aber sein, das das OS diese Abfängt und "nur" eine Speicherzugriffsverletzung schmeißt. Ließ dazu ein Tutorial deines Vertrauens zum Protected Mode.

    Greetz, Swordfish

    PS: lass es, gs usw. lieber in Ruhe.



  • Wie sieht es unter Linux mit anderen allgemeinen Registern aus (abgesehen von sp, bp), die man nicht benutzen sollte bzw. vorher retten und nach der Benutzung wiederherstellen?



  • Du kannst alle Allzweckregister so verwenden wie du möchtest.

    Das sind AX,BX,CX,DX,DI,SI,BP und SP , bzw. ihre 64 oder 32 bit Varianten z.b. RAX für 64bit, EAX für 32bit usw... R8 bis R15 sind die zusätzlichen Allzweckregister der 64bit CPU's. R8 wäre also 64bit breit, R8D 32bit, R8W 16bit und R8B 8bit...

    Wenn du nicht mit den Interrupt Funktionen des Kernel arbeiten möchtest und lieber die libc / glibc Funktionen nutzen möchtest ( printf, scanf etc ), gibt es ein paar Dinge die du beachten mußt. Stichwort "Calling Conventions"

    http://en.wikipedia.org/wiki/Calling_convention

    32Bit Beispiel:

    BITS 32 ; Unser Linux ist ein 32bit Betriebssystem und wir wollen 32bit
            ; Programme, wer hätte das gedacht *g
    
    global main ; die Funtktion Main ist der PEP, Programm Entry Point
    
    extern printf ; printf muss sich der Linker irgendwo anders suchen und
                  ; er wird sie beim linken in der libc finden.
    
    section .data USE32 ; unsere data section, mit globalen variablen
    
    mytext: db "Hello World", 0xA, 0x00 ; Der Text den wir ausgeben möchten
    
    segment .code USE32 ; der eigendliche ASM code, natürlich auch 32bit allignment
    
    main:
    	mov eax, mytext ; Laden der Adresse von "mytext" in ein beliebiges 
                            ; Allzeckregister.
    
            ; Aufruf von sprintf, welches die Calling Convention cdecl erwartet
    
    	push eax        ; Funktions Parameter werden in umgekehrter Reihenfolge
                            ; auf den stack gelegt.
    
    	call printf     ; EIP landet auf dem stack und EIP wird mit der adresse
                            ; von printf belegt.
    
    	add esp, 0x04   ; cdecl besagt, das die aufrufende Funktion, die 
                            ; Parameter wieder vom stack entfernen muss. Dazu
                            ; Zählen wir den Stackpointer einfach hoch...
                            ; ( x86er Stacks wachsen nach unten ,)
    
    	mov eax,0x0     ; Fertig, nun sagen wir dem OS noch das alles OK ist 
    	ret             ; und beenden somit unser Programm
    

    Compilieren kann man das ganze mit "nasm -f elf [FILENAME]" und linken mit gcc [FILENAME].o

    Ich würde ohnehin zu nasm oder yasm raten. Die Dokumentation ist gut und imho ist die Intel Syntax besser lesbar als AT&T , wobei es sicherlich nicht schaden kann auch letzters zu kennen, denn GDB und co neigen dazu AT&T zu verwenden ,)



  • vielen Dank!



  • Hallo,
    ich habe neulich versucht, folgenden Code zum Laufen zu bringen:

    str: .asciz "index = %i\n"
    movl $100, %ecx
    0:
    pushl %ecx
    pushl $str
    call printf
    addl $8, %esp
    decl %ecx
    jnz 0b
    

    und es geht nicht. Mit Hilfe einiger Debug-Ausgaben sieht es so aus, dass die Funktion printf den Inhalt des Registers %ecx verändert, ohne den Inhalt wiederherzustellen... Wenn man weiss, welche Register von der aufgerufenen Funktion verändert werden, ist das eigentlich nicht schlimm. Aber wie kann ich genau wissen, welche Register von der Funktion verändert werden? (ich meine jetzt also ohne den Quellcode der betroffenen Funktion zu studieren 🙂


Anmelden zum Antworten