// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "runtime.h" #include "amd64_linux.h" #include "signals_linux.h" /* From /usr/include/asm-x86_64/sigcontext.h */ struct _fpstate { uint16 cwd; uint16 swd; uint16 twd; /* Note this is not the same as the 32bit/x87/FSAVE twd */ uint16 fop; uint64 rip; uint32 rdp; uint32 mxcsr; uint32 mxcsr_mask; uint32 st_space[32]; /* 8*16 bytes for each FP-reg */ uint32 xmm_space[64]; /* 16*16 bytes for each XMM-reg */ uint32 reserved2[24]; }; struct sigcontext { uint64 r8; uint64 r9; uint64 r10; uint64 r11; uint64 r12; uint64 r13; uint64 r14; uint64 r15; uint64 rdi; uint64 rsi; uint64 rbp; uint64 rbx; uint64 rdx; uint64 rax; uint64 rcx; uint64 rsp; uint64 rip; uint64 eflags; /* RFLAGS */ uint16 cs; uint16 gs; uint16 fs; uint16 __pad0; uint64 err; uint64 trapno; uint64 oldmask; uint64 cr2; struct _fpstate *fpstate; /* zero when no FPU context */ uint64 reserved1[8]; }; /* From /usr/include/asm-x86_64/signal.h */ typedef struct sigaltstack { void /*__user*/ *ss_sp; int32 ss_flags; uint64 ss_size; } stack_t; typedef uint64 sigset_t; /* From /usr/include/asm-x86_64/ucontext.h */ struct ucontext { uint64 uc_flags; struct ucontext *uc_link; stack_t uc_stack; struct sigcontext uc_mcontext; sigset_t uc_sigmask; /* mask last for extensibility */ }; void print_sigcontext(struct sigcontext *sc) { prints("\nrax "); sys·printhex(sc->rax); prints("\nrbx "); sys·printhex(sc->rbx); prints("\nrcx "); sys·printhex(sc->rcx); prints("\nrdx "); sys·printhex(sc->rdx); prints("\nrdi "); sys·printhex(sc->rdi); prints("\nrsi "); sys·printhex(sc->rsi); prints("\nrbp "); sys·printhex(sc->rbp); prints("\nrsp "); sys·printhex(sc->rsp); prints("\nr8 "); sys·printhex(sc->r8 ); prints("\nr9 "); sys·printhex(sc->r9 ); prints("\nr10 "); sys·printhex(sc->r10); prints("\nr11 "); sys·printhex(sc->r11); prints("\nr12 "); sys·printhex(sc->r12); prints("\nr13 "); sys·printhex(sc->r13); prints("\nr14 "); sys·printhex(sc->r14); prints("\nr15 "); sys·printhex(sc->r15); prints("\nrip "); sys·printhex(sc->rip); prints("\nrflags "); sys·printhex(sc->eflags); prints("\ncs "); sys·printhex(sc->cs); prints("\nfs "); sys·printhex(sc->fs); prints("\ngs "); sys·printhex(sc->gs); prints("\n"); } /* * This assembler routine takes the args from registers, puts them on the stack, * and calls sighandler(). */ extern void sigtramp(void); extern void sigignore(void); // just returns extern void sigreturn(void); // calls sigreturn /* * Rudimentary reverse-engineered definition of signal interface. * You'd think it would be documented. */ /* From /usr/include/bits/siginfo.h */ struct siginfo { int32 si_signo; /* signal number */ int32 si_errno; /* errno association */ int32 si_code; /* signal code */ int32 si_status; /* exit value */ void *si_addr; /* faulting address */ /* more stuff here */ }; // This is a struct sigaction from /usr/include/asm/signal.h struct sigaction { void (*sa_handler)(int32, struct siginfo*, void*); uint64 sa_flags; void (*sa_restorer)(void); uint64 sa_mask; }; void sighandler(int32 sig, struct siginfo* info, void** context) { if(panicking) // traceback already printed sys_Exit(2); struct sigcontext *sc = &(((struct ucontext *)context)->uc_mcontext); if(sig < 0 || sig >= NSIG){ prints("Signal "); sys·printint(sig); }else{ prints(sigtab[sig].name); } prints("\nFaulting address: "); sys·printpointer(info->si_addr); prints("\npc: "); sys·printhex(sc->rip); prints("\n\n"); if(gotraceback()){ traceback((void *)sc->rip, (void *)sc->rsp, (void *)sc->r15); tracebackothers((void*)sc->r15); print_sigcontext(sc); } sys·Breakpoint(); sys_Exit(2); } struct stack_t { void *sp; int32 flags; int32 pad; int64 size; }; void signalstack(byte *p, int32 n) { struct stack_t st; st.sp = p; st.size = n; st.pad = 0; st.flags = 0; sigaltstack(&st, nil); } void rt_sigaction(int64, void*, void*, uint64); enum { SA_RESTART = 0x10000000, SA_ONSTACK = 0x08000000, SA_RESTORER = 0x04000000, SA_SIGINFO = 0x00000004, }; void initsig(void) { static struct sigaction sa; int32 i; sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; sa.sa_mask = 0xFFFFFFFFFFFFFFFFULL; sa.sa_restorer = (void*)sigreturn; for(i = 0; i= 0 || ret == -EAGAIN || ret == -EINTR) return; prints("futexsleep addr="); sys·printpointer(addr); prints(" val="); sys·printint(val); prints(" returned "); sys·printint(ret); prints("\n"); *(int32*)0x1005 = 0x1005; } // If any procs are sleeping on addr, wake up at least one. static void futexwakeup(uint32 *addr) { int64 ret; ret = futex(addr, FUTEX_WAKE, 1, nil, nil, 0); if(ret >= 0) return; // I don't know that futex wakeup can return // EAGAIN or EINTR, but if it does, it would be // safe to loop and call futex again. prints("futexwakeup addr="); sys·printpointer(addr); prints(" returned "); sys·printint(ret); prints("\n"); *(int32*)0x1006 = 0x1006; } // Lock and unlock. // // The lock state is a single 32-bit word that holds // a 31-bit count of threads waiting for the lock // and a single bit (the low bit) saying whether the lock is held. // The uncontended case runs entirely in user space. // When contention is detected, we defer to the kernel (futex). // // A reminder: compare-and-swap cas(addr, old, new) does // if(*addr == old) { *addr = new; return 1; } // else return 0; // but atomically. static void futexlock(Lock *l) { uint32 v; again: v = l->key; if((v&1) == 0){ if(cas(&l->key, v, v|1)){ // Lock wasn't held; we grabbed it. return; } goto again; } // Lock was held; try to add ourselves to the waiter count. if(!cas(&l->key, v, v+2)) goto again; // We're accounted for, now sleep in the kernel. // // We avoid the obvious lock/unlock race because // the kernel won't put us to sleep if l->key has // changed underfoot and is no longer v+2. // // We only really care that (v&1) == 1 (the lock is held), // and in fact there is a futex variant that could // accomodate that check, but let's not get carried away.) futexsleep(&l->key, v+2); // We're awake: remove ourselves from the count. for(;;){ v = l->key; if(v < 2) throw("bad lock key"); if(cas(&l->key, v, v-2)) break; } // Try for the lock again. goto again; } static void futexunlock(Lock *l) { uint32 v; // Atomically get value and clear lock bit. again: v = l->key; if((v&1) == 0) throw("unlock of unlocked lock"); if(!cas(&l->key, v, v&~1)) goto again; // If there were waiters, wake one. if(v & ~1) futexwakeup(&l->key); } void lock(Lock *l) { if(m->locks < 0) throw("lock count"); m->locks++; futexlock(l); } void unlock(Lock *l) { m->locks--; if(m->locks < 0) throw("lock count"); futexunlock(l); } // One-time notifications. // // Since the lock/unlock implementation already // takes care of sleeping in the kernel, we just reuse it. // (But it's a weird use, so it gets its own interface.) // // We use a lock to represent the event: // unlocked == event has happened. // Thus the lock starts out locked, and to wait for the // event you try to lock the lock. To signal the event, // you unlock the lock. void noteclear(Note *n) { n->lock.key = 0; // memset(n, 0, sizeof *n) futexlock(&n->lock); } void notewakeup(Note *n) { futexunlock(&n->lock); } void notesleep(Note *n) { futexlock(&n->lock); futexunlock(&n->lock); // Let other sleepers find out too. } // Clone, the Linux rfork. enum { CLONE_VM = 0x100, CLONE_FS = 0x200, CLONE_FILES = 0x400, CLONE_SIGHAND = 0x800, CLONE_PTRACE = 0x2000, CLONE_VFORK = 0x4000, CLONE_PARENT = 0x8000, CLONE_THREAD = 0x10000, CLONE_NEWNS = 0x20000, CLONE_SYSVSEM = 0x40000, CLONE_SETTLS = 0x80000, CLONE_PARENT_SETTID = 0x100000, CLONE_CHILD_CLEARTID = 0x200000, CLONE_UNTRACED = 0x800000, CLONE_CHILD_SETTID = 0x1000000, CLONE_STOPPED = 0x2000000, CLONE_NEWUTS = 0x4000000, CLONE_NEWIPC = 0x8000000, }; void newosproc(M *m, G *g, void *stk, void (*fn)(void)) { int64 ret; int32 flags; flags = CLONE_PARENT /* getppid doesn't change in child */ | CLONE_VM /* share memory */ | CLONE_FS /* share cwd, etc */ | CLONE_FILES /* share fd table */ | CLONE_SIGHAND /* share sig handler table */ | CLONE_PTRACE /* revisit - okay for now */ | CLONE_THREAD /* revisit - okay for now */ ; if(0){ prints("newosproc stk="); sys·printpointer(stk); prints(" m="); sys·printpointer(m); prints(" g="); sys·printpointer(g); prints(" fn="); sys·printpointer(fn); prints(" clone="); sys·printpointer(clone); prints("\n"); } ret = clone(flags, stk, m, g, fn); if(ret < 0) *(int32*)123 = 123; } void osinit(void) { } // Called to initialize a new m (including the bootstrap m). void minit(void) { // Initialize signal handling. m->gsignal = malg(32*1024); // OS X wants >=8K, Linux >=2K signalstack(m->gsignal->stackguard, 32*1024); }