littlekernel / lk

LK embedded kernel
MIT License
3.11k stars 613 forks source link

GCC 12.1.0 x86-64 build is broken #331

Open heatd opened 2 years ago

heatd commented 2 years ago

GCC generates SSE instructions in thread_init_early(), which makes the kernel die at runtime with "unhandled invalid op, halting".

travisg commented 2 years ago

Yeah, that's a tough problem. I've known about it for a while and it only really shows up on x86-64 AFAIK.

The problem is there is code in LK that wants to use floating point (some apps, fpu test code, etc) and code that should not (kernel, drivers, interrupt controllers, etc). The latter shouldn't use it because it can be invoked from an interrupt handler and since the interrupt handler code doesn't save fpu state, at worst it'll corrupt whatever the kernel application state is.

Usually kernels avoid this problem by simply forbidding x87/SSE/AVX entirely in the kernel, but since LK is a mixed environment where the kernel and applications to the kernel are compiled together you end up without a clear line.

What LK has done on most arches is simply avoid using floating point code in any core kernel bits and then assume the compiler wont use the instructions. Alas, x86 and especially newer GCC and clang compilers, love to use SSE at the drop of a hat to do things like bulk zero fill stuff (what thread_init_early() is probably doing). What I'd love to see is a switch that simply disables this behavior: only use FPU for FPU stuff, and then the status quo would be met. Otherwise I only see two solutions:

a) define some way in the build system to mark modules as 'may use fpu' and 'no fpu' and then segregate modules accordingly. Would work well except for shared bits like libc (printf for example). Dunno what to do about it. b) find a switch to disable this feature on the compiler. c) add full fpu save to the interrupt/exception glue on x86 and then just let the compiler use it however it wants.

C is probably the easieest to do, though x86 has multiple mechanisms to save fpu state so will need to do some thinking about that. Not as ideal performance wise, but really x86 is not LK's main platform, so getting it to work is better than getting it to work optimally.

travisg commented 1 year ago

I think I've mostly solved this problem in the interim by taking the B route, though I think there are a few places that may still trigger the problem. Now, on x86, by default code is compiled without any fpu support and various modules have to opt in at the module or file level to add it.

The problematic parts are shared modules like libc, which may want both fpu and non fpu case. Printf is a good example of this. For the moment printf is simply compiled without fpu and so %f stopped working in that case.