从程序集中调用scanf以获取浮点段错误,而不是返回xmm寄存器中的浮点数

时间:2018-04-22 10:24:07

标签: assembly segmentation-fault scanf x86-64

我需要在程序集中编写一个程序,需要使用scanf获取一些用户参数。问题是在第三次调用中,我得到了分段错误。

简要介绍计划目的。
它应该以这种格式从用户那里获得输入:

epsilon = 1.0e-8
order = 2
coeff 2 = 2.0 0.0
coeff 1 = 5.0 0.0
coeff 0 = 3.0 0.0
initial = 5.0 0.0
coeff 0 = 3.0 0.0

并将此数据存储在数据结构中。

字符串格式:

    section .data
    epsilon_format:
    db "epsilon = %g",0
    order_format:
    db " order = %d",0
    coeff_format:
    db " coeff %d = %le %le",0
    initial_format:
    db " initial = %le %le"
    root_format:
    db "root = %.17g %.17g", 10, 0
    divide_by_zero:
    db "divide by zero",10,0

代码:

main:
call    **get_input**
call    newton_raphson
call    show_output

mov rdi, [poly_real]
call    free
mov rdi, [poly_img]
call    free
mov rdi, [deriv_img]
call    free
mov rdi, [deriv_real]
call    free

mov rax, 60
syscall

调用scanf函数:

get_input:
mov rdi, epsilon_format
mov rax, 1
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 call   scanf             ;first call --- works fine
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
movsd   qword [epsilon], xmm0 ;epsilon
mov rdi, order_format
mov rsi, order
mov rax,0
call    scanf       ;order

mov r10, [order]
inc r10

mov rax, 8
mul r10
mov rdi, rax
call    malloc
mov [poly_real], rax 

mov r10, [order]
inc r10
mov rax, 8
mul r10
mov rdi, rax
call    malloc
mov [poly_img], rax


mov r10, [order]
inc r10
mov [counter], r10

.loop:
mov rdi, coeff_format
mov rsi, degree
mov rax, 2
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    call    scanf       ;second call - works fine
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov r8, [degree]
movsd   [poly_real+r8*8], xmm0
movsd   [poly_img+r8*8], xmm1
mov r10, [counter]
dec r10
mov [counter], r10
cmp r10, 0
ja  .loop       ;polynom

mov rdi, initial_format
mov rax, 2
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    call    scanf       ;third call - segmentation fault
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
movsd   qword[z_n_real], xmm0
movsd   qword[z_n_img], xmm1    ;initial

mov rdi, [order]
mov rsi, [poly_real]
mov rdx, [poly_img]
call    derivate

mov [deriv_img], rbx
mov [deriv_real], rax

ret

错误:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a6a4cf in _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>, argptr=argptr@entry=0x7fffffffdbc8,
    errp=errp@entry=0x0) at vfscanf.c:2444

1 个答案:

答案 0 :(得分:1)

您没有传递指针arg来存储FP转换结果的位置。你在scanf上进行了段错误,其中rsi(第二个整数/指针arg)包含垃圾。就像你在C中写scanf("%g");

一样

您的第一个scanf没有错误,因为RSI仍然保持argv从进入main。这就是你存储转换后的第一个双倍的地方。

  

目标是获得FP args。 scanf是否根据rax中提到的F​​P变量的数量自动将所需的值存储在xmm寄存器中?

不,这是向后的,只适用于 FP args传递给x86-64 SysV ABI中的函数。

您传递scanf 指针,并将转化结果存储在那里。如果它返回多个结果,则用C

(double a,double b, int i, int j) = scanf("%g%g%i%i");

但是C不是perl;这甚至不是有效的C语法,C函数只能返回单个值。 (并且具有固定的返回类型,因此它不能是具有依赖于格式字符串的类型的结构。)

不幸的是,没有办法像我在C中写的那样,除了通过传递输出指针args来模仿它,就像你已经在进行整数转换一样!

您从asm调用的scanf与您从C调用的机器代码函数相同;查看编译器输出,看看你的C版本如何用实际的指针args编译。

顺便说一下,你对scanf调用约定如何工作的想法可以在asm中实现,但不容易,因为你不能索引寄存器文件。

除了分支或自修改代码之外,您不能编写设置n XMM寄存器的代码块。在可能的内部实现中,将在内存中构建一个数组,然后将其读入具有完全展开循环的寄存器,如movsd xmm0, [rsp+0] / movsd xmm1, [rsp+8]或其他任何内容,以及另一个用于整数返回寄存器的数组。

因此,您为scanf想象的调用约定对调用者来说会很方便,但对于被调用者来说不方便,甚至比将XMM寄存器args转储到它可以在可变参数函数的开头索引的数组更多当al != 0时(这就是gcc在编译可变参数函数时实际执行的操作,以及为什么glibc printf如果用未对齐的堆栈和al != 0调用它会发生段错误。)

但这不是一个真正的反驳论证,因为对于返回固定列表值的非可变函数来说会很好,就像SysV调用约定的复杂arg-&gt;寄存器映射对于正常函数来说很好并且只是不方便对于可变函数。

它不起作用的真正原因是C根本不支持多个返回值。您需要使用输出参数或结构返回。但是struct 实际上可以在多个寄存器中返回。