【问题标题】:NASM Assembly while loop counterNASM 汇编 while 循环计数器
【发布时间】:2015-07-08 02:54:09
【问题描述】:

我正在汇编中编写一个 while 循环,以便在 Linux 终端中使用 nasm 和 gcc 进行编译。程序比较 x 和 y 直到 y >= x 并在最后报告循环数。代码如下:

segment .data

out1    db "It took ", 10, 0
out2    db "iterations to complete loop. That seems like a lot.", 10, 0
x       db 10
y       db 2
count   db 0

segment .bss

segment .text

global main
extern printf

main:
    mov    eax, x
    mov    ebx, y
    mov    ecx, count
    jmp    lp         ;jump to loop lp

lp:
    cmp    ebx, eax   ;compare x and y
    jge    end        ;jump to end if y >= x
    inc    eax        ;add 1 to x
    inc    ebx        ;add 2 to y
    inc    ebx
    inc    ecx        ;add 1 to count
    jp     lp         ;repeat loop

end:

    push    out1      ;print message part 1
    call    printf

    push    count     ;print count
    call    printf

    push    out2      ;print message part 2
    call    printf

    ;mov    edx, out1               ;
    ;call   print_string            ;
                                    ;
    ;mov    edx, ecx                ;these were other attempts to print
    ;call   print_int               ;using an included file
                                    ;
    ;mov    edx, out2               ;
    ;call   print_string            ;

这是在终端中编译和运行的:

nasm -f elf test.asm
gcc -o test test.o
./test

终端输出如下:

It took
iterations to complete loop. That seems like a lot.
Segmentation fault (core dumped)

我看不出逻辑有什么问题。我认为这是语法,但我们才刚刚开始学习汇编,我已经尝试了各种不同的语法,比如变量周围的括号和在片段末尾使用 ret,但似乎没有任何效果。我还搜索了分段错误,但没有发现任何真正有用的东西。任何帮助将不胜感激,因为我是一个绝对的初学者。

【问题讨论】:

    标签: c loops gcc assembly nasm


    【解决方案1】:

    我认为您的问题可能只是您引用了常量的地址,而不是它们的内在价值。必须将 nasm 中的标签视为指针而不是值。要访问它,您只需要使用[label]:

     segment .data
         x      dw 42
     segment .text
         global main
         extern printf
     main:
         mov    eax, x
         push   eax
         call   printf   ; will print address of x (like doing cout<<&x in C++)
         mov    eax, [x]
         push   eax
         call   printf   ; will print 42
         sub    esp, 8
         xor    eax, eax
         ret
    

    PS:我认为没有人提到它,但是在调用外部代码(C 或 C++ 或其他)时,易失性寄存器经常被修改,因为在编译时,您使用的那些函数被“翻译”为汇编,然后与您的asm 文件。 PC不是人,所以它不区分高级或低级编写的内容,处理器只是读取存储在寄存器和内存中的操作码和操作数,因此为什么在使用低级语言时需要外部函数( call printf) 将修改(或不修改!总是取决于编译器和体系结构)您也在使用的寄存器。 为了解决这个问题,有多种解决方案:

    1. 您可以使用gcc your_c_file.c -S 检查哪些寄存器没有被修改,然后在文件your_c_file.s 中将是您的编译器从您的C 文件生成的预先准备好的汇编代码。 (往往很难弄清楚什么是什么,如果您要使用此方法,请查看 Name Mangling,了解如何更改 func 名称。)

    2. 将所有要保存的寄存器推入堆栈,然后在调用后将它们弹出回它们的寄存器,同时牢记 LIFO 方法。

    3. 使用指令PUSHAPOPA分别压入或弹出所有寄存器。

    这是 NASM 手册第 3 章,它解释了要使用的语言的基础:http://www.csie.ntu.edu.tw/~comp03/nasm/nasmdoc3.html

    希望你能解决它。

    【讨论】:

      【解决方案2】:

      它崩溃的原因可能是你的main 函数没有ret 指令。还要确保将eax 设置为 0 以表示成功:

      xor     eax, eax ; or `mov eax, 0` if you're more comfortable with that
      ret
      

      此外,全局变量指定指针,而不是值。 mov eax, xeax 设置为x 的地址。如果您想发生任何事情(或不使用全局变量),您需要回写它。

      最后,您使用单个非字符串参数调用 printf

      push    count     ;print count
      call    printf
      

      第一个参数必须是格式字符串,例如"%i"。这里,count 是一个指向空字节的指针,所以你什么也得不到。在我看来,你应该试试这个:

      out3    db "%i ", 0
      
      ; snip
      
      push    ecx
      push    out3
      call    printf
      

      【讨论】:

      • 真棒帮助,谢谢,现在输出是It took 134520932 iterations... a lot. 你能解释一下在这种情况下写回eax的更多信息吗?使用mov eax, [x] 并没有像我想象的那样有帮助。另外,我在 main 末尾添加了mov eax, 0ret,但仍然出现分段错误,有什么想法吗?另外,您介意解释一下mov eax, 0ret 的用途吗?
      • 就这样,不。考虑使用gdb 找出崩溃的位置。 (另外,我刚刚注意到你使用jp 跳转回来。你确定你不是指jmp?)
      • 对于mov eax, 0 然后ret,这是因为否则,没有指令告诉你的程序结束。执行将继续以零字节(这意味着像add al, 0 或其他东西),直到它到达分配内存的末尾,然后它崩溃。当一个函数返回时,它的结果应该位于eax,所以如果你对C有任何概念的话,我们这里基本上是说“return 0”。
      • 至于134520932,它看起来非常像内存地址。您可能应该摆脱全局变量 xycount,只使用寄存器。
      • 好的,eaxret 非常有意义,谢谢。我已经消除了xycount,我只是在使用寄存器,但似乎我的循环确实存在某种问题,因为ecx(计数寄存器)返回 0。你一直在大量的帮助,我想我可以从这里解决。非常感谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-13
      • 1970-01-01
      • 2016-04-02
      • 2011-08-26
      • 1970-01-01
      • 2015-04-24
      相关资源
      最近更新 更多