【问题标题】:ASM: Seg faults on loop, unsure why [duplicate]ASM:Seg 循环故障,不确定原因 [重复]
【发布时间】:2021-01-31 20:15:45
【问题描述】:

我在 NASM x86 中编写了以下程序,但是当我尝试运行它时似乎得到的只是段错误(尝试打印时)和停止打印时的无限循环。代码成功调用printNumber一次,然后出现段错误;因此,我被引导相信这是在循环的第二次迭代中发生的事情。我在这里想念什么?我已将 cmets 添加到我的源代码中以解释发生了什么,但我不明白为什么这会为应该是一个简单程序的程序抛出段错误(目前无法访问 valgrind 或 gdb)。

这是输出:

0Segmentation fault: 11

..这是我写的小程序。我在这里没有看到什么?

SECTION .data
    arraylen dd 10
    num db 0
    farewell: db "Blah"
SECTION .bss
    array resq 8

SECTION .text
    global start

start:
    call fillArr
    mov rax, 0x02000001
    mov rdi, 0
    syscall

printNum:
    mov rsi, num
    mov rdx, 2
    call print
    ret

fillArr:
    lea rsi, [rel array] ; needed for 64 bit on mac
    xor rcx, rcx ; make this 0
    arrayloop:
        mov [rsi+rcx*8], rcx ; array element rcx updated with value
        mov rbx, [rsi + rcx*8] ; grab value at index we just filled

        add rbx, 48 ; now offset number by 48 to make it ascii'able
        mov [rsi+rcx*8], rbx ; overwritten the value we saw as it's ascii
        mov [rel num], rbx ; update storage var
        call printNum ; print
        inc rcx ; we don't need this anymore
        cmp rcx, 4
        jne arrayloop

    mov rsi, farewell
    mov rdx, 4
    call print
    ret

print:
    mov rax, 0x02000004
    mov rdi, 1
    syscall
    ret

【问题讨论】:

  • 涉及什么操作系统? rax=0x2000004应该做什么操作?
  • macOS,64 位。我使用以下命令编译它: nasm -f macho64 arr.asm ld -macosx_version_min 10.7.0 -lSystem -o arrayTest arr.o 该操作相当于Linux上的sys call 4(so print)
  • 我会一步一步地在调试器(如lldb)中运行它,但只看代码我担心的是syscall(指令本身)破坏了RCX和R11。您不使用 R11,但您的代码似乎确实依赖于 RCX,而不会在函数 fillArr 中被破坏。您可以尝试通过保存(即:PUSH RCX)并在返回之前恢复它(即:POP RCX)来保留函数print中的RCX
  • @wallyk:它从一开始就被标记为 MacOS。像这样的电话号码是 MacOS 用于“正常”POSIX 系统调用(如readwrite)的电话号码。当然,好的源代码会注释每个 syscall 确切的系统调用。
  • @PeterCordes:我误读了macos,以为是macros

标签: macos assembly segmentation-fault x86-64 nasm


【解决方案1】:

printNum调用时问题就在这里:

print:
    mov rax, 0x02000004
    mov rdi, 1
    syscall
    ret

正如 @MichaelPrefetch 和 @PeterCordes 在 cmets syscall clobbers rcx 和 r11 中提到的那样。请参阅also(这是一个带有 linux 标记的答案,但在这方面,内核行为与 x86-64 中的 MacOS 相同)。如果您对 under the hood 的工作原理感到好奇,那么在 syscall 之后不使用调试器 rcx 单步执行将包含其返回地址,这将是您的程序下一条指令(在本例中为 ret)。

由于您的 arrayloop 逻辑之后依赖于 rcx,这将在此行中出现 EXC_BAD_ACCESS 失败:

arrayloop:
    mov [rsi+rcx*8], rcx ; array element rcx updated with value

可能的修复可能如下所示:

print:
    mov rax, 0x02000004
    mov rdi, 1
    push rcx
    syscall
    pop rcx
    ret

将 rcx 值保留在堆栈上,并在 syscall 之后立即恢复其值。

作为一个替代方案,@PeterCordes 建议重构循环以完全不依赖 rcx。还有很多剩余的寄存器可供选择(除了前面提到的 r11 和 rax,rdx 用于syscall 返回值)。

【讨论】:

  • 通常的方法是为包含函数调用的循环选择一个保留调用的寄存器,而不是发明一个调用约定来函数保存 RCX。此外,syscall 总是会破坏 RCX,无论它最终返回时最终会出现什么值。在syscall 本身在内核获得控制权之前销毁旧值之后,内核所做的事情没有必要详细说明;这足以解释为什么它没有被保存。
  • 这就是问题所在!谢谢大家的帮助,真不敢相信我忘记了系统调用的副作用。
  • @Kamil:我们都没有提到系统调用也会破坏 R11(尽管 Michael Petch 在 cmets 中做到了)>.CMP Instruction and JE not firing 下一次,记得提到两个被破坏的寄存器。或者只是链接Why does a syscall clobber rcx and r11?
  • @PeterCordes 我根据您的评论编辑了答案。谢谢。
猜你喜欢
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多