Commit 38dc177d authored by Clément Chigot's avatar Clément Chigot Committed by Ian Lance Taylor

runtime: create library startup for aix/ppc64

As .init_array section aren't available on AIX, the Go runtime
initialization is made with gcc constructor attribute.
However, as cgo tool is building a binary in order to get imported
C symbols, Go symbols imported for this initilization must be ignored.
-Wl,-berok is mandatory otherwize ld will fail to create this binary,
_rt0_aix_ppc64_lib and runtime_rt0_go aren't defined in runtime/cgo.
These two symbols must also be ignored when creating _cgo_import.go.

Change-Id: Icf2e0282f5b50de5fa82007439a428e6147efef1
Reviewed-on: https://go-review.googlesource.com/c/go/+/169118
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent 53c9c068
......@@ -336,6 +336,12 @@ func dynimport(obj string) {
fatalf("cannot load imported symbols from XCOFF file %s: %v", obj, err)
}
for _, s := range sym {
if s.Name == "runtime_rt0_go" || s.Name == "_rt0_ppc64_aix_lib" {
// These symbols are imported by runtime/cgo but
// must not be added to _cgo_import.go as there are
// Go symbols.
continue
}
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, s.Name, s.Library)
}
lib, err := f.ImportedLibraries()
......
......@@ -171,6 +171,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{
re(`-Wl,--(no-)?allow-shlib-undefined`),
re(`-Wl,--(no-)?as-needed`),
re(`-Wl,-Bdynamic`),
re(`-Wl,-berok`),
re(`-Wl,-Bstatic`),
re(`-WL,-O([^@,\-][^,]*)?`),
re(`-Wl,-d[ny]`),
......
......@@ -8,3 +8,4 @@ package cgo
// longcall on cgo programs (cf gcc_aix_ppc64.c).
//go:cgo_export_static __cgo_topofstack
//go:cgo_export_static runtime.rt0_go
//go:cgo_export_static _rt0_ppc64_aix_lib
......@@ -20,6 +20,7 @@ package cgo
#cgo !android,linux LDFLAGS: -lpthread
#cgo netbsd LDFLAGS: -lpthread
#cgo openbsd LDFLAGS: -lpthread
#cgo aix LDFLAGS: -Wl,-berok
#cgo CFLAGS: -Wall -Werror
......
......@@ -12,6 +12,7 @@
*/
extern int __attribute__((longcall)) __cgo_topofstack(void);
extern int __attribute__((longcall)) runtime_rt0_go(int argc, char **argv);
extern void __attribute__((longcall)) _rt0_ppc64_aix_lib(void);
int _cgo_topofstack(void) {
return __cgo_topofstack();
......@@ -20,3 +21,18 @@ int _cgo_topofstack(void) {
int main(int argc, char **argv) {
return runtime_rt0_go(argc, argv);
}
static void libinit(void) __attribute__ ((constructor));
/*
* libinit aims to replace .init_array section which isn't available on aix.
* Using __attribute__ ((constructor)) let gcc handles this instead of
* adding special code in cmd/link.
* However, it will be called for every Go programs which has cgo.
* Inside _rt0_ppc64_aix_lib(), runtime.isarchive is checked in order
* to know if this program is a c-archive or a simple cgo program.
* If it's not set, _rt0_ppc64_ax_lib() returns directly.
*/
static void libinit() {
_rt0_ppc64_aix_lib();
}
......@@ -363,15 +363,34 @@ func syscall6(fn *libFunc, a0, a1, a2, a3, a4, a5 uintptr) (r, err uintptr) {
return c.r1, c.err
}
func exit1(code int32)
//go:nosplit
func exit(code int32) {
syscall1(&libc_exit, uintptr(code))
_g_ := getg()
// Check the validity of g because without a g during
// newosproc0.
if _g_ != nil {
syscall1(&libc_exit, uintptr(code))
return
}
exit1(code)
}
func write1(fd, p uintptr, n int32) int32
//go:nosplit
func write(fd uintptr, p unsafe.Pointer, n int32) int32 {
r, _ := syscall3(&libc_write, uintptr(fd), uintptr(p), uintptr(n))
return int32(r)
_g_ := getg()
// Check the validity of g because without a g during
// newosproc0.
if _g_ != nil {
r, _ := syscall3(&libc_write, uintptr(fd), uintptr(p), uintptr(n))
return int32(r)
}
return write1(fd, uintptr(p), n)
}
......@@ -428,13 +447,24 @@ func madvise(addr unsafe.Pointer, n uintptr, flags int32) {
}
}
func sigaction1(sig, new, old uintptr)
//go:nosplit
func sigaction(sig uintptr, new, old *sigactiont) {
r, err := syscall3(&libc_sigaction, sig, uintptr(unsafe.Pointer(new)), uintptr(unsafe.Pointer(old)))
if int32(r) == -1 {
println("Sigaction failed for sig: ", sig, " with error:", hex(err))
throw("syscall sigaction")
_g_ := getg()
// Check the validity of g because without a g during
// runtime.libpreinit.
if _g_ != nil {
r, err := syscall3(&libc_sigaction, sig, uintptr(unsafe.Pointer(new)), uintptr(unsafe.Pointer(old)))
if int32(r) == -1 {
println("Sigaction failed for sig: ", sig, " with error:", hex(err))
throw("syscall sigaction")
}
return
}
sigaction1(sig, uintptr(unsafe.Pointer(new)), uintptr(unsafe.Pointer(old)))
}
//go:nosplit
......@@ -574,16 +604,36 @@ func pthread_attr_destroy(attr *pthread_attr) int32 {
return int32(r)
}
func pthread_attr_init1(attr uintptr) int32
//go:nosplit
func pthread_attr_init(attr *pthread_attr) int32 {
r, _ := syscall1(&libpthread_attr_init, uintptr(unsafe.Pointer(attr)))
return int32(r)
_g_ := getg()
// Check the validity of g because without a g during
// newosproc0.
if _g_ != nil {
r, _ := syscall1(&libpthread_attr_init, uintptr(unsafe.Pointer(attr)))
return int32(r)
}
return pthread_attr_init1(uintptr(unsafe.Pointer(attr)))
}
func pthread_attr_setdetachstate1(attr uintptr, state int32) int32
//go:nosplit
func pthread_attr_setdetachstate(attr *pthread_attr, state int32) int32 {
r, _ := syscall2(&libpthread_attr_setdetachstate, uintptr(unsafe.Pointer(attr)), uintptr(state))
return int32(r)
_g_ := getg()
// Check the validity of g because without a g during
// newosproc0.
if _g_ != nil {
r, _ := syscall2(&libpthread_attr_setdetachstate, uintptr(unsafe.Pointer(attr)), uintptr(state))
return int32(r)
}
return pthread_attr_setdetachstate1(uintptr(unsafe.Pointer(attr)), state)
}
//go:nosplit
......@@ -598,16 +648,36 @@ func pthread_attr_getstacksize(attr *pthread_attr, size *uint64) int32 {
return int32(r)
}
func pthread_attr_setstacksize1(attr uintptr, size uint64) int32
//go:nosplit
func pthread_attr_setstacksize(attr *pthread_attr, size uint64) int32 {
r, _ := syscall2(&libpthread_attr_setstacksize, uintptr(unsafe.Pointer(attr)), uintptr(size))
return int32(r)
_g_ := getg()
// Check the validity of g because without a g during
// newosproc0.
if _g_ != nil {
r, _ := syscall2(&libpthread_attr_setstacksize, uintptr(unsafe.Pointer(attr)), uintptr(size))
return int32(r)
}
return pthread_attr_setstacksize1(uintptr(unsafe.Pointer(attr)), size)
}
func pthread_create1(tid, attr, fn, arg uintptr) int32
//go:nosplit
func pthread_create(tid *pthread, attr *pthread_attr, fn *funcDescriptor, arg unsafe.Pointer) int32 {
r, _ := syscall4(&libpthread_create, uintptr(unsafe.Pointer(tid)), uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(fn)), uintptr(arg))
return int32(r)
_g_ := getg()
// Check the validity of g because without a g during
// newosproc0.
if _g_ != nil {
r, _ := syscall4(&libpthread_create, uintptr(unsafe.Pointer(tid)), uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(fn)), uintptr(arg))
return int32(r)
}
return pthread_create1(uintptr(unsafe.Pointer(tid)), uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(fn)), uintptr(arg))
}
// On multi-thread program, sigprocmask must not be called.
......
......@@ -97,6 +97,66 @@ func osinit() {
setupSystemConf()
}
// newosproc0 is a version of newosproc that can be called before the runtime
// is initialized.
//
// This function is not safe to use after initialization as it does not pass an M as fnarg.
//
//go:nosplit
func newosproc0(stacksize uintptr, fn *funcDescriptor) {
var (
attr pthread_attr
oset sigset
tid pthread
)
if pthread_attr_init(&attr) != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
exit(1)
}
if pthread_attr_setstacksize(&attr, threadStackSize) != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
exit(1)
}
if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
exit(1)
}
// Disable signals during create, so that the new thread starts
// with signals disabled. It will enable them in minit.
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
var ret int32
for tries := 0; tries < 20; tries++ {
// pthread_create can fail with EAGAIN for no reasons
// but it will be ok if it retries.
ret = pthread_create(&tid, &attr, fn, nil)
if ret != _EAGAIN {
break
}
usleep(uint32(tries+1) * 1000) // Milliseconds.
}
sigprocmask(_SIG_SETMASK, &oset, nil)
if ret != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
exit(1)
}
}
var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
// Called to do synchronous initialization of Go code built with
// -buildmode=c-archive or -buildmode=c-shared.
// None of the Go runtime is initialized.
//go:nosplit
//go:nowritebarrierrec
func libpreinit() {
initsig(true)
}
// Ms related functions
func mpreinit(mp *m) {
mp.gsignal = malg(32 * 1024) // AIX wants >= 8K
......@@ -213,7 +273,13 @@ func setsig(i uint32, fn uintptr) {
//go:nosplit
//go:nowritebarrierrec
func setsigstack(i uint32) {
throw("Not yet implemented\n")
var sa sigactiont
sigaction(uintptr(i), nil, &sa)
if sa.sa_flags&_SA_ONSTACK != 0 {
return
}
sa.sa_flags |= _SA_ONSTACK
sigaction(uintptr(i), &sa, nil)
}
//go:nosplit
......
......@@ -47,3 +47,153 @@ TEXT _main(SB),NOSPLIT,$-8
MOVD R12, CTR
BR (CTR)
TEXT _rt0_ppc64_aix_lib(SB),NOSPLIT,$-8
// Start with standard C stack frame layout and linkage.
MOVD LR, R0
MOVD R0, 16(R1) // Save LR in caller's frame.
MOVW CR, R0 // Save CR in caller's frame
MOVD R0, 8(R1)
MOVDU R1, -344(R1) // Allocate frame.
// Preserve callee-save registers.
MOVD R14, 48(R1)
MOVD R15, 56(R1)
MOVD R16, 64(R1)
MOVD R17, 72(R1)
MOVD R18, 80(R1)
MOVD R19, 88(R1)
MOVD R20, 96(R1)
MOVD R21,104(R1)
MOVD R22, 112(R1)
MOVD R23, 120(R1)
MOVD R24, 128(R1)
MOVD R25, 136(R1)
MOVD R26, 144(R1)
MOVD R27, 152(R1)
MOVD R28, 160(R1)
MOVD R29, 168(R1)
MOVD g, 176(R1) // R30
MOVD R31, 184(R1)
FMOVD F14, 192(R1)
FMOVD F15, 200(R1)
FMOVD F16, 208(R1)
FMOVD F17, 216(R1)
FMOVD F18, 224(R1)
FMOVD F19, 232(R1)
FMOVD F20, 240(R1)
FMOVD F21, 248(R1)
FMOVD F22, 256(R1)
FMOVD F23, 264(R1)
FMOVD F24, 272(R1)
FMOVD F25, 280(R1)
FMOVD F26, 288(R1)
FMOVD F27, 296(R1)
FMOVD F28, 304(R1)
FMOVD F29, 312(R1)
FMOVD F30, 320(R1)
FMOVD F31, 328(R1)
// Synchronous initialization.
MOVD $runtime·reginit(SB), R12
MOVD R12, CTR
BL (CTR)
MOVBZ runtime·isarchive(SB), R3 // Check buildmode = c-archive
CMP $0, R3
BEQ done
MOVD R14, _rt0_ppc64_aix_lib_argc<>(SB)
MOVD R15, _rt0_ppc64_aix_lib_argv<>(SB)
MOVD $runtime·libpreinit(SB), R12
MOVD R12, CTR
BL (CTR)
// Create a new thread to do the runtime initialization and return.
MOVD _cgo_sys_thread_create(SB), R12
CMP $0, R12
BEQ nocgo
MOVD $_rt0_ppc64_aix_lib_go(SB), R3
MOVD $0, R4
MOVD R2, 40(R1)
MOVD 8(R12), R2
MOVD (R12), R12
MOVD R12, CTR
BL (CTR)
MOVD 40(R1), R2
BR done
nocgo:
MOVD $0x800000, R12 // stacksize = 8192KB
MOVD R12, 8(R1)
MOVD $_rt0_ppc64_aix_lib_go(SB), R12
MOVD R12, 16(R1)
MOVD $runtime·newosproc0(SB),R12
MOVD R12, CTR
BL (CTR)
done:
// Restore saved registers.
MOVD 48(R1), R14
MOVD 56(R1), R15
MOVD 64(R1), R16
MOVD 72(R1), R17
MOVD 80(R1), R18
MOVD 88(R1), R19
MOVD 96(R1), R20
MOVD 104(R1), R21
MOVD 112(R1), R22
MOVD 120(R1), R23
MOVD 128(R1), R24
MOVD 136(R1), R25
MOVD 144(R1), R26
MOVD 152(R1), R27
MOVD 160(R1), R28
MOVD 168(R1), R29
MOVD 176(R1), g // R30
MOVD 184(R1), R31
FMOVD 196(R1), F14
FMOVD 200(R1), F15
FMOVD 208(R1), F16
FMOVD 216(R1), F17
FMOVD 224(R1), F18
FMOVD 232(R1), F19
FMOVD 240(R1), F20
FMOVD 248(R1), F21
FMOVD 256(R1), F22
FMOVD 264(R1), F23
FMOVD 272(R1), F24
FMOVD 280(R1), F25
FMOVD 288(R1), F26
FMOVD 296(R1), F27
FMOVD 304(R1), F28
FMOVD 312(R1), F29
FMOVD 320(R1), F30
FMOVD 328(R1), F31
ADD $344, R1
MOVD 8(R1), R0
MOVFL R0, $0xff
MOVD 16(R1), R0
MOVD R0, LR
RET
DATA _rt0_ppc64_aix_lib_go+0(SB)/8, $__rt0_ppc64_aix_lib_go(SB)
DATA _rt0_ppc64_aix_lib_go+8(SB)/8, $TOC(SB)
DATA _rt0_ppc64_aix_lib_go+16(SB)/8, $0
GLOBL _rt0_ppc64_aix_lib_go(SB), NOPTR, $24
TEXT __rt0_ppc64_aix_lib_go(SB),NOSPLIT,$0
MOVD _rt0_ppc64_aix_lib_argc<>(SB), R3
MOVD _rt0_ppc64_aix_lib_argv<>(SB), R4
MOVD $runtime·rt0_go(SB), R12
MOVD R12, CTR
BR (CTR)
DATA _rt0_ppc64_aix_lib_argc<>(SB)/8, $0
GLOBL _rt0_ppc64_aix_lib_argc<>(SB),NOPTR, $8
DATA _rt0_ppc64_aix_lib_argv<>(SB)/8, $0
GLOBL _rt0_ppc64_aix_lib_argv<>(SB),NOPTR, $8
......@@ -445,6 +445,9 @@ func moduledataverify1(datap *moduledata) {
for j := 0; j <= i; j++ {
print("\t", hex(datap.ftab[j].entry), " ", funcname(funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff])), datap}), "\n")
}
if GOOS == "aix" && isarchive {
println("-Wl,-bnoobjreorder is mandatory on aix/ppc64 with c-archive")
}
throw("invalid runtime symbol table")
}
}
......
......@@ -216,17 +216,22 @@ TEXT runtime·_tstart(SB),NOSPLIT,$0
MOVD R0, R3
RET
#define CSYSCALL() \
MOVD 0(R12), R12 \
MOVD R2, 40(R1) \
MOVD 0(R12), R0 \
MOVD 8(R12), R2 \
MOVD R0, CTR \
BL (CTR) \
MOVD 40(R1), R2 \
BL runtime·reginit(SB)
// Runs on OS stack, called from runtime·osyield.
TEXT runtime·osyield1(SB),NOSPLIT,$0
MOVD $libc_sched_yield(SB), R12
MOVD 0(R12), R12
MOVD R2, 40(R1)
MOVD 0(R12), R0
MOVD 8(R12), R2
MOVD R0, CTR
BL (CTR)
MOVD 40(R1), R2
BL runtime·reginit(SB)
CSYSCALL()
RET
......@@ -236,26 +241,75 @@ TEXT runtime·sigprocmask1(SB),NOSPLIT,$0-24
MOVD new+8(FP), R4
MOVD old+16(FP), R5
MOVD $libpthread_sigthreadmask(SB), R12
MOVD 0(R12), R12
MOVD R2, 40(R1)
MOVD 0(R12), R0
MOVD 8(R12), R2
MOVD R0, CTR
BL (CTR)
MOVD 40(R1), R2
BL runtime·reginit(SB)
CSYSCALL()
RET
// Runs on OS stack, called from runtime·usleep.
TEXT runtime·usleep1(SB),NOSPLIT,$0-8
TEXT runtime·usleep1(SB),NOSPLIT,$0-4
MOVW us+0(FP), R3
MOVD $libc_usleep(SB), R12
MOVD 0(R12), R12
MOVD R2, 40(R1)
MOVD 0(R12), R0
MOVD 8(R12), R2
MOVD R0, CTR
BL (CTR)
MOVD 40(R1), R2
BL runtime·reginit(SB)
CSYSCALL()
RET
// Runs on OS stack, called from runtime·exit.
TEXT runtime·exit1(SB),NOSPLIT,$0-4
MOVW code+0(FP), R3
MOVD $libc_exit(SB), R12
CSYSCALL()
RET
// Runs on OS stack, called from runtime·write.
TEXT runtime·write1(SB),NOSPLIT,$0-28
MOVD fd+0(FP), R3
MOVD p+8(FP), R4
MOVW n+16(FP), R5
MOVD $libc_write(SB), R12
CSYSCALL()
MOVW R3, ret+24(FP)
RET
// Runs on OS stack, called from runtime·pthread_attr_init.
TEXT runtime·pthread_attr_init1(SB),NOSPLIT,$0-12
MOVD attr+0(FP), R3
MOVD $libpthread_attr_init(SB), R12
CSYSCALL()
MOVW R3, ret+8(FP)
RET
// Runs on OS stack, called from runtime·pthread_attr_setstacksize.
TEXT runtime·pthread_attr_setstacksize1(SB),NOSPLIT,$0-20
MOVD attr+0(FP), R3
MOVD size+8(FP), R4
MOVD $libpthread_attr_setstacksize(SB), R12
CSYSCALL()
MOVW R3, ret+16(FP)
RET
// Runs on OS stack, called from runtime·pthread_setdetachstate.
TEXT runtime·pthread_attr_setdetachstate1(SB),NOSPLIT,$0-20
MOVD attr+0(FP), R3
MOVW state+8(FP), R4
MOVD $libpthread_attr_setdetachstate(SB), R12
CSYSCALL()
MOVW R3, ret+16(FP)
RET
// Runs on OS stack, called from runtime·pthread_create.
TEXT runtime·pthread_create1(SB),NOSPLIT,$0-36
MOVD tid+0(FP), R3
MOVD attr+8(FP), R4
MOVD fn+16(FP), R5
MOVD arg+24(FP), R6
MOVD $libpthread_create(SB), R12
CSYSCALL()
MOVW R3, ret+32(FP)
RET
// Runs on OS stack, called from runtime·sigaction.
TEXT runtime·sigaction1(SB),NOSPLIT,$0-24
MOVD sig+0(FP), R3
MOVD new+8(FP), R4
MOVD old+16(FP), R5
MOVD $libc_sigaction(SB), R12
CSYSCALL()
RET
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment