代码之家  ›  专栏  ›  技术社区  ›  Rotem Barak

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

  •  2
  • Rotem Barak  · 技术社区  · 8 年前

    我需要在汇编中编写一个程序,需要使用 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 回复  |  直到 8 年前
        1
  •  1
  •   Peter Cordes    8 年前

    您没有为存储FP转换结果的位置传递指针参数。您在 scanf 哪里 rsi (第二个整数/指针参数)保存垃圾。就像你写的一样 scanf("%g"); 在C。

    你的第一次扫描没有故障,因为RSI仍然有效 argv 从进入main。这就是存储转换的第一个双精度。

    目标是获取FP参数。scanf不会根据中提到的FP变量数自动将所需值存储在xmm寄存器中 rax ?

    不,那是向后的,只对 通过 在x86-64 SysV ABI中,FP args指向函数。

    你通过了 scanf公司 指针 ,并将转换结果存储在那里。如果它返回多个结果,您可以用C编写

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

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

    不幸的是,除了通过传递输出指针args来模拟它之外,没有办法像我在C中写的那样做任何事情,就像您已经在进行整数转换一样!

    这个 scanf公司 从asm调用的机器码函数与从C调用的机器码函数相同;查看编译器输出,了解C版本如何使用实际指针参数进行编译。


    顺便说一句,你的想法 scanf公司 的调用约定可以在asm中实现,但并不容易,因为您无法为寄存器文件编制索引。

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

    你想象的这个电话会议 scanf公司 这对调用者来说很方便,但对被调用者来说很不方便,甚至比将XMM寄存器参数转储到一个数组更为方便,该数组可以在变量函数开始时索引 al != 0 (这就是gcc在编译变量函数时实际做的事情,也是为什么glibc printf SEGFULTS,如果使用未对齐的堆栈调用它 al!=0 )。

    但这并不是真正的反参数,因为对于返回固定列表值的非变量函数来说,这很好,就像SysV调用约定的复杂arg->寄存器映射对于普通函数很好,而对于可变函数则很不方便。

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