FreeBSD-specific porting work.
cgo/libmach remain unimplemented. However, compilers, runtime,
and packages are 100%. I still need to go through and implement
missing syscalls (at least make sure they're all listed), but
for all shipped functionality, this is done. Ship! ;)
R=rsc, VenkateshSrinivas
https://golang.org/cl/152142
diff --git a/src/pkg/runtime/freebsd/amd64/defs.h b/src/pkg/runtime/freebsd/amd64/defs.h
new file mode 100644
index 0000000..06efcc2
--- /dev/null
+++ b/src/pkg/runtime/freebsd/amd64/defs.h
@@ -0,0 +1,157 @@
+// godefs -f -m64 freebsd/defs.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+// Constants
+enum {
+ PROT_NONE = 0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+ UMTX_OP_WAIT = 0x2,
+ UMTX_OP_WAKE = 0x3,
+ EINTR = 0x4,
+};
+
+// Types
+#pragma pack on
+
+typedef struct Sigaltstack Sigaltstack;
+struct Sigaltstack {
+ int8 *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte pad0[4];
+};
+
+typedef struct Sigset Sigset;
+struct Sigset {
+ uint32 __bits[4];
+};
+
+typedef union Sigval Sigval;
+union Sigval {
+ int32 sival_int;
+ void *sival_ptr;
+ int32 sigval_int;
+ void *sigval_ptr;
+};
+
+typedef struct StackT StackT;
+struct StackT {
+ int8 *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte pad0[4];
+};
+
+typedef struct Siginfo Siginfo;
+struct Siginfo {
+ int32 si_signo;
+ int32 si_errno;
+ int32 si_code;
+ int32 si_pid;
+ uint32 si_uid;
+ int32 si_status;
+ void *si_addr;
+ Sigval si_value;
+ byte _reason[40];
+};
+
+typedef struct Mcontext Mcontext;
+struct Mcontext {
+ int64 mc_onstack;
+ int64 mc_rdi;
+ int64 mc_rsi;
+ int64 mc_rdx;
+ int64 mc_rcx;
+ int64 mc_r8;
+ int64 mc_r9;
+ int64 mc_rax;
+ int64 mc_rbx;
+ int64 mc_rbp;
+ int64 mc_r10;
+ int64 mc_r11;
+ int64 mc_r12;
+ int64 mc_r13;
+ int64 mc_r14;
+ int64 mc_r15;
+ uint32 mc_trapno;
+ uint16 mc_fs;
+ uint16 mc_gs;
+ int64 mc_addr;
+ uint32 mc_flags;
+ uint16 mc_es;
+ uint16 mc_ds;
+ int64 mc_err;
+ int64 mc_rip;
+ int64 mc_cs;
+ int64 mc_rflags;
+ int64 mc_rsp;
+ int64 mc_ss;
+ int64 mc_len;
+ int64 mc_fpformat;
+ int64 mc_ownedfp;
+ int64 mc_fpstate[64];
+ int64 mc_fsbase;
+ int64 mc_gsbase;
+ int64 mc_spare[6];
+};
+
+typedef struct Ucontext Ucontext;
+struct Ucontext {
+ Sigset uc_sigmask;
+ Mcontext uc_mcontext;
+ Ucontext *uc_link;
+ StackT uc_stack;
+ int32 uc_flags;
+ int32 __spare__[4];
+ byte pad0[12];
+};
+
+typedef struct Sigcontext Sigcontext;
+struct Sigcontext {
+ Sigset sc_mask;
+ int64 sc_onstack;
+ int64 sc_rdi;
+ int64 sc_rsi;
+ int64 sc_rdx;
+ int64 sc_rcx;
+ int64 sc_r8;
+ int64 sc_r9;
+ int64 sc_rax;
+ int64 sc_rbx;
+ int64 sc_rbp;
+ int64 sc_r10;
+ int64 sc_r11;
+ int64 sc_r12;
+ int64 sc_r13;
+ int64 sc_r14;
+ int64 sc_r15;
+ int32 sc_trapno;
+ int16 sc_fs;
+ int16 sc_gs;
+ int64 sc_addr;
+ int32 sc_flags;
+ int16 sc_es;
+ int16 sc_ds;
+ int64 sc_err;
+ int64 sc_rip;
+ int64 sc_cs;
+ int64 sc_rflags;
+ int64 sc_rsp;
+ int64 sc_ss;
+ int64 sc_len;
+ int64 sc_fpformat;
+ int64 sc_ownedfp;
+ int64 sc_fpstate[64];
+ int64 sc_fsbase;
+ int64 sc_gsbase;
+ int64 sc_spare[6];
+};
+#pragma pack off
diff --git a/src/pkg/runtime/freebsd/amd64/rt0.s b/src/pkg/runtime/freebsd/amd64/rt0.s
new file mode 100644
index 0000000..7903b7c
--- /dev/null
+++ b/src/pkg/runtime/freebsd/amd64/rt0.s
@@ -0,0 +1,9 @@
+// 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.
+
+// Darwin and Linux use the same linkage to main
+
+TEXT _rt0_amd64_freebsd(SB),7,$-8
+ MOVQ $_rt0_amd64(SB), DX
+ JMP DX
diff --git a/src/pkg/runtime/freebsd/amd64/signal.c b/src/pkg/runtime/freebsd/amd64/signal.c
new file mode 100644
index 0000000..08d8972
--- /dev/null
+++ b/src/pkg/runtime/freebsd/amd64/signal.c
@@ -0,0 +1,117 @@
+#include "runtime.h"
+#include "defs.h"
+#include "signals.h"
+#include "os.h"
+
+extern void sigtramp(void);
+
+typedef struct sigaction {
+ union {
+ void (*__sa_handler)(int32);
+ void (*__sa_sigaction)(int32, Siginfo*, void *);
+ } __sigaction_u; /* signal handler */
+ int32 sa_flags; /* see signal options below */
+ int64 sa_mask; /* signal mask to apply */
+} Sigaction;
+
+void
+dumpregs(Sigcontext *r)
+{
+ printf("rax %X\n", r->sc_rax);
+ printf("rbx %X\n", r->sc_rbx);
+ printf("rcx %X\n", r->sc_rcx);
+ printf("rdx %X\n", r->sc_rdx);
+ printf("rdi %X\n", r->sc_rdi);
+ printf("rsi %X\n", r->sc_rsi);
+ printf("rbp %X\n", r->sc_rbp);
+ printf("rsp %X\n", r->sc_rsp);
+ printf("r8 %X\n", r->sc_r8 );
+ printf("r9 %X\n", r->sc_r9 );
+ printf("r10 %X\n", r->sc_r10);
+ printf("r11 %X\n", r->sc_r11);
+ printf("r12 %X\n", r->sc_r12);
+ printf("r13 %X\n", r->sc_r13);
+ printf("r14 %X\n", r->sc_r14);
+ printf("r15 %X\n", r->sc_r15);
+ printf("rip %X\n", r->sc_rip);
+ printf("rflags %X\n", r->sc_flags);
+ printf("cs %X\n", (uint64)r->sc_cs);
+ printf("fs %X\n", (uint64)r->sc_fsbase);
+ printf("gs %X\n", (uint64)r->sc_gsbase);
+}
+
+void
+sighandler(int32 sig, Siginfo* info, void* context)
+{
+ Ucontext *uc;
+ Mcontext *mc;
+ Sigcontext *sc;
+
+ if(panicking) // traceback already printed
+ exit(2);
+ panicking = 1;
+
+ uc = context;
+ mc = &uc->uc_mcontext;
+ sc = (Sigcontext*)mc; // same layout, more conveient names
+
+ if(sig < 0 || sig >= NSIG)
+ printf("Signal %d\n", sig);
+ else
+ printf("%s\n", sigtab[sig].name);
+
+ printf("Faulting address: %p\n", info->si_addr);
+ printf("PC=%X\n", sc->sc_rip);
+ printf("\n");
+
+ if(gotraceback()){
+ traceback((void*)sc->sc_rip, (void*)sc->sc_rsp, (void*)sc->sc_r15);
+ tracebackothers((void*)sc->sc_r15);
+ dumpregs(sc);
+ }
+
+ breakpoint();
+ exit(2);
+}
+
+void
+sigignore(void)
+{
+}
+
+void
+signalstack(byte *p, int32 n)
+{
+ Sigaltstack st;
+
+ st.ss_sp = (int8*)p;
+ st.ss_size = n;
+ st.ss_flags = 0;
+ sigaltstack(&st, nil);
+}
+
+void
+initsig(void)
+{
+ static Sigaction sa;
+
+ int32 i;
+ sa.sa_flags |= SA_ONSTACK | SA_SIGINFO;
+ sa.sa_mask = ~0x0ull;
+
+ for(i = 0; i < NSIG; i++) {
+ if(sigtab[i].flags) {
+ if(sigtab[i].flags & SigCatch)
+ sa.__sigaction_u.__sa_handler = (void*) sigtramp;
+ else
+ sa.__sigaction_u.__sa_handler = (void*) sigignore;
+
+ if(sigtab[i].flags & SigRestart)
+ sa.sa_flags |= SA_RESTART;
+ else
+ sa.sa_flags &= ~SA_RESTART;
+
+ sigaction(i, &sa, nil);
+ }
+ }
+}
diff --git a/src/pkg/runtime/freebsd/amd64/sys.s b/src/pkg/runtime/freebsd/amd64/sys.s
new file mode 100644
index 0000000..1b62468
--- /dev/null
+++ b/src/pkg/runtime/freebsd/amd64/sys.s
@@ -0,0 +1,125 @@
+// 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.
+//
+// System calls and other sys.stuff for AMD64, FreeBSD
+// /usr/src/sys/kern/syscalls.master for syscall numbers.
+//
+
+#include "amd64/asm.h"
+
+TEXT sys_umtx_op(SB),7,$0
+ MOVQ 8(SP), DI
+ MOVL 16(SP), SI
+ MOVL 20(SP), DX
+ MOVQ 24(SP), R10
+ MOVQ 32(SP), R8
+ MOVL $454, AX
+ SYSCALL
+ RET
+
+TEXT thr_new(SB),7,$0
+ MOVQ 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVL $455, AX
+ SYSCALL
+ RET
+
+TEXT thr_start(SB),7,$0
+ MOVQ DI, m
+ MOVQ m_g0(m), g
+ CALL mstart(SB)
+ MOVQ 0, AX // crash (not reached)
+
+
+// Exit the entire program (like C exit)
+TEXT exit(SB),7,$-8
+ MOVL 8(SP), DI // arg 1 exit status
+ MOVL $1, AX
+ SYSCALL
+ CALL notok(SB)
+ RET
+
+TEXT exit1(SB),7,$-8
+ MOVQ 8(SP), DI // arg 1 exit status
+ MOVL $431, AX
+ SYSCALL
+ CALL notok(SB)
+ RET
+
+TEXT write(SB),7,$-8
+ MOVL 8(SP), DI // arg 1 fd
+ MOVQ 16(SP), SI // arg 2 buf
+ MOVL 24(SP), DX // arg 3 count
+ MOVL $4, AX
+ SYSCALL
+ JCC 2(PC)
+ CALL notok(SB)
+ RET
+
+TEXT sigaction(SB),7,$-8
+ MOVL 8(SP), DI // arg 1 sig
+ MOVQ 16(SP), SI // arg 2 act
+ MOVQ 24(SP), DX // arg 3 oact
+ MOVL $416, AX
+ SYSCALL
+ JCC 2(PC)
+ CALL notok(SB)
+ RET
+
+TEXT sigtramp(SB),7,$24-16
+ MOVQ m_gsignal(m), g
+ MOVQ DI, 0(SP)
+ MOVQ SI, 8(SP)
+ MOVQ DX, 16(SP)
+ CALL sighandler(SB)
+ RET
+
+TEXT runtime·mmap(SB),7,$-8
+ MOVQ 8(SP), DI // arg 1 addr
+ MOVL 16(SP), SI // arg 2 len
+ MOVL 20(SP), DX // arg 3 prot
+ MOVL 24(SP), R10 // arg 4 flags
+ MOVL 28(SP), R8 // arg 5 fid
+ MOVL 32(SP), R9 // arg 6 offset
+ MOVL $477, AX
+ SYSCALL
+ JCC 2(PC)
+ CALL notok(SB)
+ RET
+
+TEXT notok(SB),7,$-8
+ MOVL $0xf1, BP
+ MOVQ BP, (BP)
+ RET
+
+TEXT runtime·memclr(SB),7,$-8
+ MOVQ 8(SP), DI // arg 1 addr
+ MOVL 16(SP), CX // arg 2 count
+ ADDL $7, CX
+ SHRL $3, CX
+ MOVQ $0, AX
+ CLD
+ REP
+ STOSQ
+ RET
+
+TEXT runtime·getcallerpc+0(SB),7,$0
+ MOVQ x+0(FP),AX // addr of first arg
+ MOVQ -8(AX),AX // get calling pc
+ RET
+
+TEXT runtime·setcallerpc+0(SB),7,$0
+ MOVQ x+0(FP),AX // addr of first arg
+ MOVQ x+8(FP), BX
+ MOVQ BX, -8(AX) // set calling pc
+ RET
+
+TEXT sigaltstack(SB),7,$-8
+ MOVQ new+8(SP), DI
+ MOVQ old+16(SP), SI
+ MOVQ $53, AX
+ SYSCALL
+ JCC 2(PC)
+ CALL notok(SB)
+ RET
diff --git a/src/pkg/runtime/freebsd/defs.c b/src/pkg/runtime/freebsd/defs.c
new file mode 100644
index 0000000..414e7cd
--- /dev/null
+++ b/src/pkg/runtime/freebsd/defs.c
@@ -0,0 +1,49 @@
+// 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.
+
+/*
+ * Input to godefs.
+ *
+ godefs -f -m64 defs.c >amd64/defs.h
+ godefs defs.c >386/defs.h
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/ucontext.h>
+#include <sys/umtx.h>
+#include <sys/_sigset.h>
+
+enum {
+ $PROT_NONE = PROT_NONE,
+ $PROT_READ = PROT_READ,
+ $PROT_WRITE = PROT_WRITE,
+ $PROT_EXEC = PROT_EXEC,
+
+ $MAP_ANON = MAP_ANON,
+ $MAP_PRIVATE = MAP_PRIVATE,
+
+ $SA_SIGINFO = SA_SIGINFO,
+ $SA_RESTART = SA_RESTART,
+ $SA_ONSTACK = SA_ONSTACK,
+
+ $UMTX_OP_WAIT = UMTX_OP_WAIT,
+ $UMTX_OP_WAKE = UMTX_OP_WAKE,
+
+ $EINTR = EINTR,
+};
+
+typedef struct sigaltstack $Sigaltstack;
+typedef struct __sigset $Sigset;
+typedef union sigval $Sigval;
+typedef stack_t $StackT;
+
+typedef siginfo_t $Siginfo;
+
+typedef mcontext_t $Mcontext;
+typedef ucontext_t $Ucontext;
+typedef struct sigcontext $Sigcontext;
diff --git a/src/pkg/runtime/freebsd/os.h b/src/pkg/runtime/freebsd/os.h
new file mode 100644
index 0000000..ec91500
--- /dev/null
+++ b/src/pkg/runtime/freebsd/os.h
@@ -0,0 +1,19 @@
+// FreeBSD-specific system calls
+int32 ksem_init(uint64 *, uint32);
+int32 ksem_wait(uint32);
+int32 ksem_destroy(uint32);
+int32 ksem_post(uint32);
+
+struct thr_param {
+ void (*start_func)(void *); /* thread entry function. */
+ void *arg; /* argument for entry function. */
+ byte *stack_base; /* stack base address. */
+ int64 stack_size; /* stack size. */
+ byte *tls_base; /* tls base address. */
+ int64 tls_size; /* tls size. */
+ int64 *child_tid; /* address to store new TID. */
+ int64 *parent_tid; /* parent accesses the new TID here. */
+ int32 flags; /* thread flags. */
+ void *spare[4]; /* TODO: cpu affinity mask etc. */
+};
+int32 thr_new(struct thr_param*, uint64);
diff --git a/src/pkg/runtime/freebsd/signals.h b/src/pkg/runtime/freebsd/signals.h
new file mode 100644
index 0000000..c566481
--- /dev/null
+++ b/src/pkg/runtime/freebsd/signals.h
@@ -0,0 +1,48 @@
+// 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.
+
+#define C SigCatch
+#define I SigIgnore
+#define R SigRestart
+
+static SigTab sigtab[] = {
+ /* 0 */ 0, "SIGNONE: no trap",
+ /* 1 */ 0, "SIGHUP: terminal line hangup",
+ /* 2 */ 0, "SIGINT: interrupt",
+ /* 3 */ C, "SIGQUIT: quit",
+ /* 4 */ C, "SIGILL: illegal instruction",
+ /* 5 */ C, "SIGTRAP: trace trap",
+ /* 6 */ C, "SIGABRT: abort",
+ /* 7 */ C, "SIGEMT: EMT instruction",
+ /* 8 */ C, "SIGFPE: floating-point exception",
+ /* 9 */ 0, "SIGKILL: kill",
+ /* 10 */ C, "SIGBUS: bus error",
+ /* 11 */ C, "SIGSEGV: segmentation violation",
+ /* 12 */ C, "SIGSYS: bad system call",
+ /* 13 */ I, "SIGPIPE: write to broken pipe",
+ /* 14 */ 0, "SIGALRM: alarm clock",
+ /* 15 */ 0, "SIGTERM: termination",
+ /* 16 */ 0, "SIGURG: urgent condition on socket",
+ /* 17 */ 0, "SIGSTOP: stop, unblockable",
+ /* 18 */ 0, "SIGTSTP: stop from tty",
+ /* 19 */ 0, "SIGCONT: continue",
+ /* 20 */ I+R, "SIGCHLD: child status has changed",
+ /* 21 */ 0, "SIGTTIN: background read from tty",
+ /* 22 */ 0, "SIGTTOU: background write to tty",
+ /* 23 */ 0, "SIGIO: i/o now possible",
+ /* 24 */ 0, "SIGXCPU: cpu limit exceeded",
+ /* 25 */ 0, "SIGXFSZ: file size limit exceeded",
+ /* 26 */ 0, "SIGVTALRM: virtual alarm clock",
+ /* 27 */ 0, "SIGPROF: profiling alarm clock",
+ /* 28 */ I+R, "SIGWINCH: window size change",
+ /* 29 */ 0, "SIGINFO: information request",
+ /* 30 */ 0, "SIGUSR1: user-defined signal 1",
+ /* 31 */ 0, "SIGUSR2: user-defined signal 2",
+ /* 32 */ 0, "SIGTHR: reserved",
+};
+#undef C
+#undef I
+#undef R
+
+#define NSIG 33
diff --git a/src/pkg/runtime/freebsd/thread.c b/src/pkg/runtime/freebsd/thread.c
new file mode 100644
index 0000000..e7cd707
--- /dev/null
+++ b/src/pkg/runtime/freebsd/thread.c
@@ -0,0 +1,162 @@
+// Use of this source file is governed by a BSD-style
+// license that can be found in the LICENSE file.`
+
+#include "runtime.h"
+#include "defs.h"
+#include "signals.h"
+#include "os.h"
+
+// FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and
+// thus the code is largely similar. See linux/thread.c for comments.
+
+static void
+umtx_wait(uint32 *addr, uint32 val)
+{
+ int32 ret;
+
+ ret = sys_umtx_op(addr, UMTX_OP_WAIT, val, nil, nil);
+ if(ret >= 0 || ret == -EINTR)
+ return;
+
+ printf("umtx_wait addr=%p val=%d ret=%d\n", addr, val, ret);
+ *(int32*)0x1005 = 0x1005;
+}
+
+static void
+umtx_wake(uint32 *addr)
+{
+ int32 ret;
+
+ ret = sys_umtx_op(addr, UMTX_OP_WAKE, 1, nil, nil);
+ if(ret >= 0)
+ return;
+
+ printf("umtx_wake addr=%p ret=%d\n", addr, ret);
+ *(int32*)0x1006 = 0x1006;
+}
+
+// See linux/thread.c for comments about the algorithm.
+static void
+umtx_lock(Lock *l)
+{
+ uint32 v;
+
+again:
+ v = l->key;
+ if((v&1) == 0){
+ if(cas(&l->key, v, v|1))
+ return;
+ goto again;
+ }
+
+ if(!cas(&l->key, v, v+2))
+ goto again;
+
+ umtx_wait(&l->key, v+2);
+
+ for(;;){
+ v = l->key;
+ if(v < 2)
+ throw("bad lock key");
+ if(cas(&l->key, v, v-2))
+ break;
+ }
+
+ goto again;
+}
+
+static void
+umtx_unlock(Lock *l)
+{
+ uint32 v;
+
+again:
+ v = l->key;
+ if((v&1) == 0)
+ throw("unlock of unlocked lock");
+ if(!cas(&l->key, v, v&~1))
+ goto again;
+
+ if(v&~1)
+ umtx_wake(&l->key);
+}
+
+void
+lock(Lock *l)
+{
+ if(m->locks < 0)
+ throw("lock count");
+ m->locks++;
+ umtx_lock(l);
+}
+
+void
+unlock(Lock *l)
+{
+ m->locks--;
+ if(m->locks < 0)
+ throw("lock count");
+ umtx_unlock(l);
+}
+
+// Event notifications.
+void
+noteclear(Note *n)
+{
+ n->lock.key = 0;
+ umtx_lock(&n->lock);
+}
+
+void
+notesleep(Note *n)
+{
+ umtx_lock(&n->lock);
+ umtx_unlock(&n->lock);
+}
+
+void
+notewakeup(Note *n)
+{
+ umtx_unlock(&n->lock);
+}
+
+void thr_start(void*);
+
+void
+newosproc(M *m, G *g, void *stk, void (*fn)(void))
+{
+ struct thr_param param;
+
+ USED(fn); // thr_start assumes fn == mstart
+ USED(g); // thr_start assumes g == m->g0
+
+ if(0){
+ printf("newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n",
+ stk, m, g, fn, m->id, m->tls[0], &m);
+ }
+
+ runtime_memclr((byte*)¶m, sizeof param);
+
+ param.start_func = thr_start;
+ param.arg = m;
+ param.stack_base = stk;
+ param.stack_size = g->stackbase - g->stackguard + 256;
+ param.child_tid = (int64*)&m->procid;
+ param.parent_tid = nil;
+
+ thr_new(¶m, sizeof param);
+}
+
+void
+osinit(void)
+{
+}
+
+// Called to initialize a new m (including the bootstrap m).
+void
+minit(void)
+{
+ // Initialize signal handling
+ m->gsignal = malg(32*1024);
+ signalstack(m->gsignal->stackguard, 32*1024);
+}