【问题标题】:NASM - JBE instruction gives segmentation faultNASM - JBE 指令给出分段错误
【发布时间】:2017-09-09 09:08:54
【问题描述】:

我正在尝试使用 NASM 汇编器在汇编中实现斐波那契算法。 这是算法的伪代码

fibonacci_it(n):
    int f_i-1 = 1;
    int f_i-2 = 1;

    if (n==0):
        return 1;
    else:
        int i = 1;
        int f_i = 1;
        while (i < n):
            f_i = f_i-1 + f_i-2
            f_i-2 = f_i-1
            f_i-1 = f_i
            i = i + 1
        return f_i

我尝试过的看起来是这样的:

%include "asm_io.inc"

segment .data
prompt      db  "Enter number: ", 0

segment .bss
fibnum      resd 1

segment .text
    global asm_main

asm_main:
    enter   0,0
    pusha   

    mov         eax, prompt
    call        print_string
    call        read_int
    mov         [fibnum], eax

    push        dword [fibnum]
    call        fib_it
    call        print_int

    popa
    mov         eax, 0
    leave
    ret



fib_it:
    enter   12,0                ; 3 local variables: f_i, f_i-1, f_i-2
    pusha

    mov     dword [ebp-4], 1    ; initialize f_i
    mov     dword [ebp-8], 1    ; initialize f_i-1
    mov     dword [ebp-12], 1   ; initialize f_i-2
    mov     ecx, 1              ; i = 1
    mov     edx, 1              ; comparison operator for n
    cmp     [ebp+8], edx        ; n <= 1 ?
    jbe     end_fib_it          ; if n <= 1 -> end and return 1

fib_it_while:
    dump_regs 1
    mov     eax, [ebp-8]        ; eax = f_i-1
    add     eax, [ebp-12]       ; eax = f_i-1 + f_i-2
    mov     [ebp-4], eax        ; f_i = f_i-1 + f_i-2
    mov     eax, [ebp-8]        ; eax = f_i-1
    mov     [ebp-12], eax       ; f_i-2 = f_i-1
    mov     eax, [ebp-4]        ; eax = f_i
    mov     [ebp-8], eax        ; f_i-1 = f_i
    inc     ecx                 ; i += 1
    cmp     ecx, [ebp+8]        ; i < n ?
    jae     end_fib_it          ; end while loop if i < n
    jmp     fib_it_while        ; else -> loop again

end_fib_it:
    mov     eax, [ebp-4]        ; return f_i
    popa
    leave
    ret

程序首先从终端读取一个整数。然后它将它作为 fib_it 的参数推送到堆栈 当我运行程序时,它会出现分段错误。我发现每次jbe end_fib_itjae end_fib_it 为真并且程序必须跳转然后它给出分段错误。我用dump_regs 进行了更多调试,发现while 循环运行完美,jmp fib_it_while 没有问题。

【问题讨论】:

  • 将所有数据保存在内存中而不是寄存器中非常慢。 add eax,edx / add edx,eax 计算斐波那契数列的效率更高!请参阅stackoverflow.com/questions/32659715/… 了解一些编写它的好方法,从相当简单的开始。

标签: assembly nasm


【解决方案1】:

我发现每次 jbe end_fib_it 或 jae end_fib_it 为真并且程序必须跳转时,它都会给出分段错误。

您的观察可能根本不是问题。

push        dword [fibnum]
call        fib_it

当您调用 fib_it 时,您将一个双字压入堆栈之后您不会将其弹出任何位置。

最简单的解决方案可能是用 ret 的替代形式结束 fib_it:

popa
leave
ret 4      ; The 4 will remove the parameter from the stack

不要使用pusha/popa

end_fib_it:
  mov     eax, [ebp-4]        ; return f_i
  popa

您想在EAX 中返回结果,但随后的popa 指令会立即破坏您刚才放入的内容!

由于您只使用额外的寄存器ECXEDX,最好将pusha 更改为push ecx push edx。还将popa 更改为pop edx pop ecx


优化

mov     edx, 1              ; comparison operator for n
cmp     [ebp+8], edx        ; n <= 1 ?

如果您只使用附加寄存器 EDX 进行单一比较,为什么还要使用它?
如果把这对指令改成:

cmp     dword [ebp+8], 1        ; n <= 1 ?

pusha/popa 的上述替换将仅为push ecx/pop ecx

【讨论】:

  • @Codey 我已经添加了优化。
  • enter 0,0 也很慢。如果您想谈论优化,使用内存进行所有操作(并像 OP 一样复制它)比英特尔 Haswell 上的简单 add eax, edx / add edx, eax 慢大约 12 到 18 倍。至少他们决定将循环计数器保存在寄存器中,并以只读方式使用循环上限。 (比其他方式要好得多,inc [mem] 的 RMW 和 cmp 的另一个负载。)
  • 对于一个像样的 asm Fibonacci(将序列存储在一个数组中),请参阅stackoverflow.com/a/32661389/224132。第一个版本只是一个简单的一次一个版本,注册为mov。后来的版本展开了对介绍代码的各种优化:)
猜你喜欢
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 2016-09-10
  • 2014-01-31
  • 2013-07-24
  • 2015-01-09
  • 2012-06-26
  • 1970-01-01
相关资源
最近更新 更多