【问题标题】:x86 assembly: printing integer to the console after mul (seg fault)x86 程序集:在 mul 之后将整数打印到控制台(段错误)
【发布时间】:2012-06-30 15:38:57
【问题描述】:

我正在尝试学习 x86 汇编。我正在使用的书是Assembly Language - Step by Step, Programming With Linux(我不得不说它非常好)。到目前为止我学到了很多东西,但我觉得我也应该挑战自己,在很多方面保持领先,这样我才能通过实践更快地学习(我可以跟着做,自上而下的学习,但我觉得很乏味慢)。

所以,我认为尝试将两个寄存器(32 位)相乘然后将数据输出到控制台是一个很酷的主意。

问题是,当我执行程序时(我使用的是 NASM,就像本书一样 - 虽然没有 Insight 调试器),我收到了分段错误。我已经用这个小锤子在 gdb 中进行了大量调试,但无论出于何种原因,我似乎无法弄清楚问题所在。

我想知道为什么我会收到分段错误,以及有什么好的方法可以 谴责这个问题。另外,如果我在代码中创建的 cmets 与实际发生的情况不匹配,如果有人能纠正我,我将不胜感激。

这是我目前的代码(注释很好)

谢谢。

codez

section .data
;TODO

section .bss
valueToPrint: resb 4            ;alloc 4 bytes of data in 'valueToPrint'

section .text

global _start

_mul:
    mov eax, 0x2A ;store 42 in eax
    mov edx, 0x2A ;store 42 in edx
    mul eax
    ret

_safe_exit:
    mov eax, 1  ;initiate 'exit' syscall
    mov ebx, 0  ;exit with error code 0
    int 0x80    ;invoke kernel to do its bidding 

_start:
    nop                         ;used to keep gdb from complaining

    call _mul                       ;multiply the values
    mov [valueToPrint], eax         ;store address of eax in the contents of valueToPrint
    mov eax, 4                      ;specify a system write call - aka syswrite
    mov ebx, 1                      ;direction used to make the syswrite call output to console - i.e. stdout
    mov dword [ecx], valueToPrint   ;store valueToPrint in ecx: ecx represents the syswrite register
    int 0x80                        ;invoke kernel based on the given parameters

    call _safe_exit

编辑

另外,我正在运行 Arch Linux,如果这有影响的话。

【问题讨论】:

  • 在您的 _mul 子例程中,您使用的是 mul eax,它将 eax 与 eax 相乘...因为您在 eax 和 edx 中都使用了 42,所以您得到(嗯,您会得到)正确的答案,但你没有涉及 edx 中的值。 @Adam Rosenfield(下)有正确的答案,但您并没有首先创建要打印的 TEXT/ASCII 字符串。程序集并不会自动为您做任何事情。这就是为什么你有如此多的控制权......但你必须告诉它如何做每一件事。您没有创建 txt 字符串来指向 ecx 以进行打印。 (你还需要一个长度)
  • 另外,mul(乘法)将 eax(ax/rax 等)中的值与指定的值相乘,并将生成的 64 位值放入 EDX:EAX... 所以如果你重新寻找完整的答案,记得也要使用 edx 值。 42*42=1764,所以你离 edx 的答案还很远,但你仍然应该知道这就是发生的事情,所以你可以期待 edx 改变并在需要时可用。
  • (天哪,我今晚很健谈!)一些挑剔的东西......你的'_safe_exit'子程序......因为它永远不会返回,你应该jmp代替它。是的,当程序退出时,系统会转储所有内容......但同样,您应该知道事情是如何工作的。 call _save_exit 效果很好,但对我来说它在堆栈上留下了一个价值。挑剔的东西。我很抱歉。我开始在一台 16K 机器上组装,当时...内存很昂贵,而且您编写的代码很紧凑。我仍然这么认为,即使有 8G 的内存。通常较小的运行速度更快。
  • 太棒了,洛尼克斯。谢谢你。我是如何让 eax 与 edx 相乘的?
  • mul edx。我很抱歉吹毛求疵...组装很棒,但你必须能够兼顾一切,因为它真的对你没有任何帮助,而且不知道事情如何相互作用会咬你。

标签: x86 stdout nasm sys dword


【解决方案1】:

此行导致分段错误:

mov dword [ecx], valueToPrint

您告诉它将valueToPrint 存储在地址ecx 的内存位置中。您永远不会初始化 ecx(内核可能会在程序启动时为您将其初始化为 0),因此当您取消引用它时,您将访问一个无效的内存位置。

write(2) 系统调用采用 3 个参数:寄存器ebx 中的文件描述符编号,指向要写入ecx 的字符串的指针,以及要写入edx 的字节数。因此,如果您只想打印结果的原始二进制数据,您可以传递valueToPrint 的地址,并告诉它从该地址打印 4 个字节。在这种情况下,valueToPrint 是 1764(十六进制的 0x6e4),因此这段代码将在 x86 上打印出 4 个字节 e4 06 00 00,这是 little-endian:

mov [valueToPrint], eax   ; store the result into memory
mov eax, 4                ; system call #4 = sys_write
mov ebx, 1                ; file descriptor 1 = stdout
mov ecx, valueToPrint     ; store *address* of valueToPrint into ecx
mov edx, 4                ; write out 4 bytes of data
int 0x80                  ; syscall

【讨论】:

  • 首先,我要感谢您的澄清。然而,我很好奇以 10 为底的整数究竟有多难打印?
  • 要以 10 为基数打印出来,您需要保留一个足够大的字符数组来保存输出(对于有符号的 32 位整数,至少 11 个字节)。然后,重复将数字除以 10 直到达到 0,将剩余的加 48 保存到数组中的一个字符中(48 是 ASCII '0'),从右到左。最后,将该缓冲区的地址传递给sys_write(),并记录您正在打印的字节数。或者,如果您想跳过这一切,只需致电printf(3)
【解决方案2】:

编辑! (忘记了 cmp!)

以 10 为底输出值...

; assuming value is in EAX (only)
.loop1:
    div   10      ; divide by 10, leave result in eax, REMAINDER in edx
    push  eax     ; save value
    mov   eax,edx
    or    eax,0x30 ; convert 0-9 to '0'-'9' (ascii 0x30='0')
    call  display_a_char ; (you write this!)
    pop   eax
    or    eax,eax ; set flags (edit!)
    jnz   .loop1
; all done, maybe put a \n return or something here

【讨论】:

  • 这是现成的......所以它应该工作得很好,只是小问题
  • 顺便说一句...更改一行,添加两行...这将很容易将输出转换为任何基数,2-36。耶!
猜你喜欢
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多