3 * The main entry point for the mono executable
5 * The main entry point does a few things:
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
15 * * Launches Mono, by calling mono_main.
22 #include <mono/metadata/assembly.h>
23 #include <mono/metadata/mono-config.h>
24 #include <mono/utils/mono-mmap.h>
26 #include "mini-runtime.h"
34 # ifndef BUILDVER_INCLUDED
35 # include "buildver-boehm.h"
39 #include <mach-o/loader.h>
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.
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:
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))
107 search_directories(const char *envPath
, const char *program
, char **new_program
)
109 gchar
**paths
= NULL
;
112 paths
= g_strsplit (envPath
, G_SEARCHPATH_SEPARATOR_S
, 0);
115 for (i
= 0; paths
[i
]; ++i
) {
116 gchar
*path
= paths
[i
];
117 gint path_len
= strlen (path
);
124 dir
= opendir (path
);
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
);
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
;
157 unsigned char *mapaddress
= NULL
;
158 void *maphandle
= NULL
;
160 char *entry_point
= NULL
;
164 int fd
= open (program
, O_RDONLY
);
167 // Also search through the PATH in case the program was run from a different directory
168 gchar
* envPath
= getenv ("PATH");
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
);
181 if ((sigstart
= lseek (fd
, -24, SEEK_END
)) == -1)
183 if (read (fd
, sigbuffer
, sizeof (sigbuffer
)) == -1)
185 // First, see if "xmonkeysloveplay" is at the end of file
186 if (memcmp (sigbuffer
+ sizeof (uint64_t), "xmonkeysloveplay", 16) == 0)
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)
207 // Find and check binary header
208 if (read (fd
, &bin_header
, sizeof (bin_header
)) == -1)
210 if (bin_header
.magic
!= MH_MAGIC_64
)
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)
220 if (lc
.cmd
== LC_SYMTAB
) {
221 struct symtab_command stc
;
222 if ((sigstart
= lseek (fd
, -sizeof (lc
), SEEK_CUR
)) == -1)
224 if (read (fd
, &stc
, sizeof (stc
)) == -1)
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)
230 if (read (fd
, sigbuffer
, sizeof (sigbuffer
)) == -1)
232 if (memcmp (sigbuffer
+ sizeof (uint64_t), "xmonkeysloveplay", 16) == 0)
235 if ((sigstart
= lseek (fd
, sig_stored
+ lc
.cmdsize
, SEEK_SET
)) == -1)
237 total
-= sizeof (lc
.cmdsize
);
243 // did not find "xmonkeysloveplay" at end of file or end of LC_SYMTAB section
247 directory_location
= GUINT64_FROM_LE ((*(uint64_t *) &sigbuffer
[0]));
248 if (lseek (fd
, directory_location
, SEEK_SET
) == -1)
250 directory_size
= sigstart
-directory_location
;
251 directory
= g_malloc (directory_size
);
252 if (directory
== NULL
)
254 if (read (fd
, directory
, directory_size
) == -1)
257 items
= STREAM_INT (directory
);
260 assemblies
= g_array_new (0, 0, sizeof (MonoBundledAssembly
*));
261 for (i
= 0; i
< items
; i
++){
263 int strsize
= STREAM_INT (p
);
268 offset
= STREAM_LONG(p
);
270 item_size
= STREAM_INT (p
);
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
) {
279 fprintf (stderr
, "Error mapping file: %s\n", error_message
);
281 perror ("Error mapping file");
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
)
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:"));
317 fprintf (stderr
, "Unknown stream on embedded package: %s\n", kind
);
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
;
342 #if TEST_ICALL_SYMBOL_MAP
345 mono_lookup_icall_symbol_internal (gpointer func
);
347 ICALL_EXPORT
int ves_icall_Interop_Sys_DoubleToString (double, char*, char*, int);
353 #include <shellapi.h>
357 wWinMain (HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPWSTR lpCmdLine
, int nCmdShow
)
360 main (int _argc
, char* _argv
[])
363 gunichar2
*module_file_name
;
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
);
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
);
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");
401 probe_embedded (argv
[0], &argc
, &argv
);
402 return mono_main_with_options (argc
, argv
);