| /* 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); |
| } |