gpt4 book ai didi

在程序集中调用 printf() 会导致 'floating point exception'

转载 作者:行者123 更新时间:2023-12-03 09:59:23 24 4
gpt4 key购买 nike

我有以下代码:

extern printf

section .data

A db 1
B db 2
C db 3
D db 4
E db 5

fmt db "%d",10,0
fmt2 db "An overflow has occured",10,0

section .text

global _start

_start:
xor rax, rax
xor rbx, rbx
xor rdx, rdx

xor ax, ax
mov al, [B]
mul ax
jo over
call printax
mov bx, ax

xor ax, ax
mov al, [C]
mul ax
jo over
call printax

xor cx, cx
mov cl, [C]
mul cx
jo over
call printax

xor cx, cx
mov cl, [E]
div cx
jo over
call printax
xor dx, dx

xor al, al
mov rdi, fmt
call printf
jmp exit

over:
mov rdi, fmt2
xor al, al
call printf

exit:
mov rax, 60
mov rdi, 0
syscall

printbx:
xor rsi, rsi
mov si, bx
mov cl, al
xor al, al
mov rdi, fmt
call printf
mov al, cl
xor si, si
ret

printax:
xor rsi, rsi
mov si, ax
xor al, al
mov rdi, fmt
call printf
mov ax, si
xor si, si
ret

我用 nasm -f elf64 1.asm 编译它并使用 ld -dynamic-linker /lib/ld-linux-x86-64.so.2 1.o -o 1 -lc 链接它.当我执行二进制文件时,我得到
$ ./1
4
9
0
Floating point exception (core dumped)
printf()当我在汇编代码中调用它时并不总是失败。

删除 printf()来自 printax 的电话和 printbx给我
$ ./1
0

更新:如果我删除 div cx,异常也会消失线。然后我得到以下输出:
$./1
4
9
0
0
0

但即使我添加它也不会消失
mov cx, 1
mov ax, 1

之前 div cx .

最佳答案

再看看x86-64 ABI calling conventions .具体来说,在第 15 页:

Registers %rbp, %rbx and %r12 through %r15 “belong” to the calling function and the called function is required to preserve their values. In other words, a called function must preserve these registers’ values for its caller. Remaining registers “belong” to the called function. If a calling function wants to preserve such a register value across a function call, it must save the value in its local stack frame.



所以当你调用 printf , 你必须假设所有寄存器除了 rbp, rbx, r12..r15被重创了。这对您的程序有两个影响:
  • 您尝试保存 ax调用printf通过将其值存储在 si注册然后放回去,但这无济于事,因为si可以被破坏。
  • 您的 div cx指令将dx:ax的内容相除通过 cx , 但是 dx也可能被破坏了。

  • 后者是 SIGFPE 的具体原因(尽管它的名称也在 整数 除溢出上引发),至少在我的测试运行中是这样。在 printf 之后返回, dx包含一些巨大的数字,例如 dx:ax除以 cx不适合 16 位,这是溢出。

    (这也解释了为什么当您调用 printf 时崩溃就消失了——它不再会破坏您的寄存器。)

    这里的另一个教训是,您不能通过执行 jo 来检查 x86 上的除法溢出。然后;此错误主要由异常发出信号,而不是标志。所以你真的必须在执行指令之前检查你的操作数,或者安排在发生异常时处理它(这更复杂,超出了这个答案的范围)。

    其他几点注意事项:
  • 在您最后一次调用 printf 之前(就在 jmp exit 之前),您不会将任何内容加载到 rsi 中,所以打印的值是垃圾。
  • 除非有充分的理由,否则在 x86-32 或 x86-64 上通常不首选 16 位算术。它并没有更快,并且使用操作数大小前缀使您的代码膨胀。最好使用 32 位算术来完成所有工作。
  • 由于您使用自己的入口点,而不是让 C 库调用您的 main ,这意味着 libc 没有机会运行自己的初始化代码。因此,调用任何 libc 函数不一定是安全的,尤其是 stdio 和分配函数。看来printf在这种情况下碰巧可以正常工作,也许可以进行调试,但是您不应该计划以这种方式编写程序用于生产。
  • 关于在程序集中调用 printf() 会导致 'floating point exception',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58491727/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com