mirror of
https://github.com/golang/go
synced 2024-11-26 08:48:13 -07:00
runtime: cgo support for Linux/ARM
Part 2 of CL 5601044 (cgo: Linux/ARM support) R=dave, rsc CC=golang-dev https://golang.org/cl/5989057
This commit is contained in:
parent
452a9e452b
commit
5cffce611a
@ -31,6 +31,12 @@ TEXT _rt0_arm(SB),7,$-4
|
|||||||
MOVW R13, g_stackbase(g)
|
MOVW R13, g_stackbase(g)
|
||||||
BL runtime·emptyfunc(SB) // fault if stack check is wrong
|
BL runtime·emptyfunc(SB) // fault if stack check is wrong
|
||||||
|
|
||||||
|
// if there is an initcgo, call it.
|
||||||
|
MOVW initcgo(SB), R2
|
||||||
|
CMP $0, R2
|
||||||
|
MOVW.NE g, R0 // first argument of initcgo is g
|
||||||
|
BL.NE (R2) // will clobber R0-R3
|
||||||
|
|
||||||
BL runtime·check(SB)
|
BL runtime·check(SB)
|
||||||
|
|
||||||
// saved argc, argv
|
// saved argc, argv
|
||||||
@ -86,9 +92,12 @@ TEXT runtime·gosave(SB), 7, $-4
|
|||||||
// restore state from Gobuf; longjmp
|
// restore state from Gobuf; longjmp
|
||||||
TEXT runtime·gogo(SB), 7, $-4
|
TEXT runtime·gogo(SB), 7, $-4
|
||||||
MOVW 0(FP), R1 // gobuf
|
MOVW 0(FP), R1 // gobuf
|
||||||
MOVW 4(FP), R0 // return 2nd arg
|
|
||||||
MOVW gobuf_g(R1), g
|
MOVW gobuf_g(R1), g
|
||||||
MOVW 0(g), R2 // make sure g != nil
|
MOVW 0(g), R2 // make sure g != nil
|
||||||
|
MOVW cgo_save_gm(SB), R2
|
||||||
|
CMP $0, R2 // if in Cgo, we have to save g and m
|
||||||
|
BL.NE (R2) // this call will clobber R0
|
||||||
|
MOVW 4(FP), R0 // return 2nd arg
|
||||||
MOVW gobuf_sp(R1), SP // restore SP
|
MOVW gobuf_sp(R1), SP // restore SP
|
||||||
MOVW gobuf_pc(R1), PC
|
MOVW gobuf_pc(R1), PC
|
||||||
|
|
||||||
@ -97,13 +106,16 @@ TEXT runtime·gogo(SB), 7, $-4
|
|||||||
// (call fn, returning to state in Gobuf)
|
// (call fn, returning to state in Gobuf)
|
||||||
// using frame size $-4 means do not save LR on stack.
|
// using frame size $-4 means do not save LR on stack.
|
||||||
TEXT runtime·gogocall(SB), 7, $-4
|
TEXT runtime·gogocall(SB), 7, $-4
|
||||||
MOVW 0(FP), R0 // gobuf
|
MOVW 0(FP), R3 // gobuf
|
||||||
MOVW 4(FP), R1 // fn
|
MOVW 4(FP), R1 // fn
|
||||||
MOVW 8(FP), R2 // fp offset
|
MOVW 8(FP), R2 // fp offset
|
||||||
MOVW gobuf_g(R0), g
|
MOVW gobuf_g(R3), g
|
||||||
MOVW 0(g), R3 // make sure g != nil
|
MOVW 0(g), R0 // make sure g != nil
|
||||||
MOVW gobuf_sp(R0), SP // restore SP
|
MOVW cgo_save_gm(SB), R0
|
||||||
MOVW gobuf_pc(R0), LR
|
CMP $0, R0 // if in Cgo, we have to save g and m
|
||||||
|
BL.NE (R0) // this call will clobber R0
|
||||||
|
MOVW gobuf_sp(R3), SP // restore SP
|
||||||
|
MOVW gobuf_pc(R3), LR
|
||||||
MOVW R1, PC
|
MOVW R1, PC
|
||||||
|
|
||||||
// void mcall(void (*fn)(G*))
|
// void mcall(void (*fn)(G*))
|
||||||
@ -224,11 +236,114 @@ TEXT runtime·jmpdefer(SB), 7, $0
|
|||||||
MOVW $-4(SP), SP // SP is 4 below argp, due to saved LR
|
MOVW $-4(SP), SP // SP is 4 below argp, due to saved LR
|
||||||
B (R0)
|
B (R0)
|
||||||
|
|
||||||
TEXT runtime·asmcgocall(SB),7,$0
|
// Dummy function to use in saved gobuf.PC,
|
||||||
B runtime·cgounimpl(SB)
|
// to match SP pointing at a return address.
|
||||||
|
// The gobuf.PC is unused by the contortions here
|
||||||
|
// but setting it to return will make the traceback code work.
|
||||||
|
TEXT return<>(SB),7,$0
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT runtime·cgocallback(SB),7,$0
|
// asmcgocall(void(*fn)(void*), void *arg)
|
||||||
B runtime·cgounimpl(SB)
|
// Call fn(arg) on the scheduler stack,
|
||||||
|
// aligned appropriately for the gcc ABI.
|
||||||
|
// See cgocall.c for more details.
|
||||||
|
TEXT runtime·asmcgocall(SB),7,$0
|
||||||
|
MOVW fn+0(FP), R1
|
||||||
|
MOVW arg+4(FP), R0
|
||||||
|
MOVW R13, R2
|
||||||
|
MOVW 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.
|
||||||
|
MOVW m_g0(m), R3
|
||||||
|
CMP R3, g
|
||||||
|
BEQ 7(PC)
|
||||||
|
MOVW R13, (g_sched + gobuf_sp)(g)
|
||||||
|
MOVW $return<>(SB), R4
|
||||||
|
MOVW R4, (g_sched+gobuf_pc)(g)
|
||||||
|
MOVW g, (g_sched+gobuf_g)(g)
|
||||||
|
MOVW R3, g
|
||||||
|
MOVW (g_sched+gobuf_sp)(g), R13
|
||||||
|
|
||||||
|
// Now on a scheduling stack (a pthread-created stack).
|
||||||
|
SUB $24, R13
|
||||||
|
BIC $0x7, R13 // alignment for gcc ABI
|
||||||
|
MOVW R5, 20(R13) // save old g
|
||||||
|
MOVW R2, 16(R13) // save old SP
|
||||||
|
// R0 already contains the first argument
|
||||||
|
BL (R1)
|
||||||
|
|
||||||
|
// Restore registers, g, stack pointer.
|
||||||
|
MOVW 20(R13), g
|
||||||
|
MOVW 16(R13), R13
|
||||||
|
RET
|
||||||
|
|
||||||
|
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
|
||||||
|
// See cgocall.c for more details.
|
||||||
|
TEXT runtime·cgocallback(SB),7,$16
|
||||||
|
MOVW fn+0(FP), R0
|
||||||
|
MOVW frame+4(FP), R1
|
||||||
|
MOVW framesize+8(FP), R2
|
||||||
|
|
||||||
|
// Save current m->g0->sched.sp on stack and then set it to SP.
|
||||||
|
MOVW m_g0(m), R3
|
||||||
|
MOVW (g_sched+gobuf_sp)(R3), R4
|
||||||
|
MOVW.W R4, -4(SP)
|
||||||
|
MOVW R13, (g_sched+gobuf_sp)(R3)
|
||||||
|
|
||||||
|
// Switch to m->curg stack and call runtime.cgocallbackg
|
||||||
|
// with the three arguments. 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->gobuf)
|
||||||
|
// so that we can restore it when we're done.
|
||||||
|
// We can restore m->curg->gobuf.sp easily, because calling
|
||||||
|
// runtime.cgocallbackg leaves SP unchanged upon return.
|
||||||
|
// To save m->curg->gobuf.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 we defined cgocallbackg to have
|
||||||
|
// a frame size of 16, the same amount that we use below),
|
||||||
|
// so that the traceback will seamlessly trace back into
|
||||||
|
// the earlier calls.
|
||||||
|
MOVW m_curg(m), g
|
||||||
|
MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
|
||||||
|
|
||||||
|
// Push gobuf.pc
|
||||||
|
MOVW (g_sched+gobuf_pc)(g), R5
|
||||||
|
SUB $4, R4
|
||||||
|
MOVW R5, 0(R4)
|
||||||
|
|
||||||
|
// Push arguments to cgocallbackg.
|
||||||
|
// Frame size here must match the frame size above
|
||||||
|
// to trick traceback routines into doing the right thing.
|
||||||
|
SUB $16, R4
|
||||||
|
MOVW R0, 4(R4)
|
||||||
|
MOVW R1, 8(R4)
|
||||||
|
MOVW R2, 12(R4)
|
||||||
|
|
||||||
|
// Switch stack and make the call.
|
||||||
|
MOVW R4, R13
|
||||||
|
BL runtime·cgocallbackg(SB)
|
||||||
|
|
||||||
|
// Restore g->gobuf (== m->curg->gobuf) from saved values.
|
||||||
|
MOVW 16(R13), R5
|
||||||
|
MOVW R5, (g_sched+gobuf_pc)(g)
|
||||||
|
ADD $(16+4), R13 // SP clobbered! It is ok!
|
||||||
|
MOVW R13, (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.)
|
||||||
|
MOVW m_g0(m), g
|
||||||
|
MOVW (g_sched+gobuf_sp)(g), R13
|
||||||
|
// POP R6
|
||||||
|
MOVW 0(R13), R6
|
||||||
|
ADD $4, R13
|
||||||
|
MOVW R6, (g_sched+gobuf_sp)(g)
|
||||||
|
|
||||||
|
// Done!
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT runtime·memclr(SB),7,$20
|
TEXT runtime·memclr(SB),7,$20
|
||||||
MOVW 0(FP), R0
|
MOVW 0(FP), R0
|
||||||
|
@ -1 +1,57 @@
|
|||||||
/* unimplemented */
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_arm2(void (*fn)(void), void *g, void *m)
|
||||||
|
*
|
||||||
|
* Calling into the 5c tool chain, where all registers are caller save.
|
||||||
|
* Called from standard ARM EABI, where r4-r11 are callee-save, so they
|
||||||
|
* must be saved explicitly.
|
||||||
|
*/
|
||||||
|
.globl EXT(crosscall_arm2)
|
||||||
|
EXT(crosscall_arm2):
|
||||||
|
push {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr}
|
||||||
|
mov r10, r1 // g
|
||||||
|
mov r9, r2 // m
|
||||||
|
mov r3, r0 // save r0, cgo_tls_set_gm will clobber it
|
||||||
|
bl EXT(cgo_tls_set_gm) // save current g and m into TLS variable
|
||||||
|
mov lr, pc
|
||||||
|
mov pc, r3
|
||||||
|
pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* void crosscall2(void (*fn)(void*, int32), void*, int32)
|
||||||
|
*
|
||||||
|
* Save registers and call fn with two arguments.
|
||||||
|
*/
|
||||||
|
.globl EXT(crosscall2)
|
||||||
|
EXT(crosscall2):
|
||||||
|
/*
|
||||||
|
* We still need to save all callee save register as before, and then
|
||||||
|
* push 2 args for fn (R1 and R2).
|
||||||
|
* Also note that at procedure entry in 5c/5g world, 4(R13) will be the
|
||||||
|
* first arg, so we must push another dummy reg (R0) for 0(R13).
|
||||||
|
* Additionally, cgo_tls_set_gm will clobber R0, so we need to save R0
|
||||||
|
* nevertheless.
|
||||||
|
*/
|
||||||
|
push {r0, r1, r2, r4, r5, r6, r7, r8, r9, r10, r11, ip, lr}
|
||||||
|
bl EXT(cgo_tls_get_gm) // set up g and m from TLS
|
||||||
|
mov lr, pc
|
||||||
|
ldr pc, [sp, #0]
|
||||||
|
pop {r0, r1, r2, r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}
|
||||||
|
|
||||||
|
.globl EXT(__stack_chk_fail_local)
|
||||||
|
EXT(__stack_chk_fail_local):
|
||||||
|
1:
|
||||||
|
b 1b
|
||||||
|
|
||||||
|
@ -2,12 +2,64 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
#include "libcgo.h"
|
#include "libcgo.h"
|
||||||
|
|
||||||
|
static void *threadentry(void*);
|
||||||
|
|
||||||
|
// We have to resort to TLS variable to save g(R10) and
|
||||||
|
// m(R9). One reason is that external code might trigger
|
||||||
|
// SIGSEGV, and our runtime.sigtramp don't even know we
|
||||||
|
// are in external code, and will continue to use R10/R9,
|
||||||
|
// this might as well result in another SIGSEGV.
|
||||||
|
// Note: all three functions will clobber R0, and the last
|
||||||
|
// two can be called from 5c ABI code.
|
||||||
|
void __aeabi_read_tp(void) __attribute__((naked));
|
||||||
|
void cgo_tls_set_gm(void) __attribute__((naked));
|
||||||
|
void cgo_tls_get_gm(void) __attribute__((naked));
|
||||||
|
void __aeabi_read_tp(void) {
|
||||||
|
// b __kuser_get_tls @ 0xffff0fe0
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"mvn r0, #0xf000\n\t"
|
||||||
|
"sub pc, r0, #31\n\t"
|
||||||
|
"nop\n\tnop\n\t"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// g (R10) at 8(TP), m (R9) at 12(TP)
|
||||||
|
void cgo_tls_get_gm(void) {
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"push {lr}\n\t"
|
||||||
|
"bl __aeabi_read_tp\n\t"
|
||||||
|
"ldr r10, [r0, #8]\n\t"
|
||||||
|
"ldr r9, [r0, #12]\n\t"
|
||||||
|
"pop {pc}\n\t"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
void cgo_tls_set_gm(void) {
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"push {lr}\n\t"
|
||||||
|
"bl __aeabi_read_tp\n\t"
|
||||||
|
"str r10, [r0, #8]\n\t"
|
||||||
|
"str r9, [r0, #12]\n\t"
|
||||||
|
"pop {pc}\n\t"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// both cgo_tls_{get,set}_gm can be called from runtime
|
||||||
|
void (*cgo_load_gm)(void) = cgo_tls_get_gm;
|
||||||
|
void (*cgo_save_gm)(void) = cgo_tls_set_gm;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xinitcgo(G *g)
|
xinitcgo(G *g)
|
||||||
{
|
{
|
||||||
// unimplemented
|
pthread_attr_t attr;
|
||||||
|
size_t size;
|
||||||
|
cgo_tls_set_gm(); // save g and m for the initial thread
|
||||||
|
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_getstacksize(&attr, &size);
|
||||||
|
g->stackguard = (uintptr)&attr - size + 4096;
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void (*initcgo)(G*) = xinitcgo;
|
void (*initcgo)(G*) = xinitcgo;
|
||||||
@ -15,6 +67,43 @@ void (*initcgo)(G*) = xinitcgo;
|
|||||||
void
|
void
|
||||||
libcgo_sys_thread_start(ThreadStart *ts)
|
libcgo_sys_thread_start(ThreadStart *ts)
|
||||||
{
|
{
|
||||||
// unimplemented
|
pthread_attr_t attr;
|
||||||
*(int*)0 = 0;
|
pthread_t p;
|
||||||
|
size_t size;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
// Not sure why the memset is necessary here,
|
||||||
|
// but without it, we get a bogus stack size
|
||||||
|
// out of pthread_attr_getstacksize. C'est la Linux.
|
||||||
|
memset(&attr, 0, sizeof attr);
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
size = 0;
|
||||||
|
pthread_attr_getstacksize(&attr, &size);
|
||||||
|
ts->g->stackguard = size;
|
||||||
|
err = pthread_create(&p, &attr, threadentry, ts);
|
||||||
|
if (err != 0) {
|
||||||
|
fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void crosscall_arm2(void (*fn)(void), void *g, void *m);
|
||||||
|
static void*
|
||||||
|
threadentry(void *v)
|
||||||
|
{
|
||||||
|
ThreadStart ts;
|
||||||
|
|
||||||
|
ts = *(ThreadStart*)v;
|
||||||
|
free(v);
|
||||||
|
|
||||||
|
ts.g->stackbase = (uintptr)&ts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libcgo_sys_thread_start set stackguard to stack size;
|
||||||
|
* change to actual guard pointer.
|
||||||
|
*/
|
||||||
|
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2;
|
||||||
|
|
||||||
|
crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m);
|
||||||
|
return nil;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,11 @@
|
|||||||
|
|
||||||
void *initcgo; /* filled in by dynamic linker when Cgo is available */
|
void *initcgo; /* filled in by dynamic linker when Cgo is available */
|
||||||
|
|
||||||
|
// These two are only used by the architecture where TLS based storage isn't
|
||||||
|
// the default for g and m (e.g., ARM)
|
||||||
|
void *cgo_load_gm; /* filled in by dynamic linker when Cgo is available */
|
||||||
|
void *cgo_save_gm; /* filled in by dynamic linker when Cgo is available */
|
||||||
|
|
||||||
static void unlockm(void);
|
static void unlockm(void);
|
||||||
static void unwindm(void);
|
static void unwindm(void);
|
||||||
|
|
||||||
@ -229,6 +234,7 @@ unwindm(void)
|
|||||||
runtime·throw("runtime: unwindm not implemented");
|
runtime·throw("runtime: unwindm not implemented");
|
||||||
case '8':
|
case '8':
|
||||||
case '6':
|
case '6':
|
||||||
|
case '5':
|
||||||
m->g0->sched.sp = *(void**)m->g0->sched.sp;
|
m->g0->sched.sp = *(void**)m->g0->sched.sp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,9 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
|
|||||||
// old link register is more useful in the stack trace.
|
// old link register is more useful in the stack trace.
|
||||||
if(r->arm_pc != 0)
|
if(r->arm_pc != 0)
|
||||||
r->arm_lr = r->arm_pc;
|
r->arm_lr = r->arm_pc;
|
||||||
|
// In case we are panicking from external C code
|
||||||
|
r->arm_r10 = (uintptr)gp;
|
||||||
|
r->arm_r9 = (uintptr)m;
|
||||||
r->arm_pc = (uintptr)runtime·sigpanic;
|
r->arm_pc = (uintptr)runtime·sigpanic;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -293,6 +293,14 @@ TEXT runtime·sigaltstack(SB),7,$0
|
|||||||
RET
|
RET
|
||||||
|
|
||||||
TEXT runtime·sigtramp(SB),7,$24
|
TEXT runtime·sigtramp(SB),7,$24
|
||||||
|
// this might be called in external code context,
|
||||||
|
// where g and m are not set.
|
||||||
|
// first save R0, becuase cgo_load_gm will clobber it
|
||||||
|
MOVW R0, 4(R13)
|
||||||
|
MOVW cgo_load_gm(SB), R0
|
||||||
|
CMP $0, R0
|
||||||
|
BL.NE (R0)
|
||||||
|
|
||||||
// save g
|
// save g
|
||||||
MOVW g, R3
|
MOVW g, R3
|
||||||
MOVW g, 20(R13)
|
MOVW g, 20(R13)
|
||||||
@ -301,7 +309,7 @@ TEXT runtime·sigtramp(SB),7,$24
|
|||||||
MOVW m_gsignal(m), g
|
MOVW m_gsignal(m), g
|
||||||
|
|
||||||
// copy arguments for call to sighandler
|
// copy arguments for call to sighandler
|
||||||
MOVW R0, 4(R13)
|
// R0 is already saved above
|
||||||
MOVW R1, 8(R13)
|
MOVW R1, 8(R13)
|
||||||
MOVW R2, 12(R13)
|
MOVW R2, 12(R13)
|
||||||
MOVW R3, 16(R13)
|
MOVW R3, 16(R13)
|
||||||
|
Loading…
Reference in New Issue
Block a user