1

私はアセンブリ コードを書く初心者で、助けが必要です。

私の仕事は、n 番目のフィボナッチ数を計算する NASM (Linux 上) でプログラムを作成することです。ここで、n は read syscall で STDIN から読み取られ、C atoi/atol で int/long に変換されます。計算された数値は STDOUT に書き込まれます (C の printf を使用できます)。

動作する 32 ビット コードを書くことができましたが、それを 64 ビット (64 ビット レジスタ、64 ビット long int を使用) に変換する作業に行き詰まっています。私は単純にそれをやろうとしました(eax -> rax、esp -> rsp などを変更します)が、得られるのは segfault だけです。

編集:タイプミスが修正されました

EDIT2: 64 ビット整数を使用して 46 番目のフィボナッチ数よりも高い数を表示する方法はありますか?

コードは次のとおりです。

section .data
        format: db      '%d', 0xA


section .bss
        buffer resb 8 
        bufferLength equ $-buffer; 

section .text

extern printf
extern atoi

global main

main:
        call fib 

fib:
        mov rax, 3 
        mov rbx, 0 
        mov rcx, buffer
        mov rdx, bufferLength 
        int 0x80 

        push 0
        push buffer 
        call atoi
        add rsp, 8

        push rbx
        mov rcx, rax
        xor rax, rax
        xor rbx, rbx
        inc rbx

        call print

exitProg:
        mov rbx, 0 
        mov rax, 1 
        int 0x80 

print:
        push rax
        push rcx

        push rax
        push format
        cmp rcx, 1
        je lastPrint
        add rsp, 8

        pop     rcx
        pop     rax

        mov     rdx, rax   
        mov     rax, rbx    
        add     rbx, rdx        
        dec     ecx           
        jnz     print    

        pop     rbx       

lastPrint:
        call printf
        add rsp, 8
        call exitProg

前もって感謝します。

4

1 に答える 1

3

関数は返されないため、実際には関数ではありません。それらを書き直すことを検討する必要があります。C ライブラリを使用している場合は、システム コールmainを使用する代わりに、から戻ることをお勧めします。exitまた、可能であれば、C ライブラリ I/O 関数を使用することをお勧めします。

64 ビット モードでは、通常、syscall命令を使用してシステム コールにアクセスしint 0x80ますが、互換性のためにインターフェイスも利用できます。システム コール番号は 32 ビットとは異なることに注意してください。

さらに、呼び出し規則が (ユーザー呼び出しとシステム呼び出しの両方で) 異なっていても、一部の引数はレジスターで渡され、スタックを整列させる必要があります。詳細については、ABI のドキュメントを参照してください。

コードのロジック、print特にクレイジーなスタック操作を理解するのに苦労しています。また、 が早いかどうかがチェックされるpop rbxため、この行には決して到達しないことに注意してください。したがって、デクリメント後にゼロになることはありません。rcx1

また、タイプミスがありbuforます。最後に、フォーマット文字列はテキスト セクションにあります。それはうまくいきますが、私はあなたがそれを望んでいたと推測しています.data.ディレクティブ(ファイルの最初の行にあります)を間違えただけです.

上記のほとんどは、元の 32 ビット コードにも当てはまると思います。

ここでの更新は可能な実装であり、現在は 64 ビットの結果 (最大 n=93 まで動作) を使用してXADD(Frank Kotler に感謝):

section .data
format:
        db      "%lu", 10, 0

section .bss
        buffer resb 8
        bufferLength equ $-buffer

section .text

default rel ; use rip relative addressing (optional)
extern printf
extern atoi

global main

main:
        sub rsp, 8              ; stack alignment

        ; note: this should be a call to libc read function
        ; but apparently assignment forces us to use syscall
        xor eax, eax            ; syscall number for "read" is 0
        xor edi, edi            ; fd 0, stdin
        lea rsi, [buffer]       ; buf
        mov edx, bufferLength   ; length
        syscall

        lea rdi, [buffer]
        call atoi

        mov edi, eax            ; pass returned value from atoi
        call fib

        lea rdi, [format]
        mov rsi, rax            ; the returned value from fib
        xor eax, eax            ; no xmm registers used
        call printf

        xor eax, eax            ; return zero
        add rsp, 8
        ret

fib:
        mov eax, edi
        sub edi, 1
        jle fib_done            ; f(0)=0, f(1)=1
        xor ecx, ecx            ; f(n-1)
        mov eax, 1              ; f(n)
fib_loop:
        xadd rax, rcx
        sub edi, 1
        jnz fib_loop

fib_done:
        ret
于 2012-12-15T14:26:07.127 に答える