scanf throws Segmentation fault (core dumped) when using nasm + gcc on linux 64bit
When compiling below code:
using:
and then run
it runs, print: enter a number:
but then crashes and prints:
Segmentation fault (core dumped)
So printf works fine but scanf not.
What am I doing wrong with scanf so?
Use sub rsp, 8
/ add rsp, 8
at the start/end of your function to re-align the stack to 16 bytes before your function does a call
.
Or better push/pop a dummy register, e.g. push rdx
/ pop rcx
, or save/restore a call-preserved register like RBP.
On function entry, RSP is 8 bytes away from 16-byte alignment because the call
pushed an 8-byte return address. See Printing floating point numbers from x86-64 seems to require %rbp to be saved,
main and stack alignment, and Calling printf in x86_64 using GNU assembler. This is an ABI requirement which you used to be able to get away with violating when there weren't any FP args for printf. But not any more.
gcc's code-gen for glibc scanf now depends on 16-byte stack alignment even when AL == 0
.
It seems to have auto-vectorized copying 16 bytes somewhere in __GI__IO_vfscanf
, which regular scanf
calls after spilling its register args to the stack1. (The many similar ways to call scanf share one big implementation as a back end to the various libc entry points like scanf
, fscanf
, etc.)
I downloaded Ubuntu 18.04's libc6 binary package: https://packages.ubuntu.com/bionic/amd64/libc6/download and extracted the files (with 7z x blah.deb
and tar xf data.tar
, because 7z knows how to extract a lot of file formats).
I can repro your bug with LD_LIBRARY_PATH=/tmp/bionic-libc/lib/x86_64-linux-gnu ./bad-printf
, and also it turns out with the system glibc 2.27-3 on my Arch Linux desktop.
With GDB, I ran it on your program and did set env LD_LIBRARY_PATH /tmp/bionic-libc/lib/x86_64-linux-gnu
then run
. With layout reg
, the disassembly window looks like this at the point where it received SIGSEGV:
So it copied two 8-byte objects to the stack with movq
+ movhps
to load and movaps
to store. But with the stack misaligned, movaps [rbp-0x470],xmm0
faults.
I didn't grab a debug build to find out exactly which part of the C source turned into this, but the function is written in C and compiled by GCC with optimization enabled. GCC has always been allowed to do this, but only recently did it get smart enough to take better advantage of SSE2 this way.
Footnote 1: printf / scanf with AL != 0
has always required 16-byte alignment because gcc's code-gen for variadic functions uses test al,al / je to spill the full 16-byte XMM regs xmm0..7 with aligned stores in that case. __m128i
can be an argument to a variadic function, not just double
, and gcc doesn't check whether the function ever actually reads any 16-byte FP args.
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.