| /* 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 <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <fcntl.h> | |
| #include "runtime.h" | |
| #include "array.h" | |
| #include "arch.h" | |
| #if defined(__sun) && defined(__SVR4) | |
| /* Read a file into memory on Solaris, returning an malloc'ed buffer | |
| and setting *SIZE to its size. */ | |
| static char * | |
| read_file (const char *fn, size_t *size) | |
| { | |
| struct stat st; | |
| char *buf; | |
| int o; | |
| ssize_t got; | |
| if (stat (fn, &st) < 0) | |
| return NULL; | |
| buf = malloc ((size_t) st.st_size); | |
| if (buf == NULL) | |
| return NULL; | |
| o = open (fn, O_RDONLY); | |
| if (o < 0) | |
| { | |
| free (buf); | |
| return NULL; | |
| } | |
| got = read (o, buf, st.st_size); | |
| close (o); | |
| if (got != st.st_size) | |
| { | |
| free (buf); | |
| return NULL; | |
| } | |
| *size = (size_t) got; | |
| return buf; | |
| } | |
| /* On Solaris we don't get passed argc/argv, but we can fetch it from | |
| /proc/PID/cmdline. */ | |
| static void | |
| read_cmdline (int *argc, char ***argv) | |
| { | |
| pid_t pid; | |
| char fn[50]; | |
| char *argbuf; | |
| size_t argsize; | |
| char *envbuf; | |
| size_t envsize; | |
| char *p; | |
| int i; | |
| int ac; | |
| *argc = 0; | |
| *argv = NULL; | |
| pid = getpid (); | |
| snprintf (fn, sizeof fn, "/proc/%ld/cmdline", (long) pid); | |
| argbuf = read_file (fn, &argsize); | |
| if (argbuf == NULL) | |
| return; | |
| snprintf (fn, sizeof fn, "/proc/%ld/environ", (long) pid); | |
| envbuf = read_file (fn, &envsize); | |
| if (envbuf == NULL) | |
| { | |
| free (argbuf); | |
| return; | |
| } | |
| i = 0; | |
| for (p = argbuf; p < argbuf + argsize; p++) | |
| if (*p == '\0') | |
| ++i; | |
| ac = i; | |
| ++i; // For trailing NULL. | |
| for (p = envbuf; p < envbuf + envsize; p++) | |
| if (*p == '\0') | |
| ++i; | |
| ++i; // For trailing NULL. | |
| *argv = (char **) malloc (i * sizeof (char *)); | |
| if (*argv == NULL) | |
| { | |
| free (argbuf); | |
| free (envbuf); | |
| return; | |
| } | |
| *argc = ac; | |
| (*argv)[0] = argbuf; | |
| i = 0; | |
| for (p = argbuf; p < argbuf + argsize; p++) | |
| { | |
| if (*p == '\0') | |
| { | |
| ++i; | |
| (*argv)[i] = p + 1; | |
| } | |
| } | |
| (*argv)[i] = NULL; | |
| ++i; | |
| (*argv)[i] = envbuf; | |
| for (p = envbuf; p < envbuf + envsize; p++) | |
| { | |
| if (*p == '\0') | |
| { | |
| ++i; | |
| (*argv)[i] = p + 1; | |
| } | |
| } | |
| (*argv)[i] = NULL; | |
| } | |
| #endif /* defined(__sun) && defined(__SVR4) */ | |
| /* 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); | |
| /* .init_array section does not exist in AIX XCOFF. | |
| -Wl,-binitfini:__go_init option will be required to build go | |
| libraries and make sure __go_init is called when the library is | |
| loaded. This requires __go_init to be exported. */ | |
| void __go_init (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; | |
| }; | |
| #ifndef _AIX | |
| /* 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)) = | |
| { __go_init }; | |
| #endif | |
| /* 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. */ | |
| void | |
| __go_init (int argc, char **argv, char** env __attribute__ ((unused))) | |
| { | |
| int err; | |
| pthread_attr_t attr; | |
| struct args *a; | |
| pthread_t tid; | |
| #if defined(__sun) && defined(__SVR4) | |
| read_cmdline (&argc, &argv); | |
| #endif | |
| runtime_isarchive = true; | |
| setIsCgo (); | |
| runtime_cpuinit (); | |
| runtime_initsig(true); | |
| 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; | |
| if (runtime_isstarted) | |
| return NULL; | |
| runtime_isstarted = true; | |
| runtime_ginit (); | |
| runtime_check (); | |
| runtime_args (a->argc, (byte **) a->argv); | |
| runtime_osinit (); | |
| runtime_schedinit (); | |
| __go_go ((uintptr)(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); | |
| } |