Implement saving decoupled context from syshandler.

Rewrite the syshandler assembly routine to save the full state of user threads,
like the sighandler would. With fpstate, it does so by writing straight to the
thread context struct, so there is no need to do an intermediate copy.

PiperOrigin-RevId: 514751894
This commit is contained in:
Konstantin Bogomolov
2023-03-07 09:16:13 -08:00
committed by gVisor bot
parent 63ca40b1c2
commit 39f2721c9b
10 changed files with 221 additions and 199 deletions
+2 -9
View File
@@ -675,10 +675,7 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool
if ctx.State == sysmsg.ContextStateSyscallCanBePatched {
ctx.State = sysmsg.ContextStateSyscall
// Syshandler not implemented with context decoupling yet.
if !contextDecouplingExp {
shouldPatchSyscall = true
}
shouldPatchSyscall = true
}
if ctx.State == sysmsg.ContextStateSyscall || ctx.State == sysmsg.ContextStateSyscallTrap {
@@ -750,10 +747,6 @@ func (s *subprocess) Unmap(addr hostarch.Addr, length uint64) {
}
func (s *subprocess) PullFullState(c *context, ac *arch.Context64) error {
if !contextDecouplingExp {
return s.PullFullArchState(c, ac)
}
if s != c.subprocess {
panic("Attempted to PullFullState for context that is not used in subprocess")
}
@@ -857,10 +850,10 @@ func (s *subprocess) getSysmsgThread(tregs *arch.Registers, c *context, ac *arch
sysThread.setMsg(sysmsg.StackAddrToMsg(sentryStackAddr))
sysThread.msg.Init()
sysThread.msg.Self = uint64(sysmsgStackAddr + sysmsg.MsgOffsetFromSharedStack)
sysThread.msg.Syshandler = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_syshandler))
sysThread.msg.SyshandlerStack = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr()))
sysThread.msg.ContextRegion = uint64(stubContextRegion)
sysThread.msg.ContextID = c.cid
sysThread.msg.Syshandler = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_syshandler))
sysThread.msg.State.Set(sysmsg.ThreadStateDone)
@@ -211,74 +211,6 @@ func appendArchSeccompRules(rules []seccomp.RuleSet) []seccomp.RuleSet {
}...)
}
func (s *subprocess) PullFullArchState(c *context, ac *arch.Context64) error {
// Reset necessary registers.
regs := &ac.StateData().Regs
sysThread, err := s.getSysmsgThread(regs, c, ac)
if err != nil {
return err
}
msg := sysThread.msg
ctx := s.getThreadContextFromID(c.cid)
// In case of EventTypeSyscallTrap, we have only syscall argument
// registers and we need to trigger a signal in the stub process to get
// a full set of registers and an FPU state.
//
// In other cases, we have the full set of registers and need only copy
// the FPU state from a signal frame.
if ctx.State != sysmsg.ContextStateSyscallTrap {
s.saveFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac)
return nil
}
// In case of EventTypeSyscallTrap, the Sentry knows only the syscall
// number and syscall arguments and the target thread is stopped in the
// syshandler stub function. We need to ask syshandler to trigger a
// real syscall to get the full state.
ctx.Regs = regs.PtraceRegs
sysThread.waitEvent(sysmsg.ThreadStateSigact)
if msg.Err != 0 {
panic(fmt.Sprintf("stub thread failed: err %d line %d: %s", msg.Err, msg.Line, msg))
}
if ctx.State != sysmsg.ContextStateSyscall {
panic(fmt.Sprintf("unknown context state: state %v: %s", ctx.State, msg))
}
sysThread.fpuStateToMsgOffset, err = msg.FPUStateOffset()
if err != nil {
return err
}
// When we are triggering the real syscall instruction, we don't
// restore all syscall arguments and even the syscall number.
ctx.Regs.Rax = regs.Rax
ctx.Regs.Orig_rax = regs.Orig_rax
ctx.Regs.Rdi = regs.Rdi
ctx.Regs.Rsi = regs.Rsi
ctx.Regs.Rdx = regs.Rdx
ctx.Regs.R10 = regs.R10
ctx.Regs.R8 = regs.R8
ctx.Regs.R9 = regs.R9
regs.PtraceRegs = ctx.Regs
// The thread has restored all registers that could be changed in
// the syshandler stub function, but it is still in this function. We
// know the return address and let's set it so to be not affected if
// the stub code will be changed after save/restore.
regs.Rip = msg.RetAddr
s.saveFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac)
c.signalInfo = ctx.SignalInfo
return nil
}
func restoreArchSpecificState(regs *arch.Registers, t *thread, sysThread *sysmsgThread, msg *sysmsg.Msg, _ *arch.Context64) {
regs.Gs_base = msg.Self
@@ -187,19 +187,6 @@ func (s *subprocess) arm64SyscallWorkaround(t *thread, regs *arch.Registers) {
}
}
func (s *subprocess) PullFullArchState(c *context, ac *arch.Context64) error {
// We do not support syscall trap in ARM64 so just get the fp state from the
// signal frame and we are done.
regs := &ac.StateData().Regs
sysThread, err := s.getSysmsgThread(regs, c, ac)
if err != nil {
return err
}
ctx := s.getThreadContextFromID(c.cid)
s.saveFPState(sysThread.msg, ctx, sysThread.fpuStateToMsgOffset, c, ac)
return nil
}
func restoreArchSpecificState(regs *arch.Registers, t *thread, _ *sysmsgThread, msg *sysmsg.Msg, ac *arch.Context64) {
msg.TLS = uint64(ac.TLS())
}
@@ -177,8 +177,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
// because sysmsg can't be changed.
int32_t thread_state;
thread_state = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE);
if ((thread_state != THREAD_STATE_NONE) ||
(ucontext->uc_mcontext.gregs[REG_RSP] > (unsigned long)sp)) {
if (thread_state != THREAD_STATE_NONE) {
__atomic_store_n(&sysmsg->interrupt, 1, __ATOMIC_RELEASE);
return;
}
@@ -189,6 +188,13 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
__atomic_store_n(&sysmsg->interrupt, 0, __ATOMIC_RELAXED);
// Skip the fault instruction.
ucontext->uc_mcontext.gregs[REG_RIP] = sysmsg->ret_addr;
// If we're skipping the fault instruction and going straight to the user
// RIP we must also restore RSP and RFLAGS which are the last registers
// restored. We can take these values from ctx->ptregs because the
// syshandler saved them there and hasn't overwritten them before
// retriggering the interrupt.
ucontext->uc_mcontext.gregs[REG_RSP] = ctx->ptregs.rsp;
ucontext->uc_mcontext.gregs[REG_EFL] = ctx->ptregs.eflags;
}
// Handle faults in syshandler.
@@ -220,12 +226,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
// If a syscall instruction set is "mov sysno, %eax, syscall", it can be
// replaced on a function call which works much faster.
// Look at pkg/sentry/usertrap for more details.
//
// Exclude all syscalls which requires a full thread state to be handled.
if (siginfo->si_arch == AUDIT_ARCH_X86_64 && si_sysno != __NR_execveat &&
si_sysno != __NR_execve && si_sysno != __NR_fork &&
si_sysno != __NR_clone && si_sysno != __NR_vfork &&
si_sysno != __NR_rt_sigreturn && si_sysno != __NR_arch_prctl) {
if (siginfo->si_arch == AUDIT_ARCH_X86_64) {
uint8_t *rip = (uint8_t *)ctx->ptregs.rip;
// FIXME(b/144063246): Even if all five bytes before the syscall
// instruction match the "mov sysno, %eax" instruction, they can be a
@@ -314,49 +315,32 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
__atomic_store_n(&sysmsg->state, THREAD_STATE_NONE, __ATOMIC_RELEASE);
}
// Function arguments: %rdi,%rsi, %rdx, %rcx, %r8 and %r9.
// http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf
long __syshandler(long a1, long a2, long a3, long __unused, long a5, long a6) {
long sysno, a4, rip;
void __syshandler() {
struct sysmsg *sysmsg;
asm volatile(
"movq %%rax, %0\n"
"movq %%r10, %1\n"
: "=m"(sysno), "=m"(a4)
:
:);
asm volatile("movq %%gs:0, %0\n" : "=r"(sysmsg) : :);
struct thread_context *ctx = thread_context_addr(sysmsg);
// SYSMSG_STATE_PREP is set to postpone interrupts. Look at
// __export_sighandler for more details.
int thread_state = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE);
if (thread_state != THREAD_STATE_PREP) panic(thread_state);
ctx->signo = SIGSYS;
ctx->ptregs.rax = sysno;
ctx->ptregs.rdi = a1;
ctx->ptregs.rsi = a2;
ctx->ptregs.rdx = a3;
ctx->ptregs.r10 = a4;
ctx->ptregs.r8 = a5;
ctx->ptregs.r9 = a6;
ctx->ptregs.rsp = sysmsg->app_stack;
ctx->ptregs.rip = sysmsg->ret_addr;
int state = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE);
if (state != THREAD_STATE_PREP) panic(state);
struct thread_context *ctx = thread_context_addr(sysmsg);
ctx->state = CONTEXT_STATE_SYSCALL_TRAP;
ctx->ptregs.orig_rax = ctx->ptregs.rax;
ctx->ptregs.rax = (unsigned long)-ENOSYS;
ctx->signo = SIGSYS;
ctx->siginfo.si_addr = 0;
ctx->siginfo.si_syscall = sysno;
ctx->siginfo.si_syscall = ctx->ptregs.rax;
ctx->ptregs.rax = (unsigned long)-ENOSYS;
__atomic_store_n(&sysmsg->interrupt, 0, __ATOMIC_RELAXED);
thread_state = wait_state(sysmsg, THREAD_STATE_EVENT);
long sysret = ctx->ptregs.rax;
if (thread_state == THREAD_STATE_SIGACT) {
return -1;
}
get_fsbase(&ctx->ptregs);
long fs_base = ctx->ptregs.fs_base;
__atomic_store_n(&sysmsg->state, THREAD_STATE_NONE, __ATOMIC_RELEASE);
return sysret;
state = wait_state(sysmsg, THREAD_STATE_EVENT);
// Restore state
if (fs_base != ctx->ptregs.fs_base) {
set_fsbase(&ctx->ptregs);
}
}
void verify_offsets_amd64() {
@@ -13,66 +13,208 @@
// limitations under the License.
#include "sysmsg_offsets.h"
#include "sysmsg_offsets_amd64.h"
// Helper macros:
////////////////////////////////////////
// load_thread_context loads the address of the thread context slot for the current
// context.
// Clobbers %rflags; loads address into %rcx.
.macro load_thread_context_addr
movl %gs:offsetof_sysmsg_context_id, %ecx
shl $THREAD_CONTEXT_STRUCT_BITSHIFT, %rcx
add %gs:offsetof_sysmsg_context_region, %rcx
.endm
// prepare_enter_syshandler does the following:
// - saves all registers that are restorable onto the thread_context struct.
// - loads the address of the thread_context struct into %rcx.
.macro prepare_enter_syshandler
// Syshandler clobbers rflags (load_thread_context_addr does so for example).
// Therefore save it as the first thing we do.
pushfq
// load_thread_context_addr overwrites %rcx.
push %rcx
load_thread_context_addr
// Registers listed in order as written in ptregs:
movq %r15, offsetof_thread_context_ptregs_r15(%rcx)
movq %r14, offsetof_thread_context_ptregs_r14(%rcx)
movq %r13, offsetof_thread_context_ptregs_r13(%rcx)
movq %r12, offsetof_thread_context_ptregs_r12(%rcx)
movq %rbp, offsetof_thread_context_ptregs_rbp(%rcx)
movq %rbx, offsetof_thread_context_ptregs_rbx(%rcx)
movq %r11, offsetof_thread_context_ptregs_r11(%rcx)
movq %r10, offsetof_thread_context_ptregs_r10(%rcx)
movq %r9, offsetof_thread_context_ptregs_r9(%rcx)
movq %r8, offsetof_thread_context_ptregs_r8(%rcx)
movq %rax, offsetof_thread_context_ptregs_rax(%rcx)
pop %r15
movq %r15, offsetof_thread_context_ptregs_rcx(%rcx)
movq %rdx, offsetof_thread_context_ptregs_rdx(%rcx)
movq %rsi, offsetof_thread_context_ptregs_rsi(%rcx)
movq %rdi, offsetof_thread_context_ptregs_rdi(%rcx)
movq %rax, offsetof_thread_context_ptregs_orig_rax(%rcx)
movw %cs, offsetof_thread_context_ptregs_cs(%rcx)
movw %ss, offsetof_thread_context_ptregs_ss(%rcx)
// Don't bother save/restoring ds/es on amd64
// movw %ds, offsetof_thread_context_ptregs_ds(%rcx)
// movw %es, offsetof_thread_context_ptregs_es(%rcx)
movw %fs, offsetof_thread_context_ptregs_fs(%rcx)
movw %gs, offsetof_thread_context_ptregs_gs(%rcx)
pop %rax
movq %rax, offsetof_thread_context_ptregs_eflags(%rcx)
movq %gs:offsetof_sysmsg_app_stack, %r8
movq %r8, offsetof_thread_context_ptregs_rsp(%rcx)
movq %gs:offsetof_sysmsg_ret_addr, %r9
movq %r9, offsetof_thread_context_ptregs_rip(%rcx)
.endm
// prepare_exit_syshandler assumes that:
// - the memory address of the thread_context is loaded in %rcx.
// prepare_exit_syshandler does the following:
// - sets sysmsg->ret_addr
// - restores all registers that were saved inside the thread_context struct except for
// %rsp and rflags.
// - %rcx will be restored as well, and will no longer contain the memory address to the
// thread context.
// - puts user %rsp and rflags onto the syshandler stack (in that order). rflags cannot
// be restored at this point because syshandler will clobber it before it exits.
.macro prepare_exit_syshandler
movq offsetof_thread_context_ptregs_rsp(%rcx), %rax
push %rax
movq offsetof_thread_context_ptregs_eflags(%rcx), %rbx
push %rbx
// set sysmsg->ret_addr
movq offsetof_thread_context_ptregs_rip(%rcx), %r9
movq %r9, %gs:offsetof_sysmsg_ret_addr
// Restore segments. Because restoring segments is slow, restore them only if necessary.
movw %fs, %dx
cmpw %dx, offsetof_thread_context_ptregs_fs(%rcx)
je restored_fs
movw offsetof_thread_context_ptregs_fs(%rcx), %fs
restored_fs:
movw %gs, %si
cmpw %si, offsetof_thread_context_ptregs_gs(%rcx)
je restored_gs
movw offsetof_thread_context_ptregs_gs(%rcx), %gs
restored_gs:
// Restore other GP registers
movq offsetof_thread_context_ptregs_r15(%rcx), %r15
movq offsetof_thread_context_ptregs_r14(%rcx), %r14
movq offsetof_thread_context_ptregs_r13(%rcx), %r13
movq offsetof_thread_context_ptregs_r12(%rcx), %r12
movq offsetof_thread_context_ptregs_rbp(%rcx), %rbp
movq offsetof_thread_context_ptregs_rbx(%rcx), %rbx
movq offsetof_thread_context_ptregs_r11(%rcx), %r11
movq offsetof_thread_context_ptregs_r10(%rcx), %r10
movq offsetof_thread_context_ptregs_r9(%rcx), %r9
movq offsetof_thread_context_ptregs_r8(%rcx), %r8
movq offsetof_thread_context_ptregs_rax(%rcx), %rax
// %rcx restored last
movq offsetof_thread_context_ptregs_rdx(%rcx), %rdx
movq offsetof_thread_context_ptregs_rsi(%rcx), %rsi
movq offsetof_thread_context_ptregs_rdi(%rcx), %rdi
movq offsetof_thread_context_ptregs_rcx(%rcx), %rcx
.endm
// save_fpstate saves the current fpstate onto thread_context.fpstate.
// It assumes that:
// - the memory address of the thread_context is loaded in %rcx.
.macro save_fpstate
lea offsetof_thread_context_fpstate(%rcx), %rdi
movl $0xffffffff, %eax
movl $0xffffffff, %edx
movl __export_arch_state+offsetof_arch_state_xsave_mode(%rip), %esi
// TODO(b/268366549): Fix use of xsavec/xsaveopt.
// cmpl $XSAVE_MODE_XSAVEC, %esi
// jl use_xsaveopt
// xsavec (%rdi)
// jmp fpu_saved
// use_xsaveopt:
// cmpl $XSAVE_MODE_XSAVEOPT, %esi
// jl use_xsave
// xsaveopt (%rdi)
// jmp fpu_saved
// use_xsave:
cmpl $XSAVE_MODE_XSAVE, %esi
jl use_fxsave
xsave (%rdi)
jmp fpu_saved
use_fxsave:
fxsave (%rdi)
fpu_saved:
.endm
// restore_fpstate restores the fpstate previously saved onto thread_context.fpstate.
// It assumes that:
// - the memory address of the thread_context is loaded in %rcx.
.macro restore_fpstate
// We only need to restore fpstate if we were signalled that it changed (syshandler
// does not modify fpstate).
cmpl $0, offsetof_thread_context_fpstate_changed(%rcx)
je fpu_restored
lea offsetof_thread_context_fpstate(%rcx), %rdi
mov __export_arch_state+offsetof_arch_state_xsave_mode(%rip), %eax
cmpl $XSAVE_MODE_FXSAVE, %eax
jz use_fxrstor
use_xrstor:
movl $0xffffffff, %eax
movl $0xffffffff, %edx
xrstor (%rdi)
jmp fpu_restored
use_fxrstor:
fxrstor (%rdi)
fpu_restored:
.endm
// Syshandler:
////////////////////////////////////////
.globl __export_syshandler;
.type __export_syshandler, @function;
.align 4, 0x00;
__export_syshandler:
// The start of this function is in a usertrap trampoline:
// mov %rsp,%gs:0x20
// mov %gs:0x18,%rs
// mov sysmsg.ThreadStatePrep, %gs:offset(msg.State)
// mov %rsp,%gs:0x20 // msg.AppStack
// mov %gs:0x18,%rsp // msg.SyshandlerStack
// movabs $ret_addr, %rax
// mov %rax, %fs:0x8
// mov sysno, %eax
// jmpq *%gs,0x10
// mov %rax,%gs:0x8 // msg.RetAddr
// mov sysno,%eax
// jmpq *%gs:0x10 // msg.Syshandler
prepare_enter_syshandler
save_fpstate
// Save registers which are not preserved across function calls.
// http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf
push %rbp
push %r11
push %r10
push %r9
push %r8
push %rdi
push %rsi
push %rdx
push %rcx
// We have to avoid races with sighandler, so if sysmsg isn't equal to
// SYSMSG_STATE_NONE, we can't fault on a user stack.
//
// We can fault on a user stack, what a page isn't mapped yet or when a
// process is dieing and a process address spaces has been cleaned up (see
// subprocess.Release).
callq __syshandler
// Restore registers and return back to a guest code.
pop %rcx
pop %rdx
pop %rsi
pop %rdi
pop %r8
pop %r9
pop %r10
pop %r11
pop %rbp
// thread_context may have changed, therefore we reload it into %rcx anew.
load_thread_context_addr
restore_fpstate
movq %gs:offsetof_sysmsg_app_stack,%rsp
prepare_exit_syshandler
cmpl $kTHREAD_STATE_SIGACT, %gs:offsetof_sysmsg_state // msg->state
jne skipsyscall
mov $0xffff, %eax // any syscall which isn't allowed by seccomp.
// nop is here to avoid matching the `mov sysno, %eax; syscall` pattern that
// we are substituting with function calls.
nop
syscall
jmp skipint
skipsyscall:
// Indicate syshandler is done.
movl $kTHREAD_STATE_NONE, %gs:offsetof_sysmsg_state
// Check if syshandler received an interrupt while executing.
// If it did, retrigger the interrupt. See __export_sighandler for details.
cmpl $0, %gs:offsetof_sysmsg_interrupt // msg->interrupt
je skipint
movl $kTHREAD_STATE_INTERRUPT, %gs:offsetof_sysmsg_state
.byte FAULT_OPCODE // Re-trigger the interrupt.
skipint:
// Now syshandler is exiting for good; restore user rflags and %rsp.
popfq
movq 0(%rsp), %rsp
jmp *%gs:offsetof_sysmsg_ret_addr // msg->ret_addr
.size __export_syshandler, . - __export_syshandler
@@ -108,10 +108,6 @@ const (
ThreadStateEvent
// ThreadStatePrep means that syshandler started filling the sysmsg struct.
ThreadStatePrep
// ThreadStateSigact means that the sentry requests the full state of the stub
// thread.
// TODO(b/268366549): Remove this when syshandler saves full state to context.
ThreadStateSigact
// ThreadStateInterrupt is a Sysmsg state that indicates to the sighandler
// that there is a postponed interrupt from the syshandler.
// The sentry should never see this event.
@@ -264,14 +260,6 @@ type ThreadContext struct {
State ContextState
// Interrupt is set to indicate that this context has been interrupted.
Interrupt uint32
// Decoupled is set to indicate that this context is not tied to the sysmsg
// thread used to to execute this context. This value is only changed from
// the sentry if contextDecouplingExp is on.
// It changes the behaviour of the sysmsg threads in the following ways:
// - sighandler will save fpstate to ThreadContext.FPState instead of on the
// sighandler stack.
// - syshandler will not save fpstate at all.
Decoupled uint32
// Debug is a variable to use to get visibility into the stub from the sentry.
Debug uint64
}
@@ -42,7 +42,6 @@ enum {
THREAD_STATE_DONE,
THREAD_STATE_EVENT,
THREAD_STATE_PREP,
THREAD_STATE_SIGACT,
THREAD_STATE_INTERRUPT,
};
@@ -95,7 +94,6 @@ struct thread_context {
int64_t signo;
uint32_t state;
uint32_t interrupt;
uint32_t decoupled;
uint64_t debug;
};
@@ -51,11 +51,8 @@ func (s *ArchState) Init() {
fpLenUint, _ := fs.ExtendedStateSize()
s.fpLen = uint32(fpLenUint)
if fs.UseXsavec() {
s.xsaveMode = xsavec
} else if fs.UseXsaveopt() {
s.xsaveMode = xsaveopt
} else if fs.UseXsave() {
// TODO(b/268366549): Fix use of xsavec/xsaveopt.
if fs.UseXsave() {
s.xsaveMode = xsave
} else {
s.xsaveMode = fxsave
@@ -90,13 +90,13 @@ int wait_state(struct sysmsg *sysmsg, uint32_t state) {
}
v = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE);
if (v == THREAD_STATE_DONE || v == THREAD_STATE_SIGACT) goto out;
if (v == THREAD_STATE_DONE) goto out;
handshake_timeout = __export_handshake_timeout;
start = rdtsc();
while (1) {
v = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE);
if (v == THREAD_STATE_DONE || v == THREAD_STATE_SIGACT) goto out;
if (v == THREAD_STATE_DONE) goto out;
// The Sentry can change stub_fast_path to zero if it finds out that the
// user task has to sleep.
@@ -154,7 +154,6 @@ void verify_offsets() {
offsetof(struct thread_context, ptregs));
BUILD_BUG_ON(kTHREAD_STATE_NONE != THREAD_STATE_NONE);
BUILD_BUG_ON(kTHREAD_STATE_SIGACT != THREAD_STATE_SIGACT);
BUILD_BUG_ON(kTHREAD_STATE_INTERRUPT != THREAD_STATE_INTERRUPT);
BUILD_BUG_ON(sizeof(struct thread_context) >
@@ -22,7 +22,10 @@
// LINT.IfChange
#define MAX_FPSTATE_LEN 3648
// Note: To be explicit, 2^12 = 4096; if ALLOCATED_SIZEOF_THREAD_CONTEXT_STRUCT
// is changed, make sure to change the code that relies on the bitshift.
#define ALLOCATED_SIZEOF_THREAD_CONTEXT_STRUCT 4096
#define THREAD_CONTEXT_STRUCT_BITSHIFT 12
// LINT.ThenChange(sysmsg.go)
// LINT.IfChange
@@ -44,8 +47,7 @@
#define offsetof_thread_context_ptregs 0x8 + MAX_FPSTATE_LEN
#define kTHREAD_STATE_NONE 0
#define kTHREAD_STATE_SIGACT 4
#define kTHREAD_STATE_INTERRUPT 5
#define kTHREAD_STATE_INTERRUPT 4
// LINT.ThenChange(sysmsg.h, sysmsg_lib.c)