-3

Getting segmentation fault in the function, due to instruction movq -8(%rbp), %rax, one before the printf. I can't understand why ? Note : this is not gcc generated assembly, but by compiler i am writing. Assembly code is almost similar to what gcc generates.

.text
.globl  main
.type   main, @function
main:
pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
movl    $2, -4(%rbp)
leaq    -4(%rbp), %rax
movl    %eax, %edi
movb    $0, %al
call    fcvt2
movl    %eax, -4(%rbp)
leaq    .LC0(%rip), %rdi
movl    -4(%rbp), %esi
movb    $0, %al
call    printf
leave
ret

.globl  fcvt2
.type   fcvt2, @function
fcvt2:
pushq   %rbp
movq    %rsp, %rbp
subq    $32, %rsp
movq    %rdi, -8(%rbp)
leaq    .LC1(%rip), %rdi
movq    -8(%rbp), %rax
movl    (%rax), %esi
movb    $0, %al
call    printf
movq    -8(%rbp), %rax
movl    (%rax), %edi
movl    %edi, %eax
leave
ret

.section    .rodata
.LC1:
.string "It should be : %d\f"
.LC0:
.string "%d\n"

And C Program is :

int fcvt2(int *ip) {
    int i;
    printf("It should be : %d\f", *ip); 
    return *ip;
}

void main() {
    int i;
    i = 2;
    i = fcvt2(&i);
    printf("%d\n",i);
    return;
}

gdb output at fault point:

rax            0xffffdd4c   4294958412
rbx            0x0  0
rcx            0x7ffffff7   2147483639
rdx            0x7ffff7dd3780   140737351858048
rsi            0x7fffffffdd48   140737488346440
rdi            0xffffdd4c   4294958412
rbp            0x7fffffffdd30   0x7fffffffdd30
rsp            0x7fffffffdd00   0x7fffffffdd00
r8             0x0  0
r9             0x9  9
r10            0x7ffff7dd1b78   140737351850872
r11            0x246    582
r12            0x400430 4195376
r13            0x7fffffffde30   140737488346672
r14            0x0  0
r15            0x0  0
rip            0x40059c 0x40059c <fcvt2+20>
eflags         0x10206  [ PF IF RF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0
melpomene
  • 84,125
  • 8
  • 85
  • 148
Kevin
  • 1
  • 3
  • 2
    What are the contents of the registers at the faulting instruction? What did your debugger say? – melpomene Oct 22 '17 at 20:33
  • 1
    Off topic: if you don't need to preserve the upper bytes of EAX, [`movb $0, %al` is less efficient than `xor %eax, %eax`](https://stackoverflow.com/questions/33666617/what-is-the-best-way-to-set-a-register-to-zero-in-x86-assembly-xor-mov-or-and). I think you're doing this for the x86-64 SysV variadic function convention, and you're right that only `%al` needs to say how many XMM register args there are, not the whole `%eax`, so you got that right. But zeroing `eax` is the most efficient way to zero `al`. (Related: Haswell/Skylake partial registers https://stackoverflow.com/q/45660139/224132) – Peter Cordes Oct 22 '17 at 20:39
  • In the C code, what is `\f`? Replacing that with `\n` gives no strange character and no segfault for the C code. – Weather Vane Oct 22 '17 at 20:41
  • 1
    The asm for `fcvt2` doesn't look obviously wrong, just inefficient spilling/reloading `ip` before dereferencing. Which `movq -8(%rbp), %rax` is segfaulting? The one after printf, I assume, because it's even less plausible that the first load would fault, because you just stored to that address 2 instructions earlier. Add register contents from `gdb` to make this a [mcve]. – Peter Cordes Oct 22 '17 at 20:44
  • @WeatherVane Form feed. – melpomene Oct 22 '17 at 20:46
  • @melpomene ugh of course... there is no fault in the C code. – Weather Vane Oct 22 '17 at 20:55
  • No, it crashes at `mov (%rax),%esi`. `rax` doesn't contain a valid address at this moment – n. m. could be an AI Oct 22 '17 at 21:02
  • 1
    @n.m.: That makes more sense. `movl %eax, %edi` in the caller truncates the pointer. – Peter Cordes Oct 22 '17 at 21:09
  • @peter cordes. Thanx for input.I will surely modify it to use xor instead of mov. – Kevin Oct 22 '17 at 21:13

1 Answers1

1

movl %eax, %edi in the caller truncates the pointer arg to fcvt2. You actually segfault on mov (%rax),%esi. rax, not the instruction before it like you claimed. (Time for a refresher on your GDB skills?)

leaq -4(%rbp), %rax generated it correctly in %rax, but then your compiler forgot that it was a 64-bit pointer to a 32-bit value. (Ideally you'd want to leaq -4(%rbp), %rdi directly into the arg register.)


Off topic: if you don't need to preserve the upper bytes of EAX, movb $0, %al is less efficient than xor %eax, %eax. I think you're doing this for the x86-64 SysV variadic function convention, and you're right that only %al needs to say how many XMM register args there are, not the whole %eax, so you got that right. But zeroing eax is the most efficient way to zero al. Of course, you don't need to do this at all for non-variadic functions, but your compiler is obviously still in the just-get-it-working phase, so doing it unconditionally isn't a correctness problem; you never need to pass anything else in rax, and function calls are always assumed to clobber rax.

(Also related: Haswell/Skylake partial registers have false dependencies: al isn't renamed separately from rax anymore)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Thanx a lot. that worked. I may have forgot to keep track of the fact that rax has pointer, i think it would simply seeing as integer and using 32-bit variation of mov instruction. And yeah, in hurry i wrote the wrong location as to where the fault is happening. – Kevin Oct 22 '17 at 21:17
  • @Kvn: It's really important to make sure your questions have accurate info. Double-checking might even have clued you in to the problem without having to post the question, once you saw which instruction was actually faulting. – Peter Cordes Oct 22 '17 at 21:57