[interp] Make newarr lockfree
[mono-project.git] / mono / mini / main.c
blob6916d6410f5ba8c23787a967d1a5ecdd6e5d2215
1 /**
2 * \file
3 * The main entry point for the mono executable
5 * The main entry point does a few things:
6 *
7 * * It probes whether the executable has a bundle appended
8 * at the end, and if so, registers the various bundled
9 * resources with Mono and executes the contained bundle
11 * * Parses the MONO_ENV_OPTIONS variable to treat the
12 * contents of the variable as command line arguments for
13 * the mono runtime
15 * * Launches Mono, by calling mono_main.
17 #include <config.h>
18 #include <fcntl.h>
19 #ifndef HOST_WIN32
20 #include <dirent.h>
21 #endif
22 #include <mono/metadata/assembly.h>
23 #include <mono/metadata/mono-config.h>
24 #include <mono/utils/mono-mmap.h>
25 #include <mono/utils/mono-dl.h>
26 #include "mini.h"
27 #include "mini-runtime.h"
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #ifdef HOST_WIN32
33 # include <io.h>
34 #else
35 # ifndef BUILDVER_INCLUDED
36 # include "buildver-boehm.h"
37 # endif
38 #endif
41 * If the MONO_ENV_OPTIONS environment variable is set, it uses this as a
42 * source of command line arguments that are passed to Mono before the
43 * command line arguments specified in the command line.
45 static int
46 mono_main_with_options (int argc, char *argv [])
48 mono_parse_env_options (&argc, &argv);
50 return mono_main (argc, argv);
54 * The Mono executable can initialize itself from a payload attached
55 * at the end of the main program. The payload contains the
56 * main assembly, one or more managed assemblies, configuration
57 * files and other assets that are used instead of launching a
58 * program from the command line.
60 * The startup sequence probes for a magical signature at the end of
61 * the executable, if the 16 characters "xmonkeysloveplay" are found,
62 * the code expects the 64-bits just before it to contain an offset
63 * within the executable with a directory of assets.
65 * All pointers in the file format are encoded as little-endian values
67 * The format of the file is thus:
69 * Location Content
70 * -------- -------
71 * lenght-16 Optional "xmonkeysloveplay", indicating that a
72 * bundled payload is contained in the executable.
73 * length-24 pointer to the directory in the file, address DIR
75 * DIR 32-bit value with the number of entries in the directory
76 * DIR+4 First directory entry.
78 * Each directory entry is made up of:
79 * 4-bytes uint32_t containing the size of a string (STR)
80 * STRING UTF8 encoded and \0 terminated string
81 * 8-bytes uint64_t offset in the file with the payload associated with STRING
82 * 4-bytes uint32_t size of the asset
84 * The following are the known directory entries, without the quotes:
85 * "assembly:NAME" An assembly with the name NAME, assembly is in the payload
86 * "config:NAME" A configuration file (usually file.dll.config) in the payload that is
87 * loaded as the config file for an assembly
88 * "systemconfig:" Treats as a Mono system configuration, payload contains the config file.
89 * "options:" The payload contains command line options to initialize Mono, as if you
90 had set them on MONO_ENV_OPTIONS
91 * "config_dir:DIR" Configures the MONO_PATH to point to point to DIR
92 * "machineconfig:" The payload contains the machine.config file to use at runtime
93 * "env:" Sets the environment variable to the value encoded in the payload
94 * payload contains: 1-byte lenght for the \0 terminated variable,
95 * followed by the value.
96 * "library:NAME" Bundled dynamic library NAME, payload contains the dynamic library
98 #define STREAM_INT(x) GUINT32_TO_LE((*(uint32_t*)x))
99 #define STREAM_LONG(x) GUINT64_TO_LE((*(uint64_t*)x))
102 * Loads a chunk of data from the file pointed to by the
103 * @fd starting at the file offset @offset for @size bytes
104 * and returns an allocated version of that string, or NULL
105 * on error.
107 static char *
108 load_from_region (int fd, uint64_t offset, uint64_t size)
110 char *buffer;
111 off_t loc;
112 int status;
114 do {
115 loc = lseek (fd, offset, SEEK_SET);
116 } while (loc == -1 && errno == EINTR);
117 if (loc == -1)
118 return NULL;
119 buffer = g_malloc (size + 1);
120 if (buffer == NULL)
121 return NULL;
122 buffer [size] = 0;
123 do {
124 status = read (fd, buffer, size);
125 } while (status == -1 && errno == EINTR);
126 if (status == -1){
127 g_free (buffer);
128 return NULL;
130 return buffer;
133 /* Did we initialize the temporary directory for dynamic libraries */
134 static int bundle_save_library_initialized;
136 /* List of bundled libraries we unpacked */
137 static GSList *bundle_library_paths;
139 /* Directory where we unpacked dynamic libraries */
140 static char *bundled_dylibrary_directory;
142 #ifdef HAVE_ATEXIT
143 static void
144 delete_bundled_libraries (void)
146 GSList *list;
148 for (list = bundle_library_paths; list != NULL; list = list->next){
149 unlink ((const char*)list->data);
151 rmdir (bundled_dylibrary_directory);
153 #endif
155 static void
156 bundle_save_library_initialize (void)
158 bundle_save_library_initialized = 1;
159 char *path = g_build_filename (g_get_tmp_dir (), "mono-bundle-XXXXXX", NULL);
160 bundled_dylibrary_directory = g_mkdtemp (path);
161 g_free (path);
162 if (bundled_dylibrary_directory == NULL)
163 return;
164 #ifdef HAVE_ATEXIT
165 atexit (delete_bundled_libraries);
166 #endif
169 static void
170 save_library (int fd, uint64_t offset, uint64_t size, const char *destfname)
172 MonoDl *lib;
173 char *file, *buffer, *err, *internal_path;
174 if (!bundle_save_library_initialized)
175 bundle_save_library_initialize ();
177 file = g_build_filename (bundled_dylibrary_directory, destfname, NULL);
178 buffer = load_from_region (fd, offset, size);
179 g_file_set_contents (file, buffer, size, NULL);
181 lib = mono_dl_open (file, MONO_DL_LAZY, &err);
182 if (lib == NULL){
183 fprintf (stderr, "Error loading shared library: %s %s\n", file, err);
184 exit (1);
186 // Register the name with "." as this is how it will be found when embedded
187 internal_path = g_build_filename (".", destfname, NULL);
188 mono_loader_register_module (internal_path, lib);
189 g_free (internal_path);
190 bundle_library_paths = g_slist_append (bundle_library_paths, file);
192 g_free (buffer);
195 #ifndef HOST_WIN32
196 static gboolean
197 search_directories(const char *envPath, const char *program, char **new_program)
199 gchar **paths = NULL;
200 gint i;
202 paths = g_strsplit (envPath, G_SEARCHPATH_SEPARATOR_S, 0);
203 g_assert (paths);
205 for (i = 0; paths [i]; ++i) {
206 gchar *path = paths [i];
207 gint path_len = strlen (path);
208 DIR *dir;
209 struct dirent *ent;
211 if (path_len == 0)
212 continue;
214 dir = opendir (path);
215 if (!dir)
216 continue;
218 while ((ent = readdir (dir))){
219 if (!strcmp (ent->d_name, program)){
220 *new_program = g_strdup_printf ("%s%s%s", path, path [path_len - 1] == '/' ? "" : "/", program);
221 closedir (dir);
222 g_strfreev (paths);
223 return TRUE;
227 closedir (dir);
230 *new_program = NULL;
231 g_strfreev (paths);
232 return FALSE;
234 #endif
236 static gboolean
237 probe_embedded (const char *program, int *ref_argc, char **ref_argv [])
239 MonoBundledAssembly last = { NULL, 0, 0 };
240 char sigbuffer [16+sizeof (uint64_t)];
241 gboolean status = FALSE;
242 uint64_t directory_location;
243 off_t sigstart, baseline = 0;
244 uint64_t directory_size;
245 char *directory, *p;
246 int items, i;
247 unsigned char *mapaddress = NULL;
248 void *maphandle = NULL;
249 GArray *assemblies;
250 char *entry_point = NULL;
251 char **new_argv;
252 int j;
254 int fd = open (program, O_RDONLY);
255 #ifndef HOST_WIN32
256 if (fd == -1){
257 // Also search through the PATH in case the program was run from a different directory
258 gchar* envPath = getenv ("PATH");
259 if (envPath){
260 gchar *new_program = NULL;
261 if (search_directories (envPath, program, &new_program)){
262 fd = open (new_program, O_RDONLY);
263 g_free (new_program);
264 new_program = NULL;
268 #endif
269 if (fd == -1)
270 return FALSE;
271 if ((sigstart = lseek (fd, -24, SEEK_END)) == -1)
272 goto doclose;
273 if (read (fd, sigbuffer, sizeof (sigbuffer)) == -1)
274 goto doclose;
275 if (memcmp (sigbuffer+sizeof(uint64_t), "xmonkeysloveplay", 16) != 0)
276 goto doclose;
277 directory_location = GUINT64_FROM_LE ((*(uint64_t *) &sigbuffer [0]));
278 if (lseek (fd, directory_location, SEEK_SET) == -1)
279 goto doclose;
280 directory_size = sigstart-directory_location;
281 directory = g_malloc (directory_size);
282 if (directory == NULL)
283 goto doclose;
284 if (read (fd, directory, directory_size) == -1)
285 goto dofree;
287 items = STREAM_INT (directory);
288 p = directory+4;
290 assemblies = g_array_new (0, 0, sizeof (MonoBundledAssembly*));
291 for (i = 0; i < items; i++){
292 char *kind;
293 int strsize = STREAM_INT (p);
294 uint64_t offset;
295 uint32_t item_size;
296 kind = p+4;
297 p += 4 + strsize;
298 offset = STREAM_LONG(p);
299 p += 8;
300 item_size = STREAM_INT (p);
301 p += 4;
303 if (mapaddress == NULL) {
304 char *error_message = NULL;
305 mapaddress = (guchar*)mono_file_map_error (directory_location - offset, MONO_MMAP_READ | MONO_MMAP_PRIVATE,
306 fd, offset, &maphandle, program, &error_message);
307 if (mapaddress == NULL) {
308 if (error_message)
309 fprintf (stderr, "Error mapping file: %s\n", error_message);
310 else
311 perror ("Error mapping file");
312 exit (1);
314 baseline = offset;
316 if (strncmp (kind, "assembly:", strlen ("assembly:")) == 0){
317 char *aname = kind + strlen ("assembly:");
318 MonoBundledAssembly mba = { aname, mapaddress + offset - baseline, item_size }, *ptr;
319 ptr = g_new (MonoBundledAssembly, 1);
320 memcpy (ptr, &mba, sizeof (MonoBundledAssembly));
321 g_array_append_val (assemblies, ptr);
322 if (entry_point == NULL)
323 entry_point = aname;
324 } else if (strncmp (kind, "config:", strlen ("config:")) == 0){
325 char *config = kind + strlen ("config:");
326 char *aname = g_strdup (config);
327 aname [strlen(aname)-strlen(".config")] = 0;
328 mono_register_config_for_assembly (aname, load_from_region (fd, offset, item_size));
329 } else if (strncmp (kind, "systemconfig:", strlen ("systemconfig:")) == 0){
330 mono_config_parse_memory (load_from_region (fd, offset, item_size));
331 } else if (strncmp (kind, "options:", strlen ("options:")) == 0){
332 mono_parse_options_from (load_from_region (fd, offset, item_size), ref_argc, ref_argv);
333 } else if (strncmp (kind, "config_dir:", strlen ("config_dir:")) == 0){
334 char *mono_path_value = g_getenv ("MONO_PATH");
335 mono_set_dirs (mono_path_value, load_from_region (fd, offset, item_size));
336 g_free (mono_path_value);
337 } else if (strncmp (kind, "machineconfig:", strlen ("machineconfig:")) == 0) {
338 mono_register_machine_config (load_from_region (fd, offset, item_size));
339 } else if (strncmp (kind, "env:", strlen ("env:")) == 0){
340 char *data = load_from_region (fd, offset, item_size);
341 uint8_t count = *data++;
342 char *value = data + count + 1;
343 g_setenv (data, value, FALSE);
344 } else if (strncmp (kind, "library:", strlen ("library:")) == 0){
345 save_library (fd, offset, item_size, kind + strlen ("library:"));
346 } else {
347 fprintf (stderr, "Unknown stream on embedded package: %s\n", kind);
348 exit (1);
351 g_array_append_val (assemblies, last);
353 mono_register_bundled_assemblies ((const MonoBundledAssembly **) assemblies->data);
354 new_argv = g_new (char *, (*ref_argc)+1);
355 new_argv [0] = (*ref_argv)[0];
356 new_argv [1] = entry_point;
357 for (j = 1; j < *ref_argc; j++)
358 new_argv [j+1] = (*ref_argv)[j];
359 *ref_argv = new_argv;
360 (*ref_argc)++;
362 return TRUE;
364 dofree:
365 g_free (directory);
366 doclose:
367 if (!status)
368 close (fd);
369 return status;
372 #ifdef HOST_WIN32
374 #include <shellapi.h>
377 main (void)
379 TCHAR szFileName[MAX_PATH];
380 int argc;
381 gunichar2** argvw;
382 gchar** argv;
383 int i;
384 DWORD count;
386 argvw = CommandLineToArgvW (GetCommandLine (), &argc);
387 argv = g_new0 (gchar*, argc + 1);
388 for (i = 0; i < argc; i++)
389 argv [i] = g_utf16_to_utf8 (argvw [i], -1, NULL, NULL, NULL);
390 argv [argc] = NULL;
392 LocalFree (argvw);
394 if ((count = GetModuleFileName (NULL, szFileName, MAX_PATH)) != 0){
395 char *entry = g_utf16_to_utf8 (szFileName, count, NULL, NULL, NULL);
396 probe_embedded (entry, &argc, &argv);
399 return mono_main_with_options (argc, argv);
402 #else
405 main (int argc, char* argv[])
407 mono_build_date = build_date;
409 probe_embedded (argv [0], &argc, &argv);
410 return mono_main_with_options (argc, argv);
413 #endif