]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo, runtime/cgo: support ppc64
authorAustin Clements <austin@google.com>
Tue, 16 Dec 2014 23:34:55 +0000 (18:34 -0500)
committerAustin Clements <austin@google.com>
Wed, 7 Jan 2015 20:36:27 +0000 (20:36 +0000)
This implements support for calls to and from C in the ppc64 C ABI, as
well as supporting functionality such as an entry point from the
dynamic linker.

Change-Id: I68da6df50d5638cb1a3d3fef773fb412d7bf631a
Reviewed-on: https://go-review.googlesource.com/2009
Reviewed-by: Russ Cox <rsc@golang.org>
14 files changed:
src/cmd/cgo/out.go
src/go/build/build.go
src/run.bash
src/runtime/asm_ppc64x.s
src/runtime/cgo/asm_ppc64x.s [new file with mode: 0644]
src/runtime/cgo/gcc_linux_ppc64x.c [new file with mode: 0644]
src/runtime/cgo/gcc_ppc64x.S [new file with mode: 0644]
src/runtime/cgocall.go
src/runtime/crash_cgo_test.go
src/runtime/rt0_linux_ppc64.s
src/runtime/rt0_linux_ppc64le.s
src/runtime/runtime1.go
src/runtime/sys_linux_ppc64x.s
src/runtime/tls_ppc64x.s

index d887c9df29761370b9fba6a8b2e17eaa915fb3c6..a4bd5579d353c9c1c2f0f0bf8e6c9ed7cbe68b33 100644 (file)
@@ -60,6 +60,7 @@ func (p *Package) writeDefs() {
        }
        fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n")
        fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n")
+       fmt.Fprintf(fm, "void _cgo_reginit(void) { }\n")
 
        // Write second Go output: definitions of _C_xxx.
        // In a separate file so that the import of "unsafe" does not
index 7cf2f1f774bd7265953067b253e171c49d8e8465..62935ee0cfabbbd97dd9d69395114e906d98b408 100644 (file)
@@ -267,6 +267,7 @@ var cgoEnabled = map[string]bool{
        "linux/386":       true,
        "linux/amd64":     true,
        "linux/arm":       true,
+       "linux/ppc64le":   true,
        "android/386":     true,
        "android/amd64":   true,
        "android/arm":     true,
index dbd3ddb6afcb49f58f44710e8981a9c97f3630b2..6eafc5f0c47c687aaa7810fde8fd9c7ee9dad35a 100755 (executable)
@@ -118,6 +118,7 @@ export GOTRACEBACK=2
 go test -ldflags '-linkmode=auto' || exit 1
 # linkmode=internal fails on dragonfly since errno is a TLS relocation.
 [ "$GOHOSTOS" == dragonfly ] || go test -ldflags '-linkmode=internal' || exit 1
+# TODO(austin): Add linux-ppc64(le) once external linking works (issue #8912)
 case "$GOHOSTOS-$GOARCH" in
 openbsd-386 | openbsd-amd64)
        # test linkmode=external, but __thread not supported, so skip testtls.
index a2aba632e9b5bf000bdf8c59f9cc7e3804decd5e..a708aa9377cb6d7ce09cf7a7483c79d0014ca86d 100644 (file)
@@ -29,8 +29,29 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
        MOVD    R3, (g_stack+stack_lo)(g)
        MOVD    R1, (g_stack+stack_hi)(g)
 
-       // TODO: if there is a _cgo_init, call it.
-       // TODO: add TLS
+       // if there is a _cgo_init, call it using the gcc ABI.
+       MOVD    _cgo_init(SB), R12
+       CMP     R0, R12
+       BEQ     nocgo
+       MOVD    R12, CTR                // r12 = "global function entry point"
+       MOVD    R13, R5                 // arg 2: TLS base pointer
+       MOVD    $setg_gcc<>(SB), R4     // arg 1: setg
+       MOVD    g, R3                   // arg 0: G
+       // C functions expect 32 bytes of space on caller stack frame
+       // and a 16-byte aligned R1
+       MOVD    R1, R14                 // save current stack
+       SUB     $32, R1                 // reserve 32 bytes
+       RLDCR   $0, R1, $~15, R1        // 16-byte align
+       BL      (CTR)                   // may clobber R0, R3-R12
+       MOVD    R14, R1                 // restore stack
+       XOR     R0, R0                  // fix R0
+
+nocgo:
+       // update stackguard after _cgo_init
+       MOVD    (g_stack+stack_lo)(g), R3
+       ADD     $const__StackGuard, R3
+       MOVD    R3, g_stackguard0(g)
+       MOVD    R3, g_stackguard1(g)
 
        // set the per-goroutine and per-mach "registers"
        MOVD    $runtime·m0(SB), R3
@@ -71,6 +92,11 @@ TEXT runtime·breakpoint(SB),NOSPLIT,$-8-0
 TEXT runtime·asminit(SB),NOSPLIT,$-8-0
        RETURN
 
+TEXT _cgo_reginit(SB),NOSPLIT,$-8-0
+       // crosscall_ppc64 and crosscall2 need to reginit, but can't
+       // get at the 'runtime.reginit' symbol.
+       BR      runtime·reginit(SB)
+
 TEXT runtime·reginit(SB),NOSPLIT,$-8-0
        // set R0 to zero, it's expected by the toolchain
        XOR R0, R0
@@ -625,26 +651,207 @@ TEXT gosave<>(SB),NOSPLIT,$-8
 // aligned appropriately for the gcc ABI.
 // See cgocall.c for more details.
 TEXT ·asmcgocall(SB),NOSPLIT,$0-16
-       MOVD    R0, 21(R0)
+       MOVD    fn+0(FP), R3
+       MOVD    arg+8(FP), R4
+       BL      asmcgocall<>(SB)
+       RET
+
+TEXT ·asmcgocall_errno(SB),NOSPLIT,$0-24
+       MOVD    fn+0(FP), R3
+       MOVD    arg+8(FP), R4
+       BL      asmcgocall<>(SB)
+       MOVD    R3, ret+16(FP)
+       RET
+
+// asmcgocall common code. fn in R3, arg in R4. returns errno in R3.
+TEXT asmcgocall<>(SB),NOSPLIT,$0-0
+       MOVD    R1, R2          // save original stack pointer
+       MOVD    g, R5
+
+       // Figure out if we need to switch to m->g0 stack.
+       // We get called to create new OS threads too, and those
+       // come in on the m->g0 stack already.
+       MOVD    g_m(g), R6
+       MOVD    m_g0(R6), R6
+       CMP     R6, g
+       BEQ     g0
+       BL      gosave<>(SB)
+       MOVD    R6, g
+       BL      runtime·save_g(SB)
+       MOVD    (g_sched+gobuf_sp)(g), R1
+
+       // Now on a scheduling stack (a pthread-created stack).
+g0:
+       // Save room for two of our pointers, plus 32 bytes of callee
+       // save area that lives on the caller stack.
+       SUB     $48, R1
+       RLDCR   $0, R1, $~15, R1        // 16-byte alignment for gcc ABI
+       MOVD    R5, 40(R1)      // save old g on stack
+       MOVD    (g_stack+stack_hi)(R5), R5
+       SUB     R2, R5
+       MOVD    R5, 32(R1)      // save depth in old g stack (can't just save SP, as stack might be copied during a callback)
+       MOVD    R0, 0(R1)       // clear back chain pointer (TODO can we give it real back trace information?)
+       // This is a "global call", so put the global entry point in r12
+       MOVD    R3, R12
+       MOVD    R12, CTR
+       MOVD    R4, R3          // arg in r3
+       BL      (CTR)
+
+       // C code can clobber R0, so set it back to 0.  F27-F31 are
+       // callee save, so we don't need to recover those.
+       XOR     R0, R0
+       // Restore g, stack pointer.  R3 is errno, so don't touch it
+       MOVD    40(R1), g
+       BL      runtime·save_g(SB)
+       MOVD    (g_stack+stack_hi)(g), R5
+       MOVD    32(R1), R6
+       SUB     R6, R5
+       MOVD    R5, R1
+       RET
 
 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
 // Turn the fn into a Go func (by taking its address) and call
 // cgocallback_gofunc.
 TEXT runtime·cgocallback(SB),NOSPLIT,$24-24
-       MOVD    R0, 22(R0)
+       MOVD    $fn+0(FP), R3
+       MOVD    R3, 8(R1)
+       MOVD    frame+8(FP), R3
+       MOVD    R3, 16(R1)
+       MOVD    framesize+16(FP), R3
+       MOVD    R3, 24(R1)
+       MOVD    $runtime·cgocallback_gofunc(SB), R3
+       MOVD    R3, CTR
+       BL      (CTR)
+       RET
 
 // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
 // See cgocall.c for more details.
-TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-24
-       MOVD    R0, 23(R0)
+TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-24
+       NO_LOCAL_POINTERS
+
+       // Load m and g from thread-local storage.
+       MOVB    runtime·iscgo(SB), R3
+       CMP     R3, $0
+       BEQ     nocgo
+       BL      runtime·load_g(SB)
+nocgo:
+
+       // If g is nil, Go did not create the current thread.
+       // Call needm to obtain one for temporary use.
+       // In this case, we're running on the thread stack, so there's
+       // lots of space, but the linker doesn't know. Hide the call from
+       // the linker analysis by using an indirect call.
+       CMP     g, $0
+       BNE     havem
+       MOVD    g, savedm-8(SP) // g is zero, so is m.
+       MOVD    $runtime·needm(SB), R3
+       MOVD    R3, CTR
+       BL      (CTR)
+
+       // Set m->sched.sp = SP, so that if a panic happens
+       // during the function we are about to execute, it will
+       // have a valid SP to run on the g0 stack.
+       // The next few lines (after the havem label)
+       // will save this SP onto the stack and then write
+       // the same SP back to m->sched.sp. That seems redundant,
+       // but if an unrecovered panic happens, unwindm will
+       // restore the g->sched.sp from the stack location
+       // and then systemstack will try to use it. If we don't set it here,
+       // that restored SP will be uninitialized (typically 0) and
+       // will not be usable.
+       MOVD    g_m(g), R3
+       MOVD    m_g0(R3), R3
+       MOVD    R1, (g_sched+gobuf_sp)(R3)
+
+havem:
+       MOVD    g_m(g), R8
+       MOVD    R8, savedm-8(SP)
+       // Now there's a valid m, and we're running on its m->g0.
+       // Save current m->g0->sched.sp on stack and then set it to SP.
+       // Save current sp in m->g0->sched.sp in preparation for
+       // switch back to m->curg stack.
+       // NOTE: unwindm knows that the saved g->sched.sp is at 8(R1) aka savedsp-16(SP).
+       MOVD    m_g0(R8), R3
+       MOVD    (g_sched+gobuf_sp)(R3), R4
+       MOVD    R4, savedsp-16(SP)
+       MOVD    R1, (g_sched+gobuf_sp)(R3)
+
+       // Switch to m->curg stack and call runtime.cgocallbackg.
+       // Because we are taking over the execution of m->curg
+       // but *not* resuming what had been running, we need to
+       // save that information (m->curg->sched) so we can restore it.
+       // We can restore m->curg->sched.sp easily, because calling
+       // runtime.cgocallbackg leaves SP unchanged upon return.
+       // To save m->curg->sched.pc, we push it onto the stack.
+       // This has the added benefit that it looks to the traceback
+       // routine like cgocallbackg is going to return to that
+       // PC (because the frame we allocate below has the same
+       // size as cgocallback_gofunc's frame declared above)
+       // so that the traceback will seamlessly trace back into
+       // the earlier calls.
+       //
+       // In the new goroutine, -16(SP) and -8(SP) are unused.
+       MOVD    m_curg(R8), g
+       BL      runtime·save_g(SB)
+       MOVD    (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
+       MOVD    (g_sched+gobuf_pc)(g), R5
+       MOVD    R5, -24(R4)
+       MOVD    $-24(R4), R1
+       BL      runtime·cgocallbackg(SB)
+
+       // Restore g->sched (== m->curg->sched) from saved values.
+       MOVD    0(R1), R5
+       MOVD    R5, (g_sched+gobuf_pc)(g)
+       MOVD    $24(R1), R4
+       MOVD    R4, (g_sched+gobuf_sp)(g)
+
+       // Switch back to m->g0's stack and restore m->g0->sched.sp.
+       // (Unlike m->curg, the g0 goroutine never uses sched.pc,
+       // so we do not have to restore it.)
+       MOVD    g_m(g), R8
+       MOVD    m_g0(R8), g
+       BL      runtime·save_g(SB)
+       MOVD    (g_sched+gobuf_sp)(g), R1
+       MOVD    savedsp-16(SP), R4
+       MOVD    R4, (g_sched+gobuf_sp)(g)
+
+       // If the m on entry was nil, we called needm above to borrow an m
+       // for the duration of the call. Since the call is over, return it with dropm.
+       MOVD    savedm-8(SP), R6
+       CMP     R6, $0
+       BNE     droppedm
+       MOVD    $runtime·dropm(SB), R3
+       MOVD    R3, CTR
+       BL      (CTR)
+droppedm:
+
+       // Done!
+       RET
 
 // void setg(G*); set g. for use by needm.
 TEXT runtime·setg(SB), NOSPLIT, $0-8
-       MOVD    R0, 24(R0)
+       MOVD    gg+0(FP), g
+       // This only happens if iscgo, so jump straight to save_g
+       BL      runtime·save_g(SB)
+       RET
+
+// void setg_gcc(G*); set g in C TLS.
+// Must obey the gcc calling convention.
+TEXT setg_gcc<>(SB),NOSPLIT,$-8-0
+       // The standard prologue clobbers R31, which is callee-save in
+       // the C ABI, so we have to use $-8-0 and save LR ourselves.
+       MOVD    LR, R4
+       // Also save g and R31, since they're callee-save in C ABI
+       MOVD    R31, R5
+       MOVD    g, R6
+
+       MOVD    R3, g
+       BL      runtime·save_g(SB)
 
-// void setg_gcc(G*); set g called from gcc.
-TEXT setg_gcc<>(SB),NOSPLIT,$0
-       MOVD    R0, 25(R0)
+       MOVD    R6, g
+       MOVD    R5, R31
+       MOVD    R4, LR
+       RET
 
 TEXT runtime·getcallerpc(SB),NOSPLIT,$-8-16
        MOVD    0(R1), R3
@@ -989,8 +1196,21 @@ TEXT runtime·return0(SB), NOSPLIT, $0
 
 // Called from cgo wrappers, this function returns g->m->curg.stack.hi.
 // Must obey the gcc calling convention.
-TEXT _cgo_topofstack(SB),NOSPLIT,$0
-       MOVD    R0, 26(R0)
+TEXT _cgo_topofstack(SB),NOSPLIT,$-8
+       // g (R30) and R31 are callee-save in the C ABI, so save them
+       MOVD    g, R4
+       MOVD    R31, R5
+       MOVD    LR, R6
+
+       BL      runtime·load_g(SB)     // clobbers g (R30), R31
+       MOVD    g_m(g), R3
+       MOVD    m_curg(R3), R3
+       MOVD    (g_stack+stack_hi)(R3), R3
+
+       MOVD    R4, g
+       MOVD    R5, R31
+       MOVD    R6, LR
+       RET
 
 // The top-most function running on a goroutine
 // returns to goexit+PCQuantum.
diff --git a/src/runtime/cgo/asm_ppc64x.s b/src/runtime/cgo/asm_ppc64x.s
new file mode 100644 (file)
index 0000000..0c08a1d
--- /dev/null
@@ -0,0 +1,124 @@
+// Copyright 2014 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.
+
+// +build ppc64 ppc64le
+
+#include "textflag.h"
+
+/*
+ * void crosscall2(void (*fn)(void*, int32), void*, int32)
+ * Save registers and call fn with two arguments.
+ * crosscall2 obeys the C ABI; fn obeys the Go ABI.
+ */
+TEXT crosscall2(SB),NOSPLIT,$-8
+       // TODO(austin): ABI v1 (fn is probably a function descriptor)
+
+       // Start with standard C stack frame layout and linkage
+       MOVD    LR, R0
+       MOVD    R0, 16(R1)      // Save LR in caller's frame
+       MOVD    R2, 24(R1)      // Save TOC in caller's frame
+
+       BL      saveregs2<>(SB)
+
+       MOVDU   R1, (-288-3*8)(R1)
+
+       // Initialize Go ABI environment
+       BL      runtime·reginit(SB)
+       BL      runtime·load_g(SB)
+
+       MOVD    R3, CTR
+       MOVD    R4, 8(R1)
+       MOVD    R5, 16(R1)
+       BL      (CTR)
+
+       ADD     $(288+3*8), R1
+
+       BL      restoreregs2<>(SB)
+
+       MOVD    24(R1), R2
+       MOVD    16(R1), R0
+       MOVD    R0, LR
+       RET
+
+TEXT saveregs2<>(SB),NOSPLIT,$-8
+       // O=-288; for R in R{14..31}; do echo "\tMOVD\t$R, $O(R1)"|sed s/R30/g/; ((O+=8)); done; for F in F{14..31}; do echo "\tFMOVD\t$F, $O(R1)"; ((O+=8)); done
+       MOVD    R14, -288(R1)
+       MOVD    R15, -280(R1)
+       MOVD    R16, -272(R1)
+       MOVD    R17, -264(R1)
+       MOVD    R18, -256(R1)
+       MOVD    R19, -248(R1)
+       MOVD    R20, -240(R1)
+       MOVD    R21, -232(R1)
+       MOVD    R22, -224(R1)
+       MOVD    R23, -216(R1)
+       MOVD    R24, -208(R1)
+       MOVD    R25, -200(R1)
+       MOVD    R26, -192(R1)
+       MOVD    R27, -184(R1)
+       MOVD    R28, -176(R1)
+       MOVD    R29, -168(R1)
+       MOVD    g, -160(R1)
+       MOVD    R31, -152(R1)
+       FMOVD   F14, -144(R1)
+       FMOVD   F15, -136(R1)
+       FMOVD   F16, -128(R1)
+       FMOVD   F17, -120(R1)
+       FMOVD   F18, -112(R1)
+       FMOVD   F19, -104(R1)
+       FMOVD   F20, -96(R1)
+       FMOVD   F21, -88(R1)
+       FMOVD   F22, -80(R1)
+       FMOVD   F23, -72(R1)
+       FMOVD   F24, -64(R1)
+       FMOVD   F25, -56(R1)
+       FMOVD   F26, -48(R1)
+       FMOVD   F27, -40(R1)
+       FMOVD   F28, -32(R1)
+       FMOVD   F29, -24(R1)
+       FMOVD   F30, -16(R1)
+       FMOVD   F31, -8(R1)
+
+       RET
+
+TEXT restoreregs2<>(SB),NOSPLIT,$-8
+       // O=-288; for R in R{14..31}; do echo "\tMOVD\t$O(R1), $R"|sed s/R30/g/; ((O+=8)); done; for F in F{14..31}; do echo "\tFMOVD\t$O(R1), $F"; ((O+=8)); done
+       MOVD    -288(R1), R14
+       MOVD    -280(R1), R15
+       MOVD    -272(R1), R16
+       MOVD    -264(R1), R17
+       MOVD    -256(R1), R18
+       MOVD    -248(R1), R19
+       MOVD    -240(R1), R20
+       MOVD    -232(R1), R21
+       MOVD    -224(R1), R22
+       MOVD    -216(R1), R23
+       MOVD    -208(R1), R24
+       MOVD    -200(R1), R25
+       MOVD    -192(R1), R26
+       MOVD    -184(R1), R27
+       MOVD    -176(R1), R28
+       MOVD    -168(R1), R29
+       MOVD    -160(R1), g
+       MOVD    -152(R1), R31
+       FMOVD   -144(R1), F14
+       FMOVD   -136(R1), F15
+       FMOVD   -128(R1), F16
+       FMOVD   -120(R1), F17
+       FMOVD   -112(R1), F18
+       FMOVD   -104(R1), F19
+       FMOVD   -96(R1), F20
+       FMOVD   -88(R1), F21
+       FMOVD   -80(R1), F22
+       FMOVD   -72(R1), F23
+       FMOVD   -64(R1), F24
+       FMOVD   -56(R1), F25
+       FMOVD   -48(R1), F26
+       FMOVD   -40(R1), F27
+       FMOVD   -32(R1), F28
+       FMOVD   -24(R1), F29
+       FMOVD   -16(R1), F30
+       FMOVD   -8(R1), F31
+
+       RET
diff --git a/src/runtime/cgo/gcc_linux_ppc64x.c b/src/runtime/cgo/gcc_linux_ppc64x.c
new file mode 100644 (file)
index 0000000..b176295
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright 2014 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.
+
+// +build ppc64 ppc64le
+
+#include <pthread.h>
+#include <string.h>
+#include <signal.h>
+#include "libcgo.h"
+
+static void *threadentry(void*);
+
+void (*x_cgo_inittls)(void **tlsg, void **tlsbase);
+static void (*setg_gcc)(void*);
+
+void
+x_cgo_init(G *g, void (*setg)(void*), void **tlsbase)
+{
+       pthread_attr_t attr;
+       size_t size;
+
+       setg_gcc = setg;
+       pthread_attr_init(&attr);
+       pthread_attr_getstacksize(&attr, &size);
+       g->stacklo = (uintptr)&attr - size + 4096;
+       pthread_attr_destroy(&attr);
+}
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+       pthread_attr_t attr;
+       sigset_t ign, oset;
+       pthread_t p;
+       size_t size;
+       int err;
+
+       sigfillset(&ign);
+       pthread_sigmask(SIG_SETMASK, &ign, &oset);
+
+       pthread_attr_init(&attr);
+       pthread_attr_getstacksize(&attr, &size);
+       // Leave stacklo=0 and set stackhi=size; mstack will do the rest.
+       ts->g->stackhi = size;
+       err = pthread_create(&p, &attr, threadentry, ts);
+
+       pthread_sigmask(SIG_SETMASK, &oset, nil);
+
+       if (err != 0) {
+               fatalf("pthread_create failed: %s", strerror(err));
+       }
+}
+
+extern void crosscall_ppc64(void (*fn)(void), void *g);
+
+static void*
+threadentry(void *v)
+{
+       ThreadStart ts;
+
+       ts = *(ThreadStart*)v;
+       free(v);
+
+       // Save g for this thread in C TLS
+       setg_gcc((void*)ts.g);
+
+       crosscall_ppc64(ts.fn, (void*)ts.g);
+       return nil;
+}
diff --git a/src/runtime/cgo/gcc_ppc64x.S b/src/runtime/cgo/gcc_ppc64x.S
new file mode 100644 (file)
index 0000000..fc20277
--- /dev/null
@@ -0,0 +1,140 @@
+// Copyright 2014 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.
+
+// +build ppc64 ppc64le
+
+/*
+ * Apple still insists on underscore prefixes for C function names.
+ */
+#if defined(__APPLE__)
+#define EXT(s) _##s
+#else
+#define EXT(s) s
+#endif
+
+/*
+ * void crosscall_ppc64(void (*fn)(void), void *g)
+ *
+ * Calling into the 9g tool chain, where all registers are caller save.
+ * Called from standard ppc64 C ABI, where r2, r14-r31, f14-f31 are
+ * callee-save, so they must be saved explicitly.
+ */
+.globl EXT(crosscall_ppc64)
+EXT(crosscall_ppc64):
+       // Start with standard C stack frame layout and linkage
+       mflr    %r0
+       std     %r0, 16(%r1)    // Save LR in caller's frame
+       std     %r2, 24(%r1)    // Save TOC in caller's frame
+       bl      saveregs
+       stdu    %r1, -296(%r1)
+
+       // Set up Go ABI constant registers
+       bl      _cgo_reginit
+
+       // Restore g pointer (r30 in Go ABI, which may have been clobbered by C)
+       mr      %r30, %r4
+
+       // Call fn
+       mtctr   %r3
+       bctrl
+
+       addi    %r1, %r1, 296
+       bl      restoreregs
+       ld      %r2, 24(%r1)
+       ld      %r0, 16(%r1)
+       mtlr    %r0
+       blr
+
+saveregs:
+       // Save callee-save registers
+       // O=-288; for R in %r{14..31}; do echo "\tstd\t$R, $O(%r1)"; ((O+=8)); done; for F in f{14..31}; do echo "\tstfd\t$F, $O(%r1)"; ((O+=8)); done
+       std     %r14, -288(%r1)
+       std     %r15, -280(%r1)
+       std     %r16, -272(%r1)
+       std     %r17, -264(%r1)
+       std     %r18, -256(%r1)
+       std     %r19, -248(%r1)
+       std     %r20, -240(%r1)
+       std     %r21, -232(%r1)
+       std     %r22, -224(%r1)
+       std     %r23, -216(%r1)
+       std     %r24, -208(%r1)
+       std     %r25, -200(%r1)
+       std     %r26, -192(%r1)
+       std     %r27, -184(%r1)
+       std     %r28, -176(%r1)
+       std     %r29, -168(%r1)
+       std     %r30, -160(%r1)
+       std     %r31, -152(%r1)
+       stfd    %f14, -144(%r1)
+       stfd    %f15, -136(%r1)
+       stfd    %f16, -128(%r1)
+       stfd    %f17, -120(%r1)
+       stfd    %f18, -112(%r1)
+       stfd    %f19, -104(%r1)
+       stfd    %f20, -96(%r1)
+       stfd    %f21, -88(%r1)
+       stfd    %f22, -80(%r1)
+       stfd    %f23, -72(%r1)
+       stfd    %f24, -64(%r1)
+       stfd    %f25, -56(%r1)
+       stfd    %f26, -48(%r1)
+       stfd    %f27, -40(%r1)
+       stfd    %f28, -32(%r1)
+       stfd    %f29, -24(%r1)
+       stfd    %f30, -16(%r1)
+       stfd    %f31, -8(%r1)
+
+       blr
+
+restoreregs:
+       // O=-288; for R in %r{14..31}; do echo "\tld\t$R, $O(%r1)"; ((O+=8)); done; for F in %f{14..31}; do echo "\tlfd\t$F, $O(%r1)"; ((O+=8)); done
+       ld      %r14, -288(%r1)
+       ld      %r15, -280(%r1)
+       ld      %r16, -272(%r1)
+       ld      %r17, -264(%r1)
+       ld      %r18, -256(%r1)
+       ld      %r19, -248(%r1)
+       ld      %r20, -240(%r1)
+       ld      %r21, -232(%r1)
+       ld      %r22, -224(%r1)
+       ld      %r23, -216(%r1)
+       ld      %r24, -208(%r1)
+       ld      %r25, -200(%r1)
+       ld      %r26, -192(%r1)
+       ld      %r27, -184(%r1)
+       ld      %r28, -176(%r1)
+       ld      %r29, -168(%r1)
+       ld      %r30, -160(%r1)
+       ld      %r31, -152(%r1)
+       lfd     %f14, -144(%r1)
+       lfd     %f15, -136(%r1)
+       lfd     %f16, -128(%r1)
+       lfd     %f17, -120(%r1)
+       lfd     %f18, -112(%r1)
+       lfd     %f19, -104(%r1)
+       lfd     %f20, -96(%r1)
+       lfd     %f21, -88(%r1)
+       lfd     %f22, -80(%r1)
+       lfd     %f23, -72(%r1)
+       lfd     %f24, -64(%r1)
+       lfd     %f25, -56(%r1)
+       lfd     %f26, -48(%r1)
+       lfd     %f27, -40(%r1)
+       lfd     %f28, -32(%r1)
+       lfd     %f29, -24(%r1)
+       lfd     %f30, -16(%r1)
+       lfd     %f31, -8(%r1)
+
+       blr
+
+.globl EXT(__stack_chk_fail_local)
+EXT(__stack_chk_fail_local):
+1:
+       // TODO(austin)
+       b 1b
+
+#ifdef __ELF__
+.section .note.GNU-stack,"",%progbits
+#endif
index dbeea200d45307d35556ea631d8d5454e0c1380f..96873cc2da713b90f8b2846873f2ea21ca556b21 100644 (file)
@@ -229,6 +229,11 @@ func cgocallbackg1() {
        case "386":
                // On 386, stack frame is three words, plus caller PC.
                cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
+       case "ppc64", "ppc64le":
+               // On ppc64, stack frame is two words and there's a
+               // saved LR between SP and the stack frame and between
+               // the stack frame and the arguments.
+               cb = (*args)(unsafe.Pointer(sp + 4*ptrSize))
        }
 
        // Invoke callback.
@@ -263,6 +268,8 @@ func unwindm(restore *bool) {
                sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp))
        case "arm":
                sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 4))
+       case "ppc64", "ppc64le":
+               sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 8))
        }
        releasem(mp)
 }
index 29f90fa36d27fc521c008ad71b923b103334bd52..7152b93195bd550556387f6acbaca1deeccbacfb 100644 (file)
@@ -68,6 +68,11 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) {
                        t.Skipf("no external linking on OS X 10.6")
                }
        }
+       if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
+               // TODO(austin) External linking not implemented on
+               // ppc64 (issue #8912)
+               t.Skipf("no external linking on ppc64")
+       }
        got := executeTest(t, cgoExternalThreadSIGPROFSource, nil)
        want := "OK\n"
        if got != want {
index 33bbbbd1bf38042d6f0a31d31d0b926f13f059ce..33e973db96599475320ea19a81bbd853759fd8a7 100644 (file)
@@ -7,6 +7,12 @@ TEXT _rt0_ppc64_linux(SB),NOSPLIT,$0
        DWORD $0
 
 TEXT _main<>(SB),NOSPLIT,$-8
+       // In a statically linked binary, the stack contains argc,
+       // argv as argc string pointers followed by a NULL, envv as a
+       // sequence of string pointers followed by a NULL, and auxv.
+       // There is no TLS base pointer.
+       //
+       // TODO(austin): Support ABI v1 dynamic linking entry point
        MOVD 0(R1), R3 // argc
        ADD $8, R1, R4 // argv
        BR main(SB)
index dda29ab3a0977045912c8591a06f4ad8f80cbd6d..f5c0af5c715676ab2974d573a4df6febc7123ff2 100644 (file)
@@ -4,11 +4,29 @@ TEXT _rt0_ppc64le_linux(SB),NOSPLIT,$0
        BR _main<>(SB)
 
 TEXT _main<>(SB),NOSPLIT,$-8
-       MOVD 0(R1), R3 // argc
-       ADD $8, R1, R4 // argv
+       // In a statically linked binary, the stack contains argc,
+       // argv as argc string pointers followed by a NULL, envv as a
+       // sequence of string pointers followed by a NULL, and auxv.
+       // There is no TLS base pointer.
+       //
+       // In a dynamically linked binary, r3 contains argc, r4
+       // contains argv, r5 contains envp, r6 contains auxv, and r13
+       // contains the TLS pointer.
+       //
+       // Figure out which case this is by looking at r4: if it's 0,
+       // we're statically linked; otherwise we're dynamically
+       // linked.
+       CMP     R0, R4
+       BNE     dlink
+
+       // Statically linked
+       MOVD    0(R1), R3 // argc
+       ADD     $8, R1, R4 // argv
        MOVD    $runtime·tls0(SB), R13 // TLS
        ADD     $0x7000, R13
-       BR main(SB)
+
+dlink:
+       BR      main(SB)
 
 TEXT main(SB),NOSPLIT,$-8
        MOVD    $runtime·rt0_go(SB), R31
index 495b5f915a80493bb1d0686cc8bf56063a59e491..e6510a8aa3943007a9e19b9477e94381ab1a294d 100644 (file)
@@ -77,6 +77,9 @@ func goargs() {
 }
 
 func goenvs_unix() {
+       // TODO(austin): ppc64 in dynamic linking mode doesn't
+       // guarantee env[] will immediately follow argv.  Might cause
+       // problems.
        n := int32(0)
        for argv_index(argv, argc+1+n) != nil {
                n++
index 4a4f440c53dbe5689819ca79c7ad09367c827158..b9d8be11c5af9c2e0763954758b10ee8bae2aac0 100644 (file)
@@ -193,6 +193,13 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64
        // initialize essential registers (just in case)
        BL      runtime·reginit(SB)
 
+       // this might be called in external code context,
+       // where g is not set.
+       MOVB    runtime·iscgo(SB), R6
+       CMP     R6, $0
+       BEQ     2(PC)
+       BL      runtime·load_g(SB)
+
        // check that g exists
        CMP     g, $0
        BNE     6(PC)
index fa1f9ac6f05dcec6f3e16017be45b9aaa6690999..fc1718f50842036b4f89149f5bb73e252ada4f17 100644 (file)
@@ -20,6 +20,8 @@
 // ppc64 code that will overwrite this register.
 //
 // If !iscgo, this is a no-op.
+//
+// NOTE: setg_gcc<> assume this clobbers only R31.
 TEXT runtime·save_g(SB),NOSPLIT,$-8-0
        MOVB    runtime·iscgo(SB), R31
        CMP     R31, $0
@@ -46,6 +48,8 @@ nocgo:
 // This is never called directly from C code (it doesn't have to
 // follow the C ABI), but it may be called from a C context, where the
 // usual Go registers aren't set up.
+//
+// NOTE: _cgo_topofstack assumes this only clobbers g (R30), and R31.
 TEXT runtime·load_g(SB),NOSPLIT,$-8-0
        MOVD    $runtime·tlsg(SB), R31
        // R13 is the C ABI TLS base pointer + 0x7000