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