[runtime] Transition the trampoline code to use memory managers for memory allocation...
[mono-project.git] / mono / mini / main.c
blob7cc3fecfa17d656b947dd41e021e46eb7991b187
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 "mini.h"
26 #include "mini-runtime.h"
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #ifdef HOST_WIN32
32 # include <io.h>
33 #else
34 # ifndef BUILDVER_INCLUDED
35 # include "buildver-boehm.h"
36 # endif
37 #endif
38 #ifdef TARGET_OSX
39 #include <mach-o/loader.h>
40 #endif
42 //#define TEST_ICALL_SYMBOL_MAP 1
45 * If the MONO_ENV_OPTIONS environment variable is set, it uses this as a
46 * source of command line arguments that are passed to Mono before the
47 * command line arguments specified in the command line.
49 static int
50 mono_main_with_options (int argc, char *argv [])
52 mono_parse_env_options (&argc, &argv);
54 return mono_main (argc, argv);
58 * The Mono executable can initialize itself from a payload attached
59 * at the end of the main program. The payload contains the
60 * main assembly, one or more managed assemblies, configuration
61 * files and other assets that are used instead of launching a
62 * program from the command line.
64 * The startup sequence probes for a magical signature at the end of
65 * the executable, if the 16 characters "xmonkeysloveplay" are found,
66 * the code expects the 64-bits just before it to contain an offset
67 * within the executable with a directory of assets.
69 * All pointers in the file format are encoded as little-endian values
71 * The format of the file is thus:
73 * Location Content
74 * -------- -------
75 * lenght-16 Optional "xmonkeysloveplay", indicating that a
76 * bundled payload is contained in the executable.
77 * length-24 pointer to the directory in the file, address DIR
79 * DIR 32-bit value with the number of entries in the directory
80 * DIR+4 First directory entry.
82 * Each directory entry is made up of:
83 * 4-bytes uint32_t containing the size of a string (STR)
84 * STRING UTF8 encoded and \0 terminated string
85 * 8-bytes uint64_t offset in the file with the payload associated with STRING
86 * 4-bytes uint32_t size of the asset
88 * The following are the known directory entries, without the quotes:
89 * "assembly:NAME" An assembly with the name NAME, assembly is in the payload
90 * "config:NAME" A configuration file (usually file.dll.config) in the payload that is
91 * loaded as the config file for an assembly
92 * "systemconfig:" Treats as a Mono system configuration, payload contains the config file.
93 * "options:" The payload contains command line options to initialize Mono, as if you
94 had set them on MONO_ENV_OPTIONS
95 * "config_dir:DIR" Configures the MONO_PATH to point to point to DIR
96 * "machineconfig:" The payload contains the machine.config file to use at runtime
97 * "env:" Sets the environment variable to the value encoded in the payload
98 * payload contains: 1-byte lenght for the \0 terminated variable,
99 * followed by the value.
100 * "library:NAME" Bundled dynamic library NAME, payload contains the dynamic library
102 #define STREAM_INT(x) GUINT32_TO_LE((*(uint32_t*)x))
103 #define STREAM_LONG(x) GUINT64_TO_LE((*(uint64_t*)x))
105 #ifndef HOST_WIN32
106 static gboolean
107 search_directories(const char *envPath, const char *program, char **new_program)
109 gchar **paths = NULL;
110 gint i;
112 paths = g_strsplit (envPath, G_SEARCHPATH_SEPARATOR_S, 0);
113 g_assert (paths);
115 for (i = 0; paths [i]; ++i) {
116 gchar *path = paths [i];
117 gint path_len = strlen (path);
118 DIR *dir;
119 struct dirent *ent;
121 if (path_len == 0)
122 continue;
124 dir = opendir (path);
125 if (!dir)
126 continue;
128 while ((ent = readdir (dir))){
129 if (!strcmp (ent->d_name, program)){
130 *new_program = g_strdup_printf ("%s%s%s", path, path [path_len - 1] == '/' ? "" : "/", program);
131 closedir (dir);
132 g_strfreev (paths);
133 return TRUE;
137 closedir (dir);
140 *new_program = NULL;
141 g_strfreev (paths);
142 return FALSE;
144 #endif
146 static gboolean
147 probe_embedded (const char *program, int *ref_argc, char **ref_argv [])
149 MonoBundledAssembly last = { NULL, 0, 0 };
150 char sigbuffer [16+sizeof (uint64_t)];
151 gboolean status = FALSE;
152 uint64_t directory_location;
153 off_t sigstart, baseline = 0;
154 uint64_t directory_size;
155 char *directory, *p;
156 int items, i;
157 unsigned char *mapaddress = NULL;
158 void *maphandle = NULL;
159 GArray *assemblies;
160 char *entry_point = NULL;
161 char **new_argv;
162 int j;
164 int fd = open (program, O_RDONLY);
165 #ifndef HOST_WIN32
166 if (fd == -1){
167 // Also search through the PATH in case the program was run from a different directory
168 gchar* envPath = getenv ("PATH");
169 if (envPath){
170 gchar *new_program = NULL;
171 if (search_directories (envPath, program, &new_program)){
172 fd = open (new_program, O_RDONLY);
173 g_free (new_program);
174 new_program = NULL;
178 #endif
179 if (fd == -1)
180 return FALSE;
181 if ((sigstart = lseek (fd, -24, SEEK_END)) == -1)
182 goto doclose;
183 if (read (fd, sigbuffer, sizeof (sigbuffer)) == -1)
184 goto doclose;
185 // First, see if "xmonkeysloveplay" is at the end of file
186 if (memcmp (sigbuffer + sizeof (uint64_t), "xmonkeysloveplay", 16) == 0)
187 goto found;
189 #ifdef TARGET_OSX
192 * If "xmonkeysloveplay" is not at the end of file,
193 * on Mac OS X, we try a little harder, by actually
194 * reading the binary's header structure, to see
195 * if it is located at the end of a LC_SYMTAB section.
197 * This is because Apple code-signing appends a
198 * LC_CODE_SIGNATURE section to the binary, so
199 * for a signed binary, "xmonkeysloveplay" is no
200 * longer at the end of file.
202 * The rest is sanity-checks for the header and section structures.
204 struct mach_header_64 bin_header;
205 if ((sigstart = lseek (fd, 0, SEEK_SET)) == -1)
206 goto doclose;
207 // Find and check binary header
208 if (read (fd, &bin_header, sizeof (bin_header)) == -1)
209 goto doclose;
210 if (bin_header.magic != MH_MAGIC_64)
211 goto doclose;
213 off_t total = bin_header.sizeofcmds;
214 uint32_t count = bin_header.ncmds;
215 while (total > 0 && count > 0) {
216 struct load_command lc;
217 off_t sig_stored = lseek (fd, 0, SEEK_CUR); // get current offset
218 if (read (fd, &lc, sizeof (lc)) == -1)
219 goto doclose;
220 if (lc.cmd == LC_SYMTAB) {
221 struct symtab_command stc;
222 if ((sigstart = lseek (fd, -sizeof (lc), SEEK_CUR)) == -1)
223 goto doclose;
224 if (read (fd, &stc, sizeof (stc)) == -1)
225 goto doclose;
227 // Check the end of the LC_SYMTAB section for "xmonkeysloveplay"
228 if ((sigstart = lseek (fd, -(16 + sizeof (uint64_t)) + stc.stroff + stc.strsize, SEEK_SET)) == -1)
229 goto doclose;
230 if (read (fd, sigbuffer, sizeof (sigbuffer)) == -1)
231 goto doclose;
232 if (memcmp (sigbuffer + sizeof (uint64_t), "xmonkeysloveplay", 16) == 0)
233 goto found;
235 if ((sigstart = lseek (fd, sig_stored + lc.cmdsize, SEEK_SET)) == -1)
236 goto doclose;
237 total -= sizeof (lc.cmdsize);
238 count--;
241 #endif
243 // did not find "xmonkeysloveplay" at end of file or end of LC_SYMTAB section
244 goto doclose;
246 found:
247 directory_location = GUINT64_FROM_LE ((*(uint64_t *) &sigbuffer [0]));
248 if (lseek (fd, directory_location, SEEK_SET) == -1)
249 goto doclose;
250 directory_size = sigstart-directory_location;
251 directory = g_malloc (directory_size);
252 if (directory == NULL)
253 goto doclose;
254 if (read (fd, directory, directory_size) == -1)
255 goto dofree;
257 items = STREAM_INT (directory);
258 p = directory+4;
260 assemblies = g_array_new (0, 0, sizeof (MonoBundledAssembly*));
261 for (i = 0; i < items; i++){
262 char *kind;
263 int strsize = STREAM_INT (p);
264 uint64_t offset;
265 uint32_t item_size;
266 kind = p+4;
267 p += 4 + strsize;
268 offset = STREAM_LONG(p);
269 p += 8;
270 item_size = STREAM_INT (p);
271 p += 4;
273 if (mapaddress == NULL) {
274 char *error_message = NULL;
275 mapaddress = (guchar*)mono_file_map_error (directory_location - offset, MONO_MMAP_READ | MONO_MMAP_PRIVATE,
276 fd, offset, &maphandle, program, &error_message);
277 if (mapaddress == NULL) {
278 if (error_message)
279 fprintf (stderr, "Error mapping file: %s\n", error_message);
280 else
281 perror ("Error mapping file");
282 exit (1);
284 baseline = offset;
286 if (strncmp (kind, "assembly:", strlen ("assembly:")) == 0){
287 char *aname = kind + strlen ("assembly:");
288 MonoBundledAssembly mba = { aname, mapaddress + offset - baseline, item_size }, *ptr;
289 ptr = g_new (MonoBundledAssembly, 1);
290 memcpy (ptr, &mba, sizeof (MonoBundledAssembly));
291 g_array_append_val (assemblies, ptr);
292 if (entry_point == NULL)
293 entry_point = aname;
294 } else if (strncmp (kind, "config:", strlen ("config:")) == 0){
295 char *config = kind + strlen ("config:");
296 char *aname = g_strdup (config);
297 aname [strlen(aname)-strlen(".config")] = 0;
298 mono_register_config_for_assembly (aname, g_str_from_file_region (fd, offset, item_size));
299 } else if (strncmp (kind, "systemconfig:", strlen ("systemconfig:")) == 0){
300 mono_config_parse_memory (g_str_from_file_region (fd, offset, item_size));
301 } else if (strncmp (kind, "options:", strlen ("options:")) == 0){
302 mono_parse_options_from (g_str_from_file_region (fd, offset, item_size), ref_argc, ref_argv);
303 } else if (strncmp (kind, "config_dir:", strlen ("config_dir:")) == 0){
304 char *mono_path_value = g_getenv ("MONO_PATH");
305 mono_set_dirs (mono_path_value, g_str_from_file_region (fd, offset, item_size));
306 g_free (mono_path_value);
307 } else if (strncmp (kind, "machineconfig:", strlen ("machineconfig:")) == 0) {
308 mono_register_machine_config (g_str_from_file_region (fd, offset, item_size));
309 } else if (strncmp (kind, "env:", strlen ("env:")) == 0){
310 char *data = g_str_from_file_region (fd, offset, item_size);
311 uint8_t count = *data++;
312 char *value = data + count + 1;
313 g_setenv (data, value, FALSE);
314 } else if (strncmp (kind, "library:", strlen ("library:")) == 0){
315 mono_loader_save_bundled_library (fd, offset, item_size, kind + strlen ("library:"));
316 } else {
317 fprintf (stderr, "Unknown stream on embedded package: %s\n", kind);
318 exit (1);
321 g_array_append_val (assemblies, last);
323 mono_register_bundled_assemblies ((const MonoBundledAssembly **) assemblies->data);
324 new_argv = g_new (char *, (*ref_argc)+1);
325 new_argv [0] = (*ref_argv)[0];
326 new_argv [1] = entry_point;
327 for (j = 1; j < *ref_argc; j++)
328 new_argv [j+1] = (*ref_argv)[j];
329 *ref_argv = new_argv;
330 (*ref_argc)++;
332 return TRUE;
334 dofree:
335 g_free (directory);
336 doclose:
337 if (!status)
338 close (fd);
339 return status;
342 #if TEST_ICALL_SYMBOL_MAP
344 const char*
345 mono_lookup_icall_symbol_internal (gpointer func);
347 ICALL_EXPORT int ves_icall_Interop_Sys_DoubleToString (double, char*, char*, int);
349 #endif
351 #ifdef HOST_WIN32
353 #include <shellapi.h>
355 #ifdef _WINDOWS
356 int APIENTRY
357 wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
358 #else
360 main (int _argc, char* _argv[])
361 #endif
363 gunichar2 *module_file_name;
364 guint32 length;
365 int argc;
366 gunichar2** argvw;
367 gchar** argv;
368 int i;
370 argvw = CommandLineToArgvW (GetCommandLineW (), &argc);
371 argv = g_new0 (gchar*, argc + 1);
372 for (i = 0; i < argc; i++)
373 argv [i] = g_utf16_to_utf8 (argvw [i], -1, NULL, NULL, NULL);
374 argv [argc] = NULL;
376 LocalFree (argvw);
378 if (mono_get_module_filename (NULL, &module_file_name, &length)) {
379 char *entry = g_utf16_to_utf8 (module_file_name, length, NULL, NULL, NULL);
380 g_free (module_file_name);
381 probe_embedded (entry, &argc, &argv);
384 return mono_main_with_options (argc, argv);
387 #else
390 main (int argc, char* argv[])
392 mono_build_date = build_date;
394 #if TEST_ICALL_SYMBOL_MAP
395 const char *p = mono_lookup_icall_symbol_internal (mono_lookup_icall_symbol_internal);
396 printf ("%s\n", p ? p : "null");
397 p = mono_lookup_icall_symbol_internal (ves_icall_Interop_Sys_DoubleToString);
398 printf ("%s\n", p ? p : "null");
399 #endif
401 probe_embedded (argv [0], &argc, &argv);
402 return mono_main_with_options (argc, argv);
405 #endif