Reputation: 7810
I'm trying to create a trivial console assembly program with arguments. Here's the code:
.section __TEXT,__text
.globl _main
_main:
movl $0, %edi
callq _exit
Here's the compile and link script:
as test.s -o test.o
ld test.o -e _main -o test -lc
Now the program either fails with a segmentation fault, or executes without error, depending on the argument count:
$ ./test
Segmentation fault: 11
$ ./test 1
$ ./test 1 2
$ ./test 1 2 3
Segmentation fault: 11
$ ./test 1 2 3 4
$ ./test 1 2 3 4 5
Segmentation fault: 11
And so on.
Under the LLDB I see a more informative error:
Process 16318 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x00007fffad14b2fa libdyld.dylib`stack_not_16_byte_aligned_error
libdyld.dylib`stack_not_16_byte_aligned_error:
-> 0x7fffad14b2fa <+0>: movdqa %xmm0, (%rsp)
0x7fffad14b2ff <+5>: int3
libdyld.dylib`_dyld_func_lookup:
0x7fffad14b300 <+0>: pushq %rbp
0x7fffad14b301 <+1>: movq %rsp, %rbp
Indeed, if I stop the execution at the first line of the program, I see that the stack is not 16-byte aligned for some argument count, while it is aligned for another. Although the System V ABI for AMD64 states that:
The stack pointer holds the address of the byte with lowest address which is part of the stack. It is guaranteed to be 16-byte aligned at process entry.
What am I missing?
Upvotes: 2
Views: 641
Reputation: 93172
I guess on OS X the kernel doesn't guarantee a stack alignment on entry to main
. You have to manually align the stack. Fortunately, this is rather easy, just zero-out the least four bits of the stack pointer. In case you need to fetch the argument vector or other data, make sure to store the original stack pointer somewhere:
_main:
mov %rsp,%rax # copy original stack pointer
and $-16,%rsp # align stack to 16 bytes
push %rax # save original stack pointer
push %rbp # establish...
mov %rsp,%rbp # ...stack frame
... # business logic here
leave # tear down stack frame
pop %rsp # restore original stack pointer
... # exit process
You also need to mentally keep track of your stack alignment. It might be easier to have main
do nothing but the stack alignment and then calling your actual main function so you can use a normal stack frame in it:
_main:
mov %rsp,%rbx # save original stack pointer
and $-16,%rsp # align stack to 16 bytes
call _my_main # call actual main function
mov %rbx,%rsp # restore original stack pointer
... # exit process
For your particular example program, you can just use this minimal code:
_main:
and $-16,%rsp # align stack to 16 bytes
xor %edi,%edi # set exit status to zero
call _exit # exit program
Upvotes: 2