runtime: Support -buildmode=c-shared.

These changes permit using the go tool from the upcoming Go
1.5 release with -buildmode=c-archive to build gccgo code into
an archive file that can be linked with a C program.

From-SVN: r222594
This commit is contained in:
Ian Lance Taylor 2015-04-29 21:31:53 +00:00
parent 2f79da78f3
commit 081564faed
7 changed files with 333 additions and 8 deletions

View file

@ -105,7 +105,7 @@ toolexeclib_LTLIBRARIES = libgo-llgo.la
toolexeclib_LIBRARIES = libgobegin-llgo.a
else
toolexeclib_LTLIBRARIES = libgo.la
toolexeclib_LIBRARIES = libgobegin.a libnetgo.a
toolexeclib_LIBRARIES = libgobegin.a libgolibbegin.a libnetgo.a
endif
toolexeclibgo_DATA = \
@ -2036,6 +2036,11 @@ libgobegin_llgo_a_SOURCES = \
libgobegin_a_CFLAGS = $(AM_CFLAGS) -fPIC
libgobegin_llgo_a_CFLAGS = $(AM_CFLAGS) -fPIC
libgolibbegin_a_SOURCES = \
runtime/go-libmain.c
libgolibbegin_a_CFLAGS = $(AM_CFLAGS) -fPIC
libnetgo_a_SOURCES = $(go_netgo_files)
libnetgo_a_LIBADD = netgo.o
@ -2067,7 +2072,7 @@ BUILDPACKAGE = \
BUILDNETGO = \
$(MKDIR_P) $(@D); \
files=`echo $^ | sed -e 's/[^ ]*\.gox//g'`; \
$(GOCOMPILE) -I . -c -fgo-pkgpath=net -o $@ $$files
$(GOCOMPILE) -I . -c -fPIC -fgo-pkgpath=net -o $@ $$files
GOTESTFLAGS =
GOBENCH =

View file

@ -136,6 +136,10 @@ libgobegin_a_AR = $(AR) $(ARFLAGS)
libgobegin_a_LIBADD =
am_libgobegin_a_OBJECTS = libgobegin_a-go-main.$(OBJEXT)
libgobegin_a_OBJECTS = $(am_libgobegin_a_OBJECTS)
libgolibbegin_a_AR = $(AR) $(ARFLAGS)
libgolibbegin_a_LIBADD =
am_libgolibbegin_a_OBJECTS = libgolibbegin_a-go-libmain.$(OBJEXT)
libgolibbegin_a_OBJECTS = $(am_libgolibbegin_a_OBJECTS)
libnetgo_a_AR = $(AR) $(ARFLAGS)
libnetgo_a_DEPENDENCIES = netgo.o
am__objects_1 =
@ -264,8 +268,8 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@
SOURCES = $(libgobegin_llgo_a_SOURCES) $(libgobegin_a_SOURCES) \
$(libnetgo_a_SOURCES) $(libgo_llgo_la_SOURCES) \
$(libgo_la_SOURCES)
$(libgolibbegin_a_SOURCES) $(libnetgo_a_SOURCES) \
$(libgo_llgo_la_SOURCES) $(libgo_la_SOURCES)
MULTISRCTOP =
MULTIBUILDTOP =
MULTIDIRS =
@ -532,7 +536,7 @@ AM_MAKEFLAGS = \
FLAGS_TO_PASS = $(AM_MAKEFLAGS)
@GOC_IS_LLGO_FALSE@toolexeclib_LTLIBRARIES = libgo.la
@GOC_IS_LLGO_TRUE@toolexeclib_LTLIBRARIES = libgo-llgo.la
@GOC_IS_LLGO_FALSE@toolexeclib_LIBRARIES = libgobegin.a libnetgo.a
@GOC_IS_LLGO_FALSE@toolexeclib_LIBRARIES = libgobegin.a libgolibbegin.a libnetgo.a
@GOC_IS_LLGO_TRUE@toolexeclib_LIBRARIES = libgobegin-llgo.a
toolexeclibgo_DATA = \
bufio.gox \
@ -2102,6 +2106,10 @@ libgobegin_llgo_a_SOURCES = \
# Use -fPIC for libgobegin so that it can be put in a PIE.
libgobegin_a_CFLAGS = $(AM_CFLAGS) -fPIC
libgobegin_llgo_a_CFLAGS = $(AM_CFLAGS) -fPIC
libgolibbegin_a_SOURCES = \
runtime/go-libmain.c
libgolibbegin_a_CFLAGS = $(AM_CFLAGS) -fPIC
libnetgo_a_SOURCES = $(go_netgo_files)
libnetgo_a_LIBADD = netgo.o
LTLDFLAGS = $(shell $(SHELL) $(top_srcdir)/../libtool-ldflags $(LDFLAGS))
@ -2132,7 +2140,7 @@ BUILDPACKAGE = \
BUILDNETGO = \
$(MKDIR_P) $(@D); \
files=`echo $^ | sed -e 's/[^ ]*\.gox//g'`; \
$(GOCOMPILE) -I . -c -fgo-pkgpath=net -o $@ $$files
$(GOCOMPILE) -I . -c -fPIC -fgo-pkgpath=net -o $@ $$files
GOTESTFLAGS =
GOBENCH =
@ -2421,6 +2429,10 @@ libgobegin.a: $(libgobegin_a_OBJECTS) $(libgobegin_a_DEPENDENCIES)
-rm -f libgobegin.a
$(libgobegin_a_AR) libgobegin.a $(libgobegin_a_OBJECTS) $(libgobegin_a_LIBADD)
$(RANLIB) libgobegin.a
libgolibbegin.a: $(libgolibbegin_a_OBJECTS) $(libgolibbegin_a_DEPENDENCIES)
-rm -f libgolibbegin.a
$(libgolibbegin_a_AR) libgolibbegin.a $(libgolibbegin_a_OBJECTS) $(libgolibbegin_a_LIBADD)
$(RANLIB) libgolibbegin.a
libnetgo.a: $(libnetgo_a_OBJECTS) $(libnetgo_a_DEPENDENCIES)
-rm -f libnetgo.a
$(libnetgo_a_AR) libnetgo.a $(libnetgo_a_OBJECTS) $(libnetgo_a_LIBADD)
@ -2546,6 +2558,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lfstack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgobegin_a-go-main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgobegin_llgo_a-go-main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgolibbegin_a-go-libmain.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock_futex.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock_sema.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Plo@am__quote@
@ -2631,6 +2644,20 @@ libgobegin_a-go-main.obj: runtime/go-main.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgobegin_a_CFLAGS) $(CFLAGS) -c -o libgobegin_a-go-main.obj `if test -f 'runtime/go-main.c'; then $(CYGPATH_W) 'runtime/go-main.c'; else $(CYGPATH_W) '$(srcdir)/runtime/go-main.c'; fi`
libgolibbegin_a-go-libmain.o: runtime/go-libmain.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgolibbegin_a_CFLAGS) $(CFLAGS) -MT libgolibbegin_a-go-libmain.o -MD -MP -MF $(DEPDIR)/libgolibbegin_a-go-libmain.Tpo -c -o libgolibbegin_a-go-libmain.o `test -f 'runtime/go-libmain.c' || echo '$(srcdir)/'`runtime/go-libmain.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libgolibbegin_a-go-libmain.Tpo $(DEPDIR)/libgolibbegin_a-go-libmain.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-libmain.c' object='libgolibbegin_a-go-libmain.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgolibbegin_a_CFLAGS) $(CFLAGS) -c -o libgolibbegin_a-go-libmain.o `test -f 'runtime/go-libmain.c' || echo '$(srcdir)/'`runtime/go-libmain.c
libgolibbegin_a-go-libmain.obj: runtime/go-libmain.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgolibbegin_a_CFLAGS) $(CFLAGS) -MT libgolibbegin_a-go-libmain.obj -MD -MP -MF $(DEPDIR)/libgolibbegin_a-go-libmain.Tpo -c -o libgolibbegin_a-go-libmain.obj `if test -f 'runtime/go-libmain.c'; then $(CYGPATH_W) 'runtime/go-libmain.c'; else $(CYGPATH_W) '$(srcdir)/runtime/go-libmain.c'; fi`
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libgolibbegin_a-go-libmain.Tpo $(DEPDIR)/libgolibbegin_a-go-libmain.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-libmain.c' object='libgolibbegin_a-go-libmain.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgolibbegin_a_CFLAGS) $(CFLAGS) -c -o libgolibbegin_a-go-libmain.obj `if test -f 'runtime/go-libmain.c'; then $(CYGPATH_W) 'runtime/go-libmain.c'; else $(CYGPATH_W) '$(srcdir)/runtime/go-libmain.c'; fi`
go-append.lo: runtime/go-append.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-append.lo -MD -MP -MF $(DEPDIR)/go-append.Tpo -c -o go-append.lo `test -f 'runtime/go-append.c' || echo '$(srcdir)/'`runtime/go-append.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-append.Tpo $(DEPDIR)/go-append.Plo

View file

@ -8,6 +8,9 @@
#include "go-alloc.h"
#include "interface.h"
#include "go-panic.h"
#include "go-type.h"
extern void __go_receive (ChanType *, Hchan *, byte *);
/* Prepare to call from code written in Go to code written in C or
C++. This takes the current goroutine out of the Go scheduler, as
@ -86,6 +89,15 @@ syscall_cgocallback ()
runtime_exitsyscall ();
if (runtime_g ()->ncgo == 0)
{
/* The C call to Go came from a thread not currently running any
Go. In the case of -buildmode=c-archive or c-shared, this
call may be coming in before package initialization is
complete. Wait until it is. */
__go_receive (NULL, runtime_main_init_done, NULL);
}
mp = runtime_m ();
if (mp->needextram)
{
@ -177,3 +189,65 @@ _cgo_panic (const char *p)
__go_panic (e);
}
/* Used for _cgo_wait_runtime_init_done. This is based on code in
runtime/cgo/gcc_libinit.c in the master library. */
static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER;
static _Bool runtime_init_done;
/* This is called by exported cgo functions to ensure that the runtime
has been initialized before we enter the function. This is needed
when building with -buildmode=c-archive or similar. */
void
_cgo_wait_runtime_init_done (void)
{
int err;
if (__atomic_load_n (&runtime_init_done, __ATOMIC_ACQUIRE))
return;
err = pthread_mutex_lock (&runtime_init_mu);
if (err != 0)
abort ();
while (!__atomic_load_n (&runtime_init_done, __ATOMIC_ACQUIRE))
{
err = pthread_cond_wait (&runtime_init_cond, &runtime_init_mu);
if (err != 0)
abort ();
}
err = pthread_mutex_unlock (&runtime_init_mu);
if (err != 0)
abort ();
}
/* This is called by runtime_main after the Go runtime is
initialized. */
void
_cgo_notify_runtime_init_done (void)
{
int err;
err = pthread_mutex_lock (&runtime_init_mu);
if (err != 0)
abort ();
__atomic_store_n (&runtime_init_done, 1, __ATOMIC_RELEASE);
err = pthread_cond_broadcast (&runtime_init_cond);
if (err != 0)
abort ();
err = pthread_mutex_unlock (&runtime_init_mu);
if (err != 0)
abort ();
}
// runtime_iscgo is set to true if some cgo code is linked in.
// This is done by a constructor in the cgo generated code.
_Bool runtime_iscgo;
// runtime_cgoHasExtraM is set on startup when an extra M is created
// for cgo. The extra M must be created before any C/C++ code calls
// cgocallback.
_Bool runtime_cgoHasExtraM;

114
libgo/runtime/go-libmain.c Normal file
View file

@ -0,0 +1,114 @@
/* go-libmain.c -- the startup function for a Go library.
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. */
#include "config.h"
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "runtime.h"
#include "go-alloc.h"
#include "array.h"
#include "arch.h"
#include "malloc.h"
/* This is used when building a standalone Go library using the Go
command's -buildmode=c-archive or -buildmode=c-shared option. It
starts up the Go code as a global constructor but does not take any
other action. The main program is written in some other language
and calls exported Go functions as needed. */
static void die (const char *, int);
static void initfn (int, char **, char **);
static void *gostart (void *);
/* Used to pass arguments to the thread that runs the Go startup. */
struct args {
int argc;
char **argv;
};
/* We use .init_array so that we can get the command line arguments.
This obviously assumes .init_array support; different systems may
require other approaches. */
typedef void (*initarrayfn) (int, char **, char **);
static initarrayfn initarray[1]
__attribute__ ((section (".init_array"), used)) =
{ initfn };
/* This function is called at program startup time. It starts a new
thread to do the actual Go startup, so that program startup is not
paused waiting for the Go initialization functions. Exported cgo
functions will wait for initialization to complete if
necessary. */
static void
initfn (int argc, char **argv, char** env __attribute__ ((unused)))
{
int err;
pthread_attr_t attr;
struct args *a;
pthread_t tid;
a = (struct args *) malloc (sizeof *a);
if (a == NULL)
die ("malloc", errno);
a->argc = argc;
a->argv = argv;
err = pthread_attr_init (&attr);
if (err != 0)
die ("pthread_attr_init", err);
err = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
if (err != 0)
die ("pthread_attr_setdetachstate", err);
err = pthread_create (&tid, &attr, gostart, (void *) a);
if (err != 0)
die ("pthread_create", err);
err = pthread_attr_destroy (&attr);
if (err != 0)
die ("pthread_attr_destroy", err);
}
/* Start up the Go runtime. */
static void *
gostart (void *arg)
{
struct args *a = (struct args *) arg;
runtime_isarchive = true;
if (runtime_isstarted)
return NULL;
runtime_isstarted = true;
runtime_check ();
runtime_args (a->argc, (byte **) a->argv);
runtime_osinit ();
runtime_schedinit ();
__go_go (runtime_main, NULL);
runtime_mstart (runtime_m ());
abort ();
}
/* If something goes wrong during program startup, crash. There is no
way to report failure and nobody to whom to report it. */
static void
die (const char *fn, int err)
{
fprintf (stderr, "%s: %d\n", fn, err);
exit (EXIT_FAILURE);
}

View file

@ -35,6 +35,12 @@ extern char **environ;
int
main (int argc, char **argv)
{
runtime_isarchive = false;
if (runtime_isstarted)
return NULL;
runtime_isstarted = true;
runtime_check ();
runtime_args (argc, (byte **) argv);
runtime_osinit ();

View file

@ -372,7 +372,6 @@ enum
Sched runtime_sched;
int32 runtime_gomaxprocs;
uint32 runtime_needextram = 1;
bool runtime_iscgo = true;
M runtime_m0;
G runtime_g0; // idle goroutine for m0
G* runtime_lastg;
@ -389,6 +388,8 @@ G** runtime_allg;
uintptr runtime_allglen;
static uintptr allgcap;
bool runtime_isarchive;
void* runtime_mstart(void*);
static void runqput(P*, G*);
static G* runqget(P*);
@ -428,6 +429,8 @@ static bool preemptall(void);
static bool exitsyscallfast(void);
static void allgadd(G*);
bool runtime_isstarted;
// The bootstrap sequence is:
//
// call osinit
@ -490,6 +493,64 @@ runtime_schedinit(void)
extern void main_init(void) __asm__ (GOSYM_PREFIX "__go_init_main");
extern void main_main(void) __asm__ (GOSYM_PREFIX "main.main");
// Used to determine the field alignment.
struct field_align
{
char c;
Hchan *p;
};
// main_init_done is a signal used by cgocallbackg that initialization
// has been completed. It is made before _cgo_notify_runtime_init_done,
// so all cgo calls can rely on it existing. When main_init is
// complete, it is closed, meaning cgocallbackg can reliably receive
// from it.
Hchan *runtime_main_init_done;
// The chan bool type, for runtime_main_init_done.
extern const struct __go_type_descriptor bool_type_descriptor
__asm__ (GOSYM_PREFIX "__go_tdn_bool");
static struct __go_channel_type chan_bool_type_descriptor =
{
/* __common */
{
/* __code */
GO_CHAN,
/* __align */
__alignof (Hchan *),
/* __field_align */
offsetof (struct field_align, p) - 1,
/* __size */
sizeof (Hchan *),
/* __hash */
0, /* This value doesn't matter. */
/* __hashfn */
__go_type_hash_error,
/* __equalfn */
__go_type_equal_error,
/* __gc */
NULL, /* This value doesn't matter */
/* __reflection */
NULL, /* This value doesn't matter */
/* __uncommon */
NULL,
/* __pointer_to_this */
NULL,
/* __zero */
NULL /* This value doesn't matter */
},
/* __element_type */
&bool_type_descriptor,
/* __dir */
CHANNEL_BOTH_DIR
};
extern Hchan *__go_new_channel (ChanType *, uintptr);
extern void closechan(Hchan *) __asm__ (GOSYM_PREFIX "runtime.closechan");
static void
initDone(void *arg __attribute__ ((unused))) {
runtime_unlockOSThread();
@ -535,8 +596,15 @@ runtime_main(void* dummy __attribute__((unused)))
if(m != &runtime_m0)
runtime_throw("runtime_main not on m0");
__go_go(runtime_MHeap_Scavenger, nil);
runtime_main_init_done = __go_new_channel(&chan_bool_type_descriptor, 0);
_cgo_notify_runtime_init_done();
main_init();
closechan(runtime_main_init_done);
if(g->defer != &d || d.__pfn != initDone)
runtime_throw("runtime: bad defer entry after init");
g->defer = d.__next;
@ -547,6 +615,14 @@ runtime_main(void* dummy __attribute__((unused)))
// roots.
mstats.enablegc = 1;
if(runtime_isarchive) {
// This is not a complete program, but is instead a
// library built using -buildmode=c-archive or
// c-shared. Now that we are initialized, there is
// nothing further to do.
return;
}
main_main();
// Make racy client program work: if panicking on
@ -1011,8 +1087,14 @@ runtime_mstart(void* mp)
// Install signal handlers; after minit so that minit can
// prepare the thread to be able to handle the signals.
if(m == &runtime_m0)
if(m == &runtime_m0) {
if(runtime_iscgo && !runtime_cgoHasExtraM) {
runtime_cgoHasExtraM = true;
runtime_newextram();
runtime_needextram = 0;
}
runtime_initsig();
}
if(m->mstartfn)
m->mstartfn();
@ -2747,6 +2829,13 @@ checkdead(void)
int32 run, grunning, s;
uintptr i;
// For -buildmode=c-shared or -buildmode=c-archive it's OK if
// there are no running goroutines. The calling program is
// assumed to be running.
if(runtime_isarchive) {
return;
}
// -1 for sysmon
run = runtime_sched.mcount - runtime_sched.nmidle - runtime_sched.nmidlelocked - 1 - countextra();
if(run > 0)
@ -3332,6 +3421,7 @@ void
runtime_proc_scan(struct Workbuf** wbufp, void (*enqueue1)(struct Workbuf**, Obj))
{
enqueue1(wbufp, (Obj){(byte*)&runtime_sched, sizeof runtime_sched, 0});
enqueue1(wbufp, (Obj){(byte*)&runtime_main_init_done, sizeof runtime_main_init_done, 0});
}
// Return whether we are waiting for a GC. This gc toolchain uses

View file

@ -509,6 +509,9 @@ extern uint32 runtime_Hchansize;
extern DebugVars runtime_debug;
extern uintptr runtime_maxstacksize;
extern bool runtime_isstarted;
extern bool runtime_isarchive;
/*
* common functions and data
*/
@ -845,3 +848,9 @@ struct time_now_ret
struct time_now_ret now() __asm__ (GOSYM_PREFIX "time.now")
__attribute__ ((no_split_stack));
extern void _cgo_wait_runtime_init_done (void);
extern void _cgo_notify_runtime_init_done (void);
extern _Bool runtime_iscgo;
extern _Bool runtime_cgoHasExtraM;
extern Hchan *runtime_main_init_done;