【问题标题】:NASM: getting segmentation fault in my print functionNASM:在我的打印功能中出现分段错误
【发布时间】:2021-11-26 08:34:46
【问题描述】:

代码:

;The value you want to print must be in the eax register
;one 32-byte resb to hold bit value
;and must be newline db

printDD_start:
    push ebx
    push ecx
    push edx

    push esi
    push esp

    mov esi,0
printDD:
    cmp esi,31
    jg printDD_printStart

    test eax,1
    jz printDD_zero
    jmp printDD_one
printDD_zero:
    mov dword [printDD_var_save + esi],48
    inc esi
    shr eax,1
    jmp printDD
printDD_one:
    mov dword [printDD_var_save + esi],49
    inc esi
    shr eax,1
    jmp printDD

printDD_printStart:
    mov esi,31
printDD_print:
    cmp esi,0
    jl printDD_exit

    mov esp,printDD_var_save
    add esp,esi

    mov eax,4
    mov ebx,1
    mov ecx,esp
    mov edx,1
    int 80h

    dec esi
    jmp printDD_print

printDD_exit:
    mov eax,4
    mov ebx,1
    mov ecx,newline
    mov edx,1
    int 80h


    pop ebx
    pop ecx
    pop edx
    pop esi
    pop esp

    ret

我正在将一个 4 字节变量的位打印到屏幕上。程序正在运行。它正确打印 EAX 寄存器中的值,但我得到了最新的分段错误错误。

【问题讨论】:

  • 哪条指令出错了?当您在 GDB 下运行它时,寄存器中的值是什么?不要只用... 填写您的问题,直到它允许您发布,实际上在您的minimal reproducible example 中包含调试详细信息。
  • 等一下,pop esp??这只是编写mov esp, [esp] 的一种简洁方式,在这里没有任何意义。此外,您没有以与推送相反的顺序弹出。
  • 我没有找到与推送不匹配的流行音乐的规范副本。无论如何,如果您没有尝试在堆栈本身上保存/恢复堆栈指针(这是没有意义的),那么您不匹配的推送/弹出顺序只会打乱调用者的寄存器值,而不是让 ESP 指向错误的位置,不是退货地址。 (这可能是正在发生的事情,除非还有其他错误。)
  • @Peter Cordes:mov esp,printDD_var_saveadd esp,esi也很可疑。
  • 您似乎在尝试将esp 用作计算中的通用寄存器。别。在大多数指令接受它作为操作数的意义上,它在架构上是一个“通用寄存器”,但它仍然是堆栈指针,任意修改它会弄乱堆栈。如果您需要另一个寄存器,看起来ediebp 可用。

标签: assembly segmentation-fault nasm


【解决方案1】:
push ebx
push ecx
push edx

...

pop ebx
pop ecx
pop edx

堆栈是后进先出结构 (LIFO)。您最先推送的值必须最晚出现。我经常明确标记这些pushpop 指令,以便更轻松地跟踪错误:

push ebx    ; (1)
push ecx    ; (2)
push edx    ; (3)

...

pop  edx    ; (3)
pop  ecx    ; (2)
pop  ebx    ; (1)
mov esp,printDD_var_save
add esp,esi

您已选择计算ESP 寄存器中字符的地址。这不仅非常不寻常,而且您不需要使用额外的寄存器,因为无论如何您都必须将此地址存储在ECX 寄存器中。您甚至可以在一条指令中完成:

lea  ecx, [printDD_var_save + esi]
mov dword [printDD_var_save + esi],48
mov dword [printDD_var_save + esi],49

字符是一个字节。最好也把它写成一个字节:

mov byte [printDD_var_save + esi], 48
mov byte [printDD_var_save + esi], 49

您可以简化将EAX 的位转换为字符“0”或“1”的任务。下面的建议的解决方案逐个移出位,并将进位标志(表示移出的位)的值添加到“0”字符的ASCII码中。因此,adc dl, 0 的结果将是字符“0”或字符“1”。
您还可以通过更少的跳跃来优化您的循环。因为您已经知道这些循环至少会运行一次,所以您可以省略循环顶部的测试,而只在循环底部附近有一个合适的测试,您将有条件地跳回。

printDD_start:
    push ebx
    push ecx
    push edx
    push esi

    xor  esi, esi   ; Better than `mov esi, 0`
printDD:
    mov  dl, 48     ; You can replace the 48 by "0"
    shr  eax, 1
    adc  dl, 0      ; -> DL = {"0","1"}
    mov  [printDD_var_save + esi], dl
    inc  esi
    cmp  esi, 32
    jb   printDD

    dec  esi        ; 32 - 1 = 31
printDD_print:
    mov  eax, 4
    mov  ebx, 1
    lea  ecx, [printDD_var_save + esi]
    mov  edx, 1
    int  80h
    dec  esi        ; Until ESI == -1
    jns  printDD_print

printDD_exit:
    mov  eax, 4
    mov  ebx, 1
    mov  ecx, newline
    mov  edx, 1
    int  80h

    pop  esi
    pop  edx
    pop  ecx
    pop  ebx
    ret

我不希望新代码再出现段错误...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-18
    • 2021-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多