【问题标题】:Segmentation fault while trying to write for loop in NASM on Linux尝试在 Linux 上的 NASM 中编写 for 循环时出现分段错误
【发布时间】:2011-03-18 22:13:50
【问题描述】:

我正在尝试编写一个简单的 NASM 程序,但我似乎不知道如何在其中编写一个 for 循环。使用以下代码,我得到一个分段错误。下面的代码应该打印出“Hello world!”后面是从 1 到 100 的所有数字。

section .data
    message: db 'Hello world!', 10
    messageLength: equ $-message

section .text
    global _start

_start:
    mov eax, 4
    mov ebx, 1
    mov ecx, message
    mov edx, messageLength
    int 80h

    mov ecx, 0
    jmp loop

    mov eax, 1
    mov ebx, 0
    int 80h

loop:
    mov eax, 4
    mov ebx, 1
    mov edx, 1
    int 80h

    add ecx, 1
    cmp ecx, 100
    jl loop

【问题讨论】:

  • 你试过在 gdb 下单步执行吗?
  • 问题可能不在循环中,而在里面的打印语句中。确保您可以打印“Hello world!”和 1 在循环之前。 (我无法在没有参考的情况下阅读汇编,所以尽其所能:))
  • @Paul 我不知道怎么做,所以没有。 @Eugene 我尝试打印出 1,它给了我一个分段错误
  • 在深入编程之前学习如何使用 gdb 可能是个好主意 - 正如您现在看到的,即使是非常简单的程序也需要调试。只需输入 gdb ./my_program 即可开始使用(当然,my_program 是您的可执行文件的名称)。

标签: linux assembly x86 nasm


【解决方案1】:

在你跳转到循环之前,你将 0 分配给 ECX...

这意味着您的程序将尝试打印位于内存地址 0 的字符串,该字符串不属于您,因此出现分段错误...

请记住,您正在使用内存地址。 将 10 分配给寄存器值实际上不会以 ASCII 格式打印 10...这只是意味着您正在获取内存地址 10...

【讨论】:

  • 那么...如何打印字符串“0”而不是尝试打印地址 0 处的任何内容?
  • 你需要有一个你自己的内存地址。例如,通过在程序中定义 4 个字节(3 个数字加上换行符)。然后操作这个内存地址,记住你使用 ASCII 所以 0 是十进制值 48。
【解决方案2】:

您有两个问题:首先,您对 exit() 的系统调用出现在循环之前,而不是之后;其次,您将整数而不是内存指针传递给 write() 系统调用。

试试这个:

section .data
    message: db 'Hello world!', 10
    messageLength: equ $-message

    digits: db '0123456789'

section .text
    global _start

_start:
    mov eax, 4
    mov ebx, 1
    mov ecx, message
    mov edx, messageLength
    int 80h

    mov ecx, 0
    jmp loop

loop:
    push ecx
    mov eax, digits
    add ecx, eax
    mov eax, 4
    mov ebx, 1
    mov edx, 1
    int 80h
    pop ecx

    add ecx, 1
    cmp ecx, 10
    jl loop

这是一个格式化数字并打印循环计数器的版本:

section .data
    message: db 'Hello world!', 10
    messageLength: equ $-message

    number: db '000'
            db 10

section .text
    global _start

_start:
    mov eax, 4
    mov ebx, 1
    mov ecx, message
    mov edx, messageLength
    int 80h

    mov ecx, 0
    jmp loop

loop:
    call _format_number
    push ecx
    mov ecx, number
    mov eax, 4
    mov ebx, 1
    mov edx, 4
    int 80h
    pop ecx

    add ecx, 1
    cmp ecx, 10
    jl loop

    mov eax, 1
    mov ebx, 0
    int 80h

_format_number:
    push ecx
    xor edx, edx
    mov eax, ecx
    mov ecx, 10
    idiv ecx
    add edx, '0'
    mov ebx, number
    mov [ebx+2], dl
    xor edx, edx
    idiv ecx
    add edx, '0'
    mov [ebx+1], dl
    xor edx, edx
    idiv ecx
    add edx, '0'
    mov [ebx+0], dl
    pop ecx
    ret

【讨论】:

  • 这种方法有效,只是在打印完所有数字后会出现段错误。
  • 我不确定它为什么会出现段错误。在我将 int80/eax=1 移到循环之后,它不适合我。
【解决方案3】:

您的代码的问题是您尝试将整数打印为双字。 我宁愿这样使用 printf:

extern printf ;make sure we can use printf

section .data
    message: db 'Hello world!', 10
    messageLength: equ $-message
    decFormat   db '%d',10 ;format string to use in printf

section .text
    global main

main:
    ;print hello world the usual way
    mov eax, 4
    mov ebx, 1
    mov ecx, message
    mov edx, messageLength
    int 80h

    mov ecx, 0

loop:
    push ecx ;add counter
    push decFormat ;add format string
    call printf ;execute printf, which will use the stack values as arguments

    pop ecx ;clean the stack
    pop ecx

    inc ecx ;if you just want to add 1 to the value, you should use inc not add
    cmp ecx, 100
    jl loop

mov eax,1
mov ebx,0
int 80h

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-04
    • 1970-01-01
    • 2017-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多