aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorMichael Hudson-Doyle <michael.hudson@canonical.com>2015-10-16 15:42:09 +1300
committerMichael Hudson-Doyle <michael.hudson@canonical.com>2015-11-12 23:18:58 +0000
commit368d54841704fabfcd87a39c66ff56ca87f6eeea (patch)
tree994c86285a0e42ae5bceef86df39c0258fb873ab /src/runtime
parentdbdd8c2c94ec118050331455681606b9f14b6244 (diff)
downloadgo-368d54841704fabfcd87a39c66ff56ca87f6eeea.tar.xz
cmd/compile, cmd/link, runtime: on ppc64x, maintain the TOC pointer in R2 when compiling PIC
The PowerPC ISA does not have a PC-relative load instruction, which poses obvious challenges when generating position-independent code. The way the ELFv2 ABI addresses this is to specify that r2 points to a per "module" (shared library or executable) TOC pointer. Maintaining this pointer requires cooperation between codegen and the system linker: * Non-leaf functions leave space on the stack at r1+24 to save the TOC pointer. * A call to a function that *might* have to go via a PLT stub must be followed by a nop instruction that the system linker can replace with "ld r1, 24(r1)" to restore the TOC pointer (only when dynamically linking Go code). * When calling a function via a function pointer, the address of the function must be in r12, and the first couple of instructions (the "global entry point") of the called function use this to derive the address of the TOC for the module it is in. * When calling a function that is implemented in the same module, the system linker adjusts the call to skip over the instructions mentioned above (the "local entry point"), assuming that r2 is already correctly set. So this changeset adds the global entry point instructions, sets the metadata so the system linker knows where the local entry point is, inserts code to save the TOC pointer at 24(r1), adds a nop after any call not known to be local and copes with the odd non-local code transfer in the runtime (e.g. the stuff around jmpdefer). It does not actually compile PIC yet. Change-Id: I7522e22bdfd2f891745a900c60254fe9e372c854 Reviewed-on: https://go-review.googlesource.com/15967 Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/asm_ppc64x.s57
-rw-r--r--src/runtime/cgo/gcc_ppc64x.S1
-rw-r--r--src/runtime/proc.go1
-rw-r--r--src/runtime/sys_linux_ppc64x.s2
-rw-r--r--src/runtime/sys_nonppc64x.go10
-rw-r--r--src/runtime/sys_ppc64x.go2
6 files changed, 66 insertions, 7 deletions
diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s
index 5f5a658562..86d8d04fff 100644
--- a/src/runtime/asm_ppc64x.s
+++ b/src/runtime/asm_ppc64x.s
@@ -17,8 +17,9 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
BL runtime·reginit(SB)
SUB $(FIXED_FRAME+16), R1
- MOVW R3, FIXED_FRAME+0(R1) // argc
- MOVD R4, FIXED_FRAME+8(R1) // argv
+ MOVD R2, 24(R1) // stash the TOC pointer away again now we've created a new frame
+ MOVW R3, FIXED_FRAME+0(R1) // argc
+ MOVD R4, FIXED_FRAME+8(R1) // argv
// create istack out of the given (operating system) stack.
// _cgo_init may update stackguard.
@@ -45,6 +46,7 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
RLDCR $0, R1, $~15, R1 // 16-byte align
BL (CTR) // may clobber R0, R3-R12
MOVD R14, R1 // restore stack
+ MOVD 24(R1), R2
XOR R0, R0 // fix R0
nocgo:
@@ -181,6 +183,7 @@ TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8
MOVDU R0, -8(R1)
MOVDU R0, -8(R1)
BL (CTR)
+ MOVD 24(R1), R2
BR runtime·badmcall2(SB)
// systemstack_switch is a dummy routine that systemstack leaves at the bottom
@@ -189,6 +192,11 @@ TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8
// at the top of the system stack because the one at the top of
// the system stack terminates the stack walk (see topofstack()).
TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0
+ // We have several undefs here so that 16 bytes past
+ // $runtime·systemstack_switch lies within them whether or not the
+ // instructions that derive r2 from r12 are there.
+ UNDEF
+ UNDEF
UNDEF
BL (LR) // make sure this function is not leaf
RET
@@ -221,7 +229,7 @@ switch:
// save our state in g->sched. Pretend to
// be systemstack_switch if the G stack is scanned.
MOVD $runtime·systemstack_switch(SB), R6
- ADD $8, R6 // get past prologue
+ ADD $16, R6 // get past prologue (including r2-setting instructions when they're there)
MOVD R6, (g_sched+gobuf_pc)(g)
MOVD R1, (g_sched+gobuf_sp)(g)
MOVD R0, (g_sched+gobuf_lr)(g)
@@ -242,6 +250,13 @@ switch:
MOVD R12, CTR
BL (CTR)
+ // restore TOC pointer. It seems unlikely that we will use systemstack
+ // to call a function defined in another module, but the results of
+ // doing so would be so confusing that it's worth doing this.
+ MOVD g_m(g), R3
+ MOVD m_curg(R3), g
+ MOVD (g_sched+gobuf_sp)(g), R3
+ MOVD 24(R3), R2
// switch back to g
MOVD g_m(g), R3
MOVD m_curg(R3), g
@@ -255,6 +270,7 @@ noswitch:
MOVD 0(R11), R12 // code pointer
MOVD R12, CTR
BL (CTR)
+ MOVD 24(R1), R2
RET
/*
@@ -402,6 +418,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
MOVD R12, CTR; \
PCDATA $PCDATA_StackMapIndex, $0; \
BL (CTR); \
+ MOVD 24(R1), R2; \
/* copy return values back */ \
MOVD arg+16(FP), R3; \
MOVWZ n+24(FP), R4; \
@@ -465,11 +482,15 @@ TEXT runtime·procyield(SB),NOSPLIT,$0-0
// void jmpdefer(fv, sp);
// called from deferreturn.
// 1. grab stored LR for caller
-// 2. sub 4 bytes to get back to BL deferreturn
+// 2. sub 8 bytes to get back to either nop or toc reload before deferreturn
// 3. BR to fn
+// When dynamically linking Go, it is not sufficient to rewind to the BL
+// deferreturn -- we might be jumping between modules and so we need to reset
+// the TOC pointer in r2. To do this, codegen inserts MOVD 24(R1), R2 *before*
+// the BL deferreturn and jmpdefer rewinds to that.
TEXT runtime·jmpdefer(SB), NOSPLIT|NOFRAME, $0-16
MOVD 0(R1), R31
- SUB $4, R31
+ SUB $8, R31
MOVD R31, LR
MOVD fv+0(FP), R11
@@ -532,8 +553,13 @@ g0:
// 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
+ // Restore g, stack pointer, toc pointer.
+ // R3 is errno, so don't touch it
MOVD 40(R1), g
+ MOVD (g_stack+stack_hi)(g), R5
+ MOVD 32(R1), R6
+ SUB R6, R5
+ MOVD 24(R5), R2
BL runtime·save_g(SB)
MOVD (g_stack+stack_hi)(g), R5
MOVD 32(R1), R6
@@ -1007,8 +1033,16 @@ TEXT _cgo_topofstack(SB),NOSPLIT|NOFRAME,$0
// The top-most function running on a goroutine
// returns to goexit+PCQuantum.
+//
+// When dynamically linking Go, it can be returned to from a function
+// implemented in a different module and so needs to reload the TOC pointer
+// from the stack (although this function declares that it does not set up x-a
+// frame, newproc1 does in fact allocate one for goexit and saves the TOC
+// pointer in the correct place).
+// goexit+_PCQuantum is halfway through the usual global entry point prologue
+// that derives r2 from r12 which is a bit silly, but not harmful.
TEXT runtime·goexit(SB),NOSPLIT|NOFRAME,$0-0
- MOVD R0, R0 // NOP
+ MOVD 24(R1), R2
BL runtime·goexit1(SB) // does not return
// traceback from goexit1 must hit code range of goexit
MOVD R0, R0 // NOP
@@ -1027,3 +1061,12 @@ TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8
TEXT runtime·sigreturn(SB),NOSPLIT,$0-8
RET
+
+// prepGoExitFrame saves the current TOC pointer (i.e. the TOC pointer for the
+// module containing runtime) to the frame that goexit will execute in when
+// the goroutine exits. It's implemented in assembly mainly because that's the
+// easiest way to get access to R2.
+TEXT runtime·prepGoExitFrame(SB),NOSPLIT,$0-8
+ MOVD sp+0(FP), R3
+ MOVD R2, 24(R3)
+ RET
diff --git a/src/runtime/cgo/gcc_ppc64x.S b/src/runtime/cgo/gcc_ppc64x.S
index 58f13364c6..a817b3a4fc 100644
--- a/src/runtime/cgo/gcc_ppc64x.S
+++ b/src/runtime/cgo/gcc_ppc64x.S
@@ -31,6 +31,7 @@ EXT(crosscall_ppc64):
// Set up Go ABI constant registers
bl _cgo_reginit
+ nop
// Restore g pointer (r30 in Go ABI, which may have been clobbered by C)
mr %r30, %r4
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 7da99e7e94..a98d138f35 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -2588,6 +2588,7 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
if usesLR {
// caller's LR
*(*unsafe.Pointer)(unsafe.Pointer(sp)) = nil
+ prepGoExitFrame(sp)
spArg += sys.MinFrameSize
}
memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))
diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s
index b334a03038..ba410c51b6 100644
--- a/src/runtime/sys_linux_ppc64x.s
+++ b/src/runtime/sys_linux_ppc64x.s
@@ -209,6 +209,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
MOVD fn+0(FP), R12
MOVD R12, CTR
BL (CTR)
+ MOVD 24(R1), R2
RET
#ifdef GOARCH_ppc64le
@@ -238,6 +239,7 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64
MOVD $runtime·sigtrampgo(SB), R12
MOVD R12, CTR
BL (CTR)
+ MOVD 24(R1), R2
RET
TEXT runtime·mmap(SB),NOSPLIT|NOFRAME,$0
diff --git a/src/runtime/sys_nonppc64x.go b/src/runtime/sys_nonppc64x.go
new file mode 100644
index 0000000000..440937498f
--- /dev/null
+++ b/src/runtime/sys_nonppc64x.go
@@ -0,0 +1,10 @@
+// Copyright 2015 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
+
+package runtime
+
+func prepGoExitFrame(sp uintptr) {
+}
diff --git a/src/runtime/sys_ppc64x.go b/src/runtime/sys_ppc64x.go
index bd182e3a19..2ea1f81ee3 100644
--- a/src/runtime/sys_ppc64x.go
+++ b/src/runtime/sys_ppc64x.go
@@ -35,3 +35,5 @@ func rewindmorestack(buf *gobuf) {
print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n")
throw("runtime: misuse of rewindmorestack")
}
+
+func prepGoExitFrame(sp uintptr)