:kernel: Fix stack alignment in syscall entry on x86_64.

The user iframe and associated data that the syscall entry pushes to the
stack directly were causing the stack to be mis-aligned by 8 bytes. Since
we re-aligned %rsp afterwards, for most usecases this wasn't a problem.
However, since we stored the pre-realinged %rsp in %rbp (as we need it to
access the iframe data), this also meant that anything which depended on
%rbp being 16-byte-aligned would run into serious problems.

As it turned out, GCC 7 assumed that %rbp was indeed 16-byte-aligned, and
so optimized certain accesses to use SSE instructions that depended on this
alignment. Since inside any callstack begining with a syscall this was not
the case, a "General Protection Exception" resulted (see #14160 for an example)
at the first usage of such an instruction. I wasn't really sure what was going
on when it first came up, and so "fixed" it by disabling the GCC optimization
that used such instructions. Replacing the -fdisable... with -mstackrealign thus
also "fixes" the problem, as I discovered earlier today, as it forces GCC to
realign the stack in function prologues.

So instead of rounding %rsp down to the nearest aligned address after the
pushes are complete, we offset %rsp by the amount the pushes are not,
thus fixing both %rsp and %rbp in syscall handling routines. This of course
depends on syscall_rsp being already aligned, which it is.

Thanks to PulkoMandy and js for the advice and guidance (and PulkoMandy
for the ASCII art), as this is essentially my first time working with
kernel assembly.
This commit is contained in:
Augustin Cavalier 2018-06-16 16:58:06 -04:00
parent 77c8afb47f
commit 4a459f066d
2 changed files with 3 additions and 16 deletions

View File

@ -75,21 +75,6 @@ KernelMergeObject kernel_core.o :
: $(TARGET_KERNEL_PIC_CCFLAGS)
;
if $(HAIKU_GCC_VERSION_$(TARGET_PACKAGING_ARCH)[1]) >= 7
&& ( $(TARGET_ARCH) = x86 || $(TARGET_ARCH) = x86_64 ) {
# With the rtl-stv1 pass on these files, the kernel panics towards the end
# of the boot process with a "General Protection Exception", see
# https://dev.haiku-os.org/ticket/14160. The pass itself vectorizes
# a significant number of otherwise-scalar operations, which may be why
# disabling it fixes the problem.
#
# At time of writing, GCC does not seem to have a way to disable
# vectorization as a result of optimization without disabling
# FPU usage altogether, which of course is not what we want and
# breaks when using libstdc++'s headers anyway.
ObjectC++Flags commpage.o signal.o thread.o : -fdisable-rtl-stv1 ;
}
# Generate the header defining macros for C++ structure sizes.
local kernelC++StructSizesHeader = [ FGristFiles kernel_c++_struct_sizes.h ] ;
TARGET_HDRS_$(TARGET_PACKAGING_ARCH) on $(kernelC++StructSizesHeader)

View File

@ -343,6 +343,9 @@ FUNCTION(x86_64_syscall_entry):
movq %rsp, %gs:ARCH_THREAD_user_rsp
movq %gs:ARCH_THREAD_syscall_rsp, %rsp
// The following pushes de-align the stack by 8 bytes, so account for that first.
sub $8, %rsp
// Set up an iframe on the stack (R11 = saved RFLAGS, RCX = saved RIP).
push $USER_DATA_SELECTOR // ss
push %gs:ARCH_THREAD_user_rsp // rsp
@ -357,7 +360,6 @@ FUNCTION(x86_64_syscall_entry):
// Frame pointer is the iframe.
movq %rsp, %rbp
andq $~15, %rsp
// Preserve call number (R14 is callee-save), get thread pointer.
movq %rax, %r14