2017-01-31 2 views
0

mein Lehrer macht einen Crash-Kurs in der Montage mit uns, und ich habe keine Erfahrung darin. Ich soll eine einfache Funktion schreiben, die vier Variablen benötigt und (x + y) - (z + a) berechnet und dann die Antwort ausgibt. Ich weiß, dass es ein einfaches Problem ist, aber nach stundenlangen Recherchen komme ich nirgendwohin, jeder Schritt in die richtige Richtung wäre sehr hilfreich! Ich muss den Stapel verwenden, da ich mehr Dinge zum Programm hinzufügen muss, sobald ich diesen Punkt überwunden habe und viele Variablen speichern muss. Ich kompiliere mit Nasm und Gcc, in Linux. (X86 64)Push/Pop Segmentierung Fehler in einfacher Multiplikationsfunktion

(Seite Frage, meine '3' zeigt nicht im Register r10, aber ich bin in Linux so sollte dies das richtige Register sein ... irgendwelche Ideen?)

Hier wird so weit mein Code:

global main 

    extern printf 

segment .data 

    mulsub_str db "(%ld * %ld) - (%ld * %ld) = %ld",10,0 

    data   dq 1, 2, 3, 4 
segment .text 

main: 

    call multiplyandsubtract 
    pop r9 
    mov rdi, mulsub_str 
    mov rsi, [data] 
    mov rdx, [data+8] 
    mov r10, [data+16] 
    mov r8, [data+24] 
    mov rax, 0 
    call printf 

    ret 



multiplyandsubtract: 

    ;;multiplies first function 
    mov rax, [data] 
    mov rdi, [data+8] 
    mul rdi 
    mov rbx, rdi 
    push rbx 

    ;;multiplies second function 
    mov rax, [data+16] 
    mov rsi, [data+24] 
    mul rsi 
    mov rbx, rsi 
    push rbx 

    ;;subtracts function 2 from function 1 
    pop rsi 
    pop rdi 
    sub rdi, rsi 
    push rdi 
    ret 
+0

'x + y' ist Addition, keine Multiplikation, obwohl das für das Problem nicht relevant ist. – Barmar

Antwort

3

Schubs in die richtige Richtung

Nizza Wortspiel!

Ihr Problem ist, dass Sie scheinbar nicht wissen, dass ret den Stapel für die Rücksendeadresse verwendet. Als solche push rdi; ret wird nur auf die Adresse in rdi gehen und nicht zu Ihrem Anrufer zurückkehren. Da dies wahrscheinlich keine gültige Codeadresse ist, erhalten Sie einen schönen segfault.

Um Werte von Funktionen zurückzugeben, lassen Sie das Ergebnis einfach in einem Register, Standard-Aufrufkonventionen verwenden normalerweise rax. Hier ist eine mögliche Version:

global main 

    extern printf 

segment .data 

    mulsub_str db "(%ld * %ld) - (%ld * %ld) = %ld",10,0 

    data   dq 1, 2, 3, 4 
segment .text 

main: 
    sub rsp, 8 
    call multiplyandsubtract 
    mov r9, rax 
    mov rdi, mulsub_str 
    mov rsi, [data] 
    mov rdx, [data+8] 
    mov r10, [data+16] 
    mov r8, [data+24] 
    mov rax, 0 
    call printf 
    add rsp, 8 

    ret 



multiplyandsubtract: 

    ;;multiplies first function 
    mov rax, [data] 
    mov rdi, [data+8] 
    mul rdi 
    mov rbx, rdi 
    push rbx 

    ;;multiplies second function 
    mov rax, [data+16] 
    mov rsi, [data+24] 
    mul rsi 
    mov rbx, rsi 
    push rbx 

    ;;subtracts function 2 from function 1 
    pop rsi 
    pop rdi 
    sub rdi, rsi 
    mov rax, rdi 
    ret 

PS: Hinweis Ich habe auch die Stapelausrichtung nach der ABI behoben. printf ist darüber bekannt, dass auch wählerisch.

0

Um mehr als 64b von Subroutine zurückgeben (rax ist nicht genug), können Sie optional die gesamte Standard-ABI-Konvention fallen lassen (oder tatsächlich folgen, gibt es sicherlich eine gut definierte Möglichkeit, mehr als 64b von Unterprogrammen zurückgeben), und Benutze andere Register, bis du keine mehr hast.

Und wenn Sie aus Ersatz Rückkehr Register lief (oder, wenn Sie verzweifelt Stapelspeicher verwenden möchten), können Sie die Art und Weise C folgen ++ Compiler tun:

SUB rsp,<return_data_size + alignment> 
    CALL subroutine 
    ... 
    MOV al,[rsp + <offset>] ; to access some value from returned data 
     ; <offset> = 0 to return_data_size-1, as defined by you when defining 
     ; the memory layout for returned data structure 
    ... 
    ADD rsp,<return_data_size + alignment> ; restore stack pointer 

subroutine: 
    MOV al,<result_value_1> 
    MOV [rsp + 8 + <offset>],al ; store it into allocated stack space 
     ; the +8 is there to jump beyond return address, which was pushed 
     ; at stack by "CALL" instruction. If you will push more registers/data 
     ; at the stack inside the subroutine, you will have either to recalculate 
     ; all offsets in following code, or use 32b C-like function prologue: 

    PUSH rbp 
    MOV rbp,rsp 
    MOV [rbp + 16 + <offset>],al ; now all offsets are constant relative to rbp 
    ... other code ... 
    ; epilogue code restoring stack 
    MOV rsp,rbp ; optional, when you did use RSP and didn't restore it yet 
    POP rbp 
    RET 

So während Sie den Anweisungen des Unterprogramms ausgeführt wird, die Stapelspeicher Layout ist wie folgt:

rsp -> current_top_of_stack (some temporary push/pop as needed) 
+x  ... 
rbp -> original rbp value (if prologue/epilogue code was used) 
+8  return address to caller 
+16 allocated space for returning values 
+16+return_data_size 
     ... padding to have rsp correctly aligned by ABI requirements ... 
+16+return_data_size+alignment 
     ... other caller stack data or it's own stack frame/return address ... 

ich werde nicht überprüfen, wie ABI es definiert, weil ich zu faul bin, und ich hoffe, dass diese Antwort verständlich ist für Sie das zu erklären, Prinzip, so werden Sie erkennen, in welche Richtung der ABI funktioniert und stellen ...


Dann wieder, würde ich sehr ziemlich viele kürzere einfacher Subroutinen zu verwenden empfehlen nur Einzelwert zurückkehrt (in rax/eax/ax/al), wann immer möglich, versuchen das SRP (Prinzip der einheitlichen Verantwortung) zu befolgen. Der obige Weg wird Sie zwingen, eine Rückgabedatenstruktur zu definieren, die zu aufwändig sein kann, wenn es nur eine vorübergehende Sache ist und stattdessen in Einzelwert-Unterprogramme aufgeteilt werden kann (wenn die Leistung gefährdet ist, dann wahrscheinlich das ganze Unterprogramm) wird sogar die Logik der gruppierten zurückgegebenen Werte und einzelne CALL übertreffen.