Fix stack pointer alignment for x86_64 platforms

The x86_64 ABI requires the stack pointer to be 16-byte aligned before the
call of a function and decreased by 8 at the function entrypoint (after
the return address has been pushed to the stack).

Currently, when a new Genode thread gets created, the initial stack
pointer is aligned to 16 byte. On Genode/Linux, the thread entry function
is entered by a 'call' instruction, so the stack pointer alignment at the
function entrypoint is correct. On Fiasco.OC and NOVA, however, the thread
entry function gets executed without a return address being pushed to the
stack, so at the function entrypoint the stack pointer is still aligned to
16 byte, which can cause problems with compiler-generated SSE
instructions.

With this patch, the stack pointer given to a new thread gets aligned to
16 bytes and decreased by 8 by default, since most of the currently
supported base platforms execute the thread entry function without pushing
a return address to the stack. For base-linux, the stack pointer gets
realigned to 16 bytes before the thread entry function gets called.

Fixes #1043.
This commit is contained in:
Christian Prochaska
2014-01-31 16:50:08 +01:00
committed by Christian Helmuth
parent a19d491fbd
commit 4b420f6e71
6 changed files with 26 additions and 9 deletions

View File

@ -153,6 +153,12 @@ Thread_base::Context *Thread_base::_alloc_context(size_t stack_size)
context->stack_base = ds_addr; context->stack_base = ds_addr;
context->ds_cap = ds_cap; context->ds_cap = ds_cap;
/*
* The value at the top of the stack might get interpreted as return
* address of the thread start function by GDB, so we set it to 0.
*/
*(addr_t*)context->stack_top() = 0;
return context; return context;
} }

View File

@ -122,9 +122,7 @@ void Thread_base::start()
threadlib_initialized = true; threadlib_initialized = true;
} }
/* align initial stack to 16 byte boundary */ _tid.tid = lx_create_thread(Thread_base::_thread_start, stack_top(), this);
void *thread_sp = (void *)((addr_t)(stack_top()) & ~0xf);
_tid.tid = lx_create_thread(Thread_base::_thread_start, thread_sp, this);
_tid.pid = lx_getpid(); _tid.pid = lx_getpid();
/* wait until the 'thread_start' function got entered */ /* wait until the 'thread_start' function got entered */

View File

@ -83,8 +83,7 @@ L(thread_start):
.cfi_startproc; .cfi_startproc;
/* Clearing frame pointer is insufficient, use CFI. */ /* Clearing frame pointer is insufficient, use CFI. */
.cfi_undefined %eip; .cfi_undefined %eip;
/* Note: %esi is zero. */ xorl %ebp,%ebp /* terminate the stack frame */
movl %esi,%ebp /* terminate the stack frame */
call *%ebx call *%ebx
#ifdef PIC #ifdef PIC
call L(here) call L(here)

View File

@ -17,6 +17,9 @@
lx_clone: lx_clone:
.cfi_startproc .cfi_startproc
/* Align the new stack pointer to 16 bytes. */
andq $0xfffffffffffffff0, %rsi
/* Insert the argument onto the new stack. */ /* Insert the argument onto the new stack. */
subq $16,%rsi subq $16,%rsi
movq %rcx,8(%rsi) movq %rcx,8(%rsi)
@ -51,7 +54,7 @@ L(thread_start):
/* Clear the frame pointer. The ABI suggests this be done, to mark /* Clear the frame pointer. The ABI suggests this be done, to mark
the outermost frame obviously. */ the outermost frame obviously. */
xorl %ebp, %ebp xorq %rbp, %rbp
/* Set up arguments for the function call. */ /* Set up arguments for the function call. */
popq %rax /* Function to call. */ popq %rax /* Function to call. */

View File

@ -100,11 +100,16 @@ namespace Genode {
public: public:
/** /**
* Top of stack aligned to 16 byte * Top of stack
* *
* The alignment is also sufficient for the AMD64 ABI. * The alignment matches an initial stack frame, which is
* sufficient for the AMD64 ABI (stack top + 8 is 16-byte
* aligned).
*/ */
addr_t stack_top() const { return (addr_t)_stack & ~0xf; } addr_t stack_top() const
{
return ((addr_t)_stack & ~0xf) - sizeof(addr_t);
}
/** /**
* Virtual address of the start of the stack * Virtual address of the start of the stack

View File

@ -153,6 +153,12 @@ Thread_base::Context *Thread_base::_alloc_context(size_t stack_size)
context->stack_base = ds_addr; context->stack_base = ds_addr;
context->ds_cap = ds_cap; context->ds_cap = ds_cap;
/*
* The value at the top of the stack might get interpreted as return
* address of the thread start function by GDB, so we set it to 0.
*/
*(addr_t*)context->stack_top() = 0;
return context; return context;
} }