我需要在程序集中编写一个程序,需要使用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
答案 0 :(得分:1)
您没有传递指针arg来存储FP转换结果的位置。你在scanf
上进行了段错误,其中rsi
(第二个整数/指针arg)包含垃圾。就像你在C中写scanf("%g");
您的第一个scanf没有错误,因为RSI仍然保持argv
从进入main。这就是你存储转换后的第一个双倍的地方。
目标是获得FP args。 scanf是否根据
rax
中提到的FP变量的数量自动将所需的值存储在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
实际上可以在多个寄存器中返回。