Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / main.c
1 /**
2  * \file
3  * The main entry point for the mono executable
4  *
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
10  *
11  *   * Parses the MONO_ENV_OPTIONS variable to treat the
12  *     contents of the variable as command line arguments for
13  *     the mono runtime
14  *
15  *   * Launches Mono, by calling mono_main.
16  */
17 #include <config.h>
18 #include <fcntl.h>
19 #include <mono/metadata/assembly.h>
20 #include <mono/metadata/mono-config.h>
21 #include <mono/utils/mono-mmap.h>
22 #include <mono/utils/mono-dl.h>
23 #include "mini.h"
24
25 #ifdef HAVE_UNISTD_H
26 #  include <unistd.h>
27 #endif
28 #ifdef HOST_WIN32
29 #  include <io.h>
30 #else
31 #  ifndef BUILDVER_INCLUDED
32 #    include "buildver-boehm.h"
33 #  endif
34 #endif
35
36 /*
37  * If the MONO_ENV_OPTIONS environment variable is set, it uses this as a
38  * source of command line arguments that are passed to Mono before the
39  * command line arguments specified in the command line.
40  */
41 static int
42 mono_main_with_options (int argc, char *argv [])
43 {
44         mono_parse_env_options (&argc, &argv);
45
46         return mono_main (argc, argv);
47 }
48
49 /*
50  * The Mono executable can initialize itself from a payload attached
51  * at the end of the main program.   The payload contains the
52  * main assembly, one or more managed assemblies, configuration
53  * files and other assets that are used instead of launching a
54  * program from the command line.
55  *
56  * The startup sequence probes for a magical signature at the end of
57  * the executable, if the 16 characters "xmonkeysloveplay" are found,
58  * the code expects the 64-bits just before it to contain an offset
59  * within the executable with a directory of assets.
60  *
61  * All pointers in the file format are encoded as little-endian values
62  *
63  * The format of the file is thus:
64  *
65  * Location        Content
66  * --------        -------
67  * lenght-16       Optional "xmonkeysloveplay", indicating that a
68  *                 bundled payload is contained in the executable.
69  * length-24       pointer to the directory in the file, address DIR
70  *
71  * DIR             32-bit value with the number of entries in the directory
72  * DIR+4           First directory entry.
73  *
74  * Each directory entry is made up of:
75  * 4-bytes         uint32_t containing the size of a string (STR)
76  * STRING          UTF8 encoded and \0 terminated string
77  * 8-bytes         uint64_t offset in the file with the payload associated with STRING
78  * 4-bytes         uint32_t size of the asset
79  *
80  * The following are the known directory entries, without the quotes:
81  * "assembly:NAME"  An assembly with the name NAME, assembly is in the payload
82  * "config:NAME"    A configuration file (usually file.dll.config) in the payload that is
83  *                  loaded as the config file for an assembly
84  * "systemconfig:"  Treats as a Mono system configuration, payload contains the config file.
85  * "options:"       The payload contains command line options to initialize Mono, as if you 
86                     had set them on MONO_ENV_OPTIONS
87  * "config_dir:DIR" Configures the MONO_PATH to point to point to DIR
88  * "machineconfig:" The payload contains the machine.config file to use at runtime
89  * "env:"           Sets the environment variable to the value encoded in the payload
90  *                  payload contains: 1-byte lenght for the \0 terminated variable,
91  *                  followed by the value.
92  * "library:NAME"   Bundled dynamic library NAME, payload contains the dynamic library
93  */
94 #define STREAM_INT(x) GUINT32_TO_LE((*(uint32_t*)x))
95 #define STREAM_LONG(x) GUINT64_TO_LE((*(uint64_t*)x))
96
97 /**
98  * Loads a chunk of data from the file pointed to by the
99  * @fd starting at the file offset @offset for @size bytes
100  * and returns an allocated version of that string, or NULL
101  * on error.
102  */
103 static char *
104 load_from_region (int fd, uint64_t offset, uint64_t size)
105 {
106         char *buffer;
107         off_t loc;
108         int status;
109         
110         do {
111                 loc = lseek (fd, offset, SEEK_SET);
112         } while (loc == -1 && errno == EINTR);
113         if (loc == -1)
114                 return NULL;
115         buffer = g_malloc (size + 1);
116         if (buffer == NULL)
117                 return NULL;
118         buffer [size] = 0;
119         do {
120                 status = read (fd, buffer, size);
121         } while (status == -1 && errno == EINTR);
122         if (status == -1){
123                 g_free (buffer);
124                 return NULL;
125         }
126         return buffer;
127 }
128
129 /* Did we initialize the temporary directory for dynamic libraries */
130 static int bundle_save_library_initialized;
131
132 /* List of bundled libraries we unpacked */
133 static GSList *bundle_library_paths;
134
135 /* Directory where we unpacked dynamic libraries */
136 static char *bundled_dylibrary_directory;
137
138 static void
139 delete_bundled_libraries (void)
140 {
141         GSList *list;
142
143         for (list = bundle_library_paths; list != NULL; list = list->next){
144                 unlink (list->data);
145         }
146         rmdir (bundled_dylibrary_directory);
147 }
148
149 static void
150 bundle_save_library_initialize (void)
151 {
152         bundle_save_library_initialized = 1;
153         char *path = g_build_filename (g_get_tmp_dir (), "mono-bundle-XXXXXX", NULL);
154         bundled_dylibrary_directory = g_mkdtemp (path);
155         g_free (path);
156         if (bundled_dylibrary_directory == NULL)
157                 return;
158         atexit (delete_bundled_libraries);
159 }
160
161 static void
162 save_library (int fd, uint64_t offset, uint64_t size, const char *destfname)
163 {
164         MonoDl *lib;
165         char *file, *buffer, *err, *internal_path;
166         if (!bundle_save_library_initialized)
167                 bundle_save_library_initialize ();
168         
169         file = g_build_filename (bundled_dylibrary_directory, destfname, NULL);
170         buffer = load_from_region (fd, offset, size);
171         g_file_set_contents (file, buffer, size, NULL);
172
173         lib = mono_dl_open (file, MONO_DL_LAZY, &err);
174         if (lib == NULL){
175                 fprintf (stderr, "Error loading shared library: %s %s\n", file, err);
176                 exit (1);
177         }
178         // Register the name with "." as this is how it will be found when embedded
179         internal_path = g_build_filename (".", destfname, NULL);
180         mono_loader_register_module (internal_path, lib);
181         g_free (internal_path);
182         bundle_library_paths = g_slist_append (bundle_library_paths, file);
183         
184         g_free (buffer);
185 }
186
187 static gboolean
188 probe_embedded (const char *program, int *ref_argc, char **ref_argv [])
189 {
190         MonoBundledAssembly last = { NULL, 0, 0 };
191         char sigbuffer [16+sizeof (uint64_t)];
192         gboolean status = FALSE;
193         uint64_t directory_location;
194         off_t sigstart, baseline = 0;
195         uint64_t directory_size;
196         char *directory, *p;
197         int items, i;
198         unsigned char *mapaddress = NULL;
199         void *maphandle = NULL;
200         GArray *assemblies;
201         char *entry_point = NULL;
202         char **new_argv;
203         int j;
204
205         int fd = open (program, O_RDONLY);
206         if (fd == -1)
207                 return FALSE;
208         if ((sigstart = lseek (fd, -24, SEEK_END)) == -1)
209                 goto doclose;
210         if (read (fd, sigbuffer, sizeof (sigbuffer)) == -1)
211                 goto doclose;
212         if (memcmp (sigbuffer+sizeof(uint64_t), "xmonkeysloveplay", 16) != 0)
213                 goto doclose;
214         directory_location = GUINT64_FROM_LE ((*(uint64_t *) &sigbuffer [0]));
215         if (lseek (fd, directory_location, SEEK_SET) == -1)
216                 goto doclose;
217         directory_size = sigstart-directory_location;
218         directory = g_malloc (directory_size);
219         if (directory == NULL)
220                 goto doclose;
221         if (read (fd, directory, directory_size) == -1)
222                 goto dofree;
223
224         items = STREAM_INT (directory);
225         p = directory+4;
226
227         assemblies = g_array_new (0, 0, sizeof (MonoBundledAssembly*));
228         for (i = 0; i < items; i++){
229                 char *kind;
230                 int strsize = STREAM_INT (p);
231                 uint64_t offset, item_size;
232                 kind = p+4;
233                 p += 4 + strsize;
234                 offset = STREAM_LONG(p);
235                 p += 8;
236                 item_size = STREAM_INT (p);
237                 p += 4;
238                 
239                 if (mapaddress == NULL){
240                         mapaddress = mono_file_map (directory_location-offset, MONO_MMAP_READ | MONO_MMAP_PRIVATE, fd, offset, &maphandle);
241                         if (mapaddress == NULL){
242                                 perror ("Error mapping file");
243                                 exit (1);
244                         }
245                         baseline = offset;
246                 }
247                 if (strncmp (kind, "assembly:", strlen ("assembly:")) == 0){
248                         char *aname = kind + strlen ("assembly:");
249                         MonoBundledAssembly mba = { aname, mapaddress + offset - baseline, item_size }, *ptr;
250                         ptr = g_new (MonoBundledAssembly, 1);
251                         memcpy (ptr, &mba, sizeof (MonoBundledAssembly));
252                         g_array_append_val  (assemblies, ptr);
253                         if (entry_point == NULL)
254                                 entry_point = aname;
255                 } else if (strncmp (kind, "config:", strlen ("config:")) == 0){
256                         char *config = kind + strlen ("config:");
257                         char *aname = g_strdup (config);
258                         aname [strlen(aname)-strlen(".config")] = 0;
259                         mono_register_config_for_assembly (aname, load_from_region (fd, offset, item_size));
260                 } else if (strncmp (kind, "systemconfig:", strlen ("systemconfig:")) == 0){
261                         mono_config_parse_memory (load_from_region (fd, offset, item_size));
262                 } else if (strncmp (kind, "options:", strlen ("options:")) == 0){
263                         mono_parse_options_from (load_from_region (fd, offset, item_size), ref_argc, ref_argv);
264                 } else if (strncmp (kind, "config_dir:", strlen ("config_dir:")) == 0){
265                         mono_set_dirs (getenv ("MONO_PATH"), load_from_region (fd, offset, item_size));
266                 } else if (strncmp (kind, "machineconfig:", strlen ("machineconfig:")) == 0) {
267                         mono_register_machine_config (load_from_region (fd, offset, item_size));
268                 } else if (strncmp (kind, "env:", strlen ("env:")) == 0){
269                         char *data = load_from_region (fd, offset, item_size);
270                         uint8_t count = *data++;
271                         char *value = data + count + 1;
272                         g_setenv (data, value, FALSE);
273                 } else if (strncmp (kind, "library:", strlen ("library:")) == 0){
274                         save_library (fd, offset, item_size, kind + strlen ("library:"));
275                 } else {
276                         fprintf (stderr, "Unknown stream on embedded package: %s\n", kind);
277                         exit (1);
278                 }
279         }
280         g_array_append_val (assemblies, last);
281
282         mono_register_bundled_assemblies ((const MonoBundledAssembly **) assemblies->data);
283         new_argv = g_new (char *, (*ref_argc)+1);
284         new_argv [0] = (*ref_argv)[0];
285         new_argv [1] = entry_point;
286         for (j = 1; j < *ref_argc; j++)
287                 new_argv [j+1] = (*ref_argv)[j];
288         *ref_argv = new_argv;
289         (*ref_argc)++;
290         
291         return TRUE;
292         
293 dofree:
294         g_free (directory);
295 doclose:
296         if (!status)
297                 close (fd);
298         return status;
299 }
300
301 #ifdef HOST_WIN32
302
303 #include <shellapi.h>
304
305 int
306 main (void)
307 {
308         TCHAR szFileName[MAX_PATH];
309         int argc;
310         gunichar2** argvw;
311         gchar** argv;
312         int i;
313         DWORD count;
314         
315         argvw = CommandLineToArgvW (GetCommandLine (), &argc);
316         argv = g_new0 (gchar*, argc + 1);
317         for (i = 0; i < argc; i++)
318                 argv [i] = g_utf16_to_utf8 (argvw [i], -1, NULL, NULL, NULL);
319         argv [argc] = NULL;
320
321         LocalFree (argvw);
322
323         if ((count = GetModuleFileName (NULL, szFileName, MAX_PATH)) != 0){
324                 char *entry = g_utf16_to_utf8 (szFileName, count, NULL, NULL, NULL);
325                 probe_embedded (entry, &argc, &argv);
326         }
327
328         return mono_main_with_options  (argc, argv);
329 }
330
331 #else
332
333 int
334 main (int argc, char* argv[])
335 {
336         mono_build_date = build_date;
337
338         probe_embedded (argv [0], &argc, &argv);
339         return mono_main_with_options (argc, argv);
340 }
341
342 #endif