2

I need to write a program in assembly that needs to get some user arguments using scanf. The problem is that in the third call, I get segmentation fault.

Briefly about the program purpose.
It should get input from the user in this format:

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

and store this data in data structures.


String formats:

    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

Code:

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

Calling scanf function:

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

The error:

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
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Rotem Barak
  • 154
  • 1
  • 9
  • 1
    You don't set `rsi` for your first `scanf`, and you're incorrectly setting `AL=1` when you didn't actually *pass* any FP args in xmm0. But it's not looking for any so that doesn't matter (unless you misaligned RSP and telling it there were FP args made it do 16-byte stores and crash). Anyway, in C you did `scanf("blah blah %g");` with no `&epsilon` arg. – Peter Cordes Apr 22 '18 at 11:04
  • And BTW, you have some of the same bugs I commented on in [another question about this same assignment](https://stackoverflow.com/questions/49958703/nasm-not-scaning-input-using-scanf#comment86944732_49958703), using qword loads into `r10` for `order` when you have a 32-bit `int` conversion (`%d`). IDK why you're using `r10` for that, or why you keep your loop counter in memory instead of a call-preserved register like `ebx` or `r12d`. – Peter Cordes Apr 22 '18 at 11:08
  • The target is to get FP args. Doesn't scanf automatically storing the desired values in the xmm registers according to the number of FP variables mentioned in rax? – Rotem Barak Apr 22 '18 at 17:41
  • I tried to open a frame (using `push rbp mov rbp rsp`) but got segmentation faults doing so. Can you please elaborate about the stack alignment pattern using scanf? – Rotem Barak Apr 22 '18 at 17:44
  • No, you pass `scanf` pointers, and it stores the results there. If it returned multiple results, you'd write in C `(double a,double b, int i, int j) = scanf("%g%g%i%i");`. But C isn't perl so that's not even valid syntax, and functions can only return a single value. (And have a fixed return *type*.) Unfortunately there's no way to do anything like what I wrote, except for a weak imitation of it by passing output pointer args, like you're already doing for integer conversions! The `scanf` you call from asm is the same machine-code function you call from C; look at compiler output. – Peter Cordes Apr 22 '18 at 20:39
  • Inside `get_input`, RSP is 16-byte aligned with no pushes, because you called it from `main` when `main` still had the stack 8 bytes away from aligned on function entry. That's fine, your `get_input` is a private helper function so you can invent a custom calling convention where RSP is 16-byte aligned *after* a `call` instead of before, because that's the most convenient thing here. – Peter Cordes Apr 22 '18 at 20:43
  • Oh, I just realized why your first `scanf` doesn't fault: RSI still holds `argv` from entry into `main`. So that's where you're storing the first `double` you convert. – Peter Cordes Apr 23 '18 at 03:13
  • The worst things happens when bugs are hiding behind other bugs. – Rotem Barak Apr 23 '18 at 09:23
  • @PeterCordes Now it works. What should i do? Post the working code and tag you? What's the convention? – Rotem Barak Apr 23 '18 at 09:24
  • Sure, post an answer if you want. Unfortunately, I doubt anyone else with the same bug will know what to search on to find this. That's why I only answered in comments instead of posting an answer. (A less-generic title might help, though; I'll edit and post an answer now that we know what you thought scanf was going to do.) – Peter Cordes Apr 23 '18 at 09:28

1 Answers1

1

You didn't pass a pointer arg for where to store FP conversion results. You segfault on a scanf where rsi (the 2nd integer/pointer arg) holds garbage. Just like if you'd written scanf("%g"); in C.

Your first scanf doesn't fault because RSI still holds argv from entry into main. So that's where you're storing the first double you convert.

The target is to get FP args. Doesn't scanf automatically storing the desired values in the xmm registers according to the number of FP variables mentioned in rax?

No, that's backwards and only works for passing FP args to functions, in the x86-64 SysV ABI.

You pass scanf pointers, and it stores the conversion results there. If it returned multiple results, you'd write in C

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

But C isn't perl; that's not even valid C syntax, and C functions can only return a single value. (And have a fixed return type, so it can't be a struct with types that depend on the format string.)

Unfortunately there's no way to do anything like what I wrote in C, except for a weak imitation of it by passing output pointer args, like you're already doing for integer conversions!

The scanf you call from asm is the same machine-code function you call from C; look at compiler output to see how your C version compiles with actual pointer args.


BTW, your idea of how scanf's calling convention worked would be possible to implement in asm, but not easily because you can't index the register file.

You can't write a block of code that sets the nth XMM register except by branching, or self-modifying code. On likely internal implementation would build an array in memory and then read it into registers with a fully-unrolled loop like movsd xmm0, [rsp+0] / movsd xmm1, [rsp+8] or whatever, and another array for integer return registers.

So this calling convention you were imagining for scanf would be convenient for the caller, but inconvenient for the callee, even moreso than dumping the XMM register args to an array it can index at the start of a variadic function when al != 0 (that's what gcc actually does when compiling variadic functions, and why glibc printf segfaults if you call it with a misaligned stack and al != 0).

But that's not a real counter-argument, because it would be fine for non-variadic functions that return a fixed list values, just like the SysV calling convention's complex arg->register mapping is fine for normal functions and only inconvenient for variadic functions.

The real reason it doesn't work that way is that C doesn't support multiple return values, at all, ever. You need to use output args, or struct returns. A struct can actually be returned in multiple registers, though.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847