0

不久前我涉足了组装(NASM 风格),我正在尝试再次学习它。(我知道我可以先计算字符串长度,但这是一个练习)

无论如何,我编写了这段代码来打印出一个以 null 结尾的字符串来stdout使用sys_write. (我打算概括一下,我现在只是在测试。)

似乎它会起作用,因为如果我i在调用之前增加sys_write它会打印'e',但如果我之后增加它,那么它会'H'按预期打印。但是,一旦遇到: jne print_loop,它就会产生运行时错误,错误代码为-1。我尝试了几个跳转指令,它们都崩溃了,但是一旦我删除跳转,程序就会运行而没有任何错误。

爱迪生

SECTION .data
    hello:
        db "Hello World!/n",0

SECTION .bss
    i: 
        resb 1

SECTION .text
    global _start

    _start:
        mov ecx, hello
        call print

        mov eax, 1
        mov ebx, 0
        int 80h

    print:
        mov eax, 4
        mov ebx, 1
        mov edx, 1

        print_loop:
            push eax
            mov eax, [i]
            lea ecx, [ecx+eax]
            pop eax

            int 80h
            inc dword [i]
            cmp ecx, 0  
            jne print_loop ; if I comment this out, it runs without error.

        ret

这是固定版本:

%macro print_ 1
    mov eax, 4
    mov ebx, 1
    mov ecx, %1

    call print
%endmacro

%macro exit_ 1
    mov eax, 1
    mov ebx, %1
    int 80h
%endmacro

SECTION .data
    hello:
        db "Hello World!/n",0

SECTION .bss
    i: 
        resb 1

SECTION .text
    global _start

    _start:
        print_ hello
        exit_ 0

    ;print code called by print_ macro
    print:
        push ecx
        count:
            inc ecx
            cmp byte [ecx], 0
            jne count

        mov edx, ecx
        pop ecx
        sub edx, ecx

        int 80h
    ret
4

2 回答 2

1

代码有几个问题:

i: 
    resb 1

在这里您为一个字节保留空间,但在 print 函数中您将i其视为 dword。

int 80h

这将丢弃 的旧值eax,因为eax保存系统调用的返回值。因此,您需要eax在每次迭代时重新加载系统调用号 (4)。

lea ecx, [ecx+eax]

在这里,您用 覆盖字符串的基地址&string[i],这将导致后续迭代中的地址不正确,因为您不保留原始ecx值。

cmp ecx, 0

我不确定您为什么希望为 0。加载存储的字节并将与零进行比较(在系统调用之前)ecx会更有意义。[ecx]

于 2013-03-28T18:01:49.177 回答
0

几个问题。

第一的:

  mov eax, [i]

i是一个字节变量,但指令从其位置读取 4 个字节。

  inc dword [i]

i是一个字节变量,但指令在其位置增加一个 4 字节值。

第二(我认为):

ecx在这个指令之后会发生什么

  lea ecx, [ecx+eax]

第二次执行?它仍然是指向的有效地址hello吗?

顺便说一句,学习使用调试器。是时候了。

于 2013-03-28T17:55:59.453 回答