1 /* go-libmain.c -- the startup function for a Go library.
2 
3    Copyright 2015 The Go Authors. All rights reserved.
4    Use of this source code is governed by a BSD-style
5    license that can be found in the LICENSE file.  */
6 
7 #include "config.h"
8 
9 #include <errno.h>
10 #include <pthread.h>
11 #include <stdlib.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 
18 #include "runtime.h"
19 #include "array.h"
20 #include "arch.h"
21 
22 #if defined(__sun) && defined(__SVR4)
23 
24 /* Read a file into memory on Solaris, returning an malloc'ed buffer
25    and setting *SIZE to its size.  */
26 
27 static char *
read_file(const char * fn,size_t * size)28 read_file (const char *fn, size_t *size)
29 {
30   struct stat st;
31   char *buf;
32   int o;
33   ssize_t got;
34 
35   if (stat (fn, &st) < 0)
36     return NULL;
37   buf = malloc ((size_t) st.st_size);
38   if (buf == NULL)
39     return NULL;
40   o = open (fn, O_RDONLY);
41   if (o < 0)
42     {
43       free (buf);
44       return NULL;
45     }
46   got = read (o, buf, st.st_size);
47   close (o);
48   if (got != st.st_size)
49     {
50       free (buf);
51       return NULL;
52     }
53 
54   *size = (size_t) got;
55   return buf;
56 }
57 
58 /* On Solaris we don't get passed argc/argv, but we can fetch it from
59    /proc/PID/cmdline.  */
60 
61 static void
read_cmdline(int * argc,char *** argv)62 read_cmdline (int *argc, char ***argv)
63 {
64   pid_t pid;
65   char fn[50];
66   char *argbuf;
67   size_t argsize;
68   char *envbuf;
69   size_t envsize;
70   char *p;
71   int i;
72   int ac;
73 
74   *argc = 0;
75   *argv = NULL;
76 
77   pid = getpid ();
78   snprintf (fn, sizeof fn, "/proc/%ld/cmdline", (long) pid);
79   argbuf = read_file (fn, &argsize);
80   if (argbuf == NULL)
81     return;
82 
83   snprintf (fn, sizeof fn, "/proc/%ld/environ", (long) pid);
84   envbuf = read_file (fn, &envsize);
85   if (envbuf == NULL)
86     {
87       free (argbuf);
88       return;
89     }
90 
91   i = 0;
92   for (p = argbuf; p < argbuf + argsize; p++)
93     if (*p == '\0')
94       ++i;
95   ac = i;
96   ++i; // For trailing NULL.
97   for (p = envbuf; p < envbuf + envsize; p++)
98     if (*p == '\0')
99       ++i;
100   ++i; // For trailing NULL.
101 
102   *argv = (char **) malloc (i * sizeof (char *));
103   if (*argv == NULL)
104     {
105       free (argbuf);
106       free (envbuf);
107       return;
108     }
109 
110   *argc = ac;
111   (*argv)[0] = argbuf;
112   i = 0;
113   for (p = argbuf; p < argbuf + argsize; p++)
114     {
115       if (*p == '\0')
116 	{
117 	  ++i;
118 	  (*argv)[i] = p + 1;
119 	}
120     }
121   (*argv)[i] = NULL;
122   ++i;
123   (*argv)[i] = envbuf;
124   for (p = envbuf; p < envbuf + envsize; p++)
125     {
126       if (*p == '\0')
127 	{
128 	  ++i;
129 	  (*argv)[i] = p + 1;
130 	}
131     }
132   (*argv)[i] = NULL;
133 }
134 
135 #endif /* defined(__sun) && defined(__SVR4) */
136 
137 /* This is used when building a standalone Go library using the Go
138    command's -buildmode=c-archive or -buildmode=c-shared option.  It
139    starts up the Go code as a global constructor but does not take any
140    other action.  The main program is written in some other language
141    and calls exported Go functions as needed.  */
142 
143 static void die (const char *, int);
144 /* .init_array section does not exist in AIX XCOFF.
145    -Wl,-binitfini:__go_init option will be required to build go
146    libraries and make sure __go_init is called when the library is
147    loaded. This requires __go_init to be exported.  */
148 
149 void __go_init (int, char **, char **);
150 static void *gostart (void *);
151 
152 /* Used to pass arguments to the thread that runs the Go startup.  */
153 
154 struct args {
155   int argc;
156   char **argv;
157 };
158 
159 #ifndef _AIX
160 /* We use .init_array so that we can get the command line arguments.
161    This obviously assumes .init_array support; different systems may
162    require other approaches.  */
163 
164 typedef void (*initarrayfn) (int, char **, char **);
165 
166 static initarrayfn initarray[1]
167 __attribute__ ((section (".init_array"), used)) =
168   { __go_init };
169 #endif
170 
171 /* This function is called at program startup time.  It starts a new
172    thread to do the actual Go startup, so that program startup is not
173    paused waiting for the Go initialization functions.  Exported cgo
174    functions will wait for initialization to complete if
175    necessary.  */
176 
177 void
__go_init(int argc,char ** argv,char ** env)178 __go_init (int argc, char **argv, char** env __attribute__ ((unused)))
179 {
180   int err;
181   pthread_attr_t attr;
182   struct args *a;
183   pthread_t tid;
184 
185 #if defined(__sun) && defined(__SVR4)
186   read_cmdline (&argc, &argv);
187 #endif
188 
189   runtime_isarchive = true;
190 
191   setIsCgo ();
192   runtime_cpuinit ();
193   runtime_initsig(true);
194 
195   a = (struct args *) malloc (sizeof *a);
196   if (a == NULL)
197     die ("malloc", errno);
198   a->argc = argc;
199   a->argv = argv;
200 
201   err = pthread_attr_init (&attr);
202   if (err != 0)
203     die ("pthread_attr_init", err);
204   err = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
205   if (err != 0)
206     die ("pthread_attr_setdetachstate", err);
207 
208   err = pthread_create (&tid, &attr, gostart, (void *) a);
209   if (err != 0)
210     die ("pthread_create", err);
211 
212   err = pthread_attr_destroy (&attr);
213   if (err != 0)
214     die ("pthread_attr_destroy", err);
215 }
216 
217 /* Start up the Go runtime.  */
218 
219 static void *
gostart(void * arg)220 gostart (void *arg)
221 {
222   struct args *a = (struct args *) arg;
223 
224   if (runtime_isstarted)
225     return NULL;
226   runtime_isstarted = true;
227 
228   runtime_check ();
229   runtime_args (a->argc, (byte **) a->argv);
230   setncpu (getproccount ());
231   setpagesize (getpagesize ());
232   runtime_sched = runtime_getsched();
233   runtime_schedinit ();
234   __go_go ((uintptr)(runtime_main), NULL);
235   runtime_mstart (runtime_m ());
236   abort ();
237 }
238 
239 /* If something goes wrong during program startup, crash.  There is no
240    way to report failure and nobody to whom to report it.  */
241 
242 static void
die(const char * fn,int err)243 die (const char *fn, int err)
244 {
245   fprintf (stderr, "%s: %d\n", fn, err);
246   exit (EXIT_FAILURE);
247 }
248