Looking at the difference between the generated assembly.
Foo::Foo() [base object constructor]: <
pushq %rbp <
movq %rsp, %rbp <
movq %rdi, -8(%rbp) <
movq -8(%rbp), %rax <
pxor %xmm0, %xmm0 <
movss %xmm0, (%rax) ; 1. <
nop <
popq %rbp <
ret <
my_func(): my_func():
pushq %rbp pushq %rbp ; Save previous stack frame addr
movq %rsp, %rbp ; 4. movq %rsp, %rbp ; Address of current stack frame as new base ptr
subq $16, %rsp ; save 16 bytes for local data | movl $0, %eax ; move value 0 to function return register
movq %rdi, -8(%rbp) ; 3. | popq %rbp ; unwind the stack to exit function
movq -8(%rbp), %rax <
movq %rax, %rdi ; 2. <
call Foo::Foo() [complete object constructor] <
movq -8(%rbp), %rax <
leave <
ret ret
movss %xmm0, (%rax)
Caused the SIGSEGV. Using gdb: p/x %rax points to 0x4004d6 which is the address of my_func(). Attempt to write to the stack is the problem. Memory pages are write or execute (read) only. So why is %rax pointing there?
-
movq %rax, %rdi
This line was when the %rax was last written to, in my_func. What did %rdi contain? -
movq %rdi, -8(%rbp)
Contained whatever was at the address of %rbp - 8. What's in %rbp? The stack's base pointer of course. This was 0x4004d6.
The mov instructions write things. In the assembly code of int my_func(), besides the standard function setup/teardown, there was only movl $0, %eax which isn't going to cause a problem.
Registers
%rbp : base pointer of current stack frame (called %ebp in 32 bit) %rsp : stack pointer (top element) %eax : return value of a function, 32 bit register %rax : 64 bit register, same use as %eax %rdi : 64 bit general purpose register
Instructions
movss : move scalar single precision floating point value (copies 32 lowest bits from a XMM 128 bit register) pxor : logical exclusive or movl : move long (32 bit) movq : move quad word (64 bit) pushq : push quad word onto the stack
Suffix b : 8b aka byte s : single 32b float w : 16b l : 32b int or 64b float q : 64b t : 80b, 10 bytes
Here's what happen if you don't lie to the compiler.

I.e.
Notice these very important lines
which left room on the stack for the Foo object. There was no change in
my_func()
code.