X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmain.c;h=77f8bc9578c9cea42168c5695dd3dca43d53ff6c;hb=bf95567e69496ada5f4c2cc77ca28e4c44b2fb55;hp=4ef344ecdd9b0f1b5e005e5e9a1f6b258744dadb;hpb=1076f878926aad85bd919e684caeeeb6d44a1cc1;p=mono.git diff --git a/mono/mini/main.c b/mono/mini/main.c index 4ef344ecdd9..77f8bc9578c 100644 --- a/mono/mini/main.c +++ b/mono/mini/main.c @@ -1,10 +1,35 @@ +/* + * main.c: The main entry point for the mono executable + * + * The main entry point does a few things: + * + * * It probes whether the executable has a bundle appended + * at the end, and if so, registers the various bundled + * resources with Mono and executes the contained bundle + * + * * Parses the MONO_ENV_OPTIONS variable to treat the + * contents of the variable as command line arguments for + * the mono runtime + * + * * Launches Mono, by calling mono_main. + */ #include +#include +#include +#include +#include +#include #include "mini.h" -#ifndef HOST_WIN32 -#ifndef BUILDVER_INCLUDED -#include "buildver-boehm.h" +#ifdef HAVE_UNISTD_H +# include #endif +#ifdef HOST_WIN32 +# include +#else +# ifndef BUILDVER_INCLUDED +# include "buildver-boehm.h" +# endif #endif /* @@ -20,6 +45,258 @@ mono_main_with_options (int argc, char *argv []) return mono_main (argc, argv); } +/* + * The Mono executable can initialize itself from a payload attached + * at the end of the main program. The payload contains the + * main assembly, one or more managed assemblies, configuration + * files and other assets that are used instead of launching a + * program from the command line. + * + * The startup sequence probes for a magical signature at the end of + * the executable, if the 16 characters "xmonkeysloveplay" are found, + * the code expects the 64-bits just before it to contain an offset + * within the executable with a directory of assets. + * + * All pointers in the file format are encoded as little-endian values + * + * The format of the file is thus: + * + * Location Content + * -------- ------- + * lenght-16 Optional "xmonkeysloveplay", indicating that a + * bundled payload is contained in the executable. + * length-24 pointer to the directory in the file, address DIR + * + * DIR 32-bit value with the number of entries in the directory + * DIR+4 First directory entry. + * + * Each directory entry is made up of: + * 4-bytes uint32_t containing the size of a string (STR) + * STRING UTF8 encoded and \0 terminated string + * 8-bytes uint64_t offset in the file with the payload associated with STRING + * 4-bytes uint32_t size of the asset + * + * The following are the known directory entries, without the quotes: + * "assembly:NAME" An assembly with the name NAME, assembly is in the payload + * "config:NAME" A configuration file (usually file.dll.config) in the payload that is + * loaded as the config file for an assembly + * "systemconfig:" Treats as a Mono system configuration, payload contains the config file. + * "options:" The payload contains command line options to initialize Mono, as if you + had set them on MONO_ENV_OPTIONS + * "config_dir:DIR" Configures the MONO_PATH to point to point to DIR + * "machineconfig:" The payload contains the machine.config file to use at runtime + * "env:" Sets the environment variable to the value encoded in the payload + * payload contains: 1-byte lenght for the \0 terminated variable, + * followed by the value. + * "library:NAME" Bundled dynamic library NAME, payload contains the dynamic library + */ +#define STREAM_INT(x) GUINT32_TO_LE((*(uint32_t*)x)) +#define STREAM_LONG(x) GUINT64_TO_LE((*(uint64_t*)x)) + +/** + * Loads a chunk of data from the file pointed to by the + * @fd starting at the file offset @offset for @size bytes + * and returns an allocated version of that string, or NULL + * on error. + */ +static char * +load_from_region (int fd, uint64_t offset, uint64_t size) +{ + char *buffer; + off_t loc; + int status; + + do { + loc = lseek (fd, offset, SEEK_SET); + } while (loc == -1 && errno == EINTR); + if (loc == -1) + return NULL; + buffer = g_malloc (size + 1); + if (buffer == NULL) + return NULL; + buffer [size] = 0; + do { + status = read (fd, buffer, size); + } while (status == -1 && errno == EINTR); + if (status == -1){ + g_free (buffer); + return NULL; + } + return buffer; +} + +/* Did we initialize the temporary directory for dynamic libraries */ +static int bundle_save_library_initialized; + +/* List of bundled libraries we unpacked */ +static GSList *bundle_library_paths; + +/* Directory where we unpacked dynamic libraries */ +static char *bundled_dylibrary_directory; + +static void +delete_bundled_libraries () +{ + GSList *list; + + for (list = bundle_library_paths; list != NULL; list = list->next){ + unlink (list->data); + } + rmdir (bundled_dylibrary_directory); +} + +static void +bundle_save_library_initialize () +{ + bundle_save_library_initialized = 1; + char *path = g_build_filename (g_get_tmp_dir (), "mono-bundle-XXXXXX", NULL); + bundled_dylibrary_directory = g_mkdtemp (path); + /* don't free path - mkdtemp modifies it in place, and bundled_dylibrary_directory is an alias of it */ + if (bundled_dylibrary_directory == NULL) + return; + atexit (delete_bundled_libraries); +} + +static void +save_library (int fd, uint64_t offset, uint64_t size, const char *destfname) +{ + MonoDl *lib; + char *file, *buffer, *err, *internal_path; + if (!bundle_save_library_initialized) + bundle_save_library_initialize (); + + file = g_build_filename (bundled_dylibrary_directory, destfname, NULL); + buffer = load_from_region (fd, offset, size); + g_file_set_contents (file, buffer, size, NULL); + + lib = mono_dl_open (file, MONO_DL_LAZY, &err); + if (lib == NULL){ + fprintf (stderr, "Error loading shared library: %s %s\n", file, err); + exit (1); + } + // Register the name with "." as this is how it will be found when embedded + internal_path = g_build_filename (".", destfname, NULL); + mono_loader_register_module (internal_path, lib); + g_free (internal_path); + bundle_library_paths = g_slist_append (bundle_library_paths, file); + + g_free (buffer); +} + +static gboolean +probe_embedded (const char *program, int *ref_argc, char **ref_argv []) +{ + MonoBundledAssembly last = { NULL, 0, 0 }; + char sigbuffer [16+sizeof (uint64_t)]; + gboolean status = FALSE; + uint64_t directory_location; + off_t sigstart, baseline = 0; + uint64_t directory_size; + char *directory, *p; + int items, i; + unsigned char *mapaddress = NULL; + void *maphandle = NULL; + GArray *assemblies; + char *entry_point = NULL; + char **new_argv; + int j; + + int fd = open (program, O_RDONLY); + if (fd == -1) + return FALSE; + if ((sigstart = lseek (fd, -24, SEEK_END)) == -1) + goto doclose; + if (read (fd, sigbuffer, sizeof (sigbuffer)) == -1) + goto doclose; + if (memcmp (sigbuffer+sizeof(uint64_t), "xmonkeysloveplay", 16) != 0) + goto doclose; + directory_location = GUINT64_FROM_LE ((*(uint64_t *) &sigbuffer [0])); + if (lseek (fd, directory_location, SEEK_SET) == -1) + goto doclose; + directory_size = sigstart-directory_location; + directory = g_malloc (directory_size); + if (directory == NULL) + goto doclose; + if (read (fd, directory, directory_size) == -1) + goto dofree; + + items = STREAM_INT (directory); + p = directory+4; + + assemblies = g_array_new (0, 0, sizeof (MonoBundledAssembly*)); + for (i = 0; i < items; i++){ + char *kind; + int strsize = STREAM_INT (p); + uint64_t offset, item_size; + kind = p+4; + p += 4 + strsize; + offset = STREAM_LONG(p); + p += 8; + item_size = STREAM_INT (p); + p += 4; + + if (mapaddress == NULL){ + mapaddress = mono_file_map (directory_location-offset, MONO_MMAP_READ | MONO_MMAP_PRIVATE, fd, offset, &maphandle); + if (mapaddress == NULL){ + perror ("Error mapping file"); + exit (1); + } + baseline = offset; + } + if (strncmp (kind, "assembly:", strlen ("assembly:")) == 0){ + char *aname = kind + strlen ("assembly:"); + MonoBundledAssembly mba = { aname, mapaddress + offset - baseline, item_size }, *ptr; + ptr = g_new (MonoBundledAssembly, 1); + memcpy (ptr, &mba, sizeof (MonoBundledAssembly)); + g_array_append_val (assemblies, ptr); + if (entry_point == NULL) + entry_point = aname; + } else if (strncmp (kind, "config:", strlen ("config:")) == 0){ + char *config = kind + strlen ("config:"); + char *aname = g_strdup (config); + aname [strlen(aname)-strlen(".config")] = 0; + mono_register_config_for_assembly (aname, load_from_region (fd, offset, item_size)); + } else if (strncmp (kind, "systemconfig:", strlen ("systemconfig:")) == 0){ + mono_config_parse_memory (load_from_region (fd, offset, item_size)); + } else if (strncmp (kind, "options:", strlen ("options:")) == 0){ + mono_parse_options_from (load_from_region (fd, offset, item_size), ref_argc, ref_argv); + } else if (strncmp (kind, "config_dir:", strlen ("config_dir:")) == 0){ + mono_set_dirs (getenv ("MONO_PATH"), load_from_region (fd, offset, item_size)); + } else if (strncmp (kind, "machineconfig:", strlen ("machineconfig:")) == 0) { + mono_register_machine_config (load_from_region (fd, offset, item_size)); + } else if (strncmp (kind, "env:", strlen ("env:")) == 0){ + char *data = load_from_region (fd, offset, item_size); + uint8_t count = *data++; + char *value = data + count + 1; + g_setenv (data, value, FALSE); + } else if (strncmp (kind, "library:", strlen ("library:")) == 0){ + save_library (fd, offset, item_size, kind + strlen ("library:")); + } else { + fprintf (stderr, "Unknown stream on embedded package: %s\n", kind); + exit (1); + } + } + g_array_append_val (assemblies, last); + + mono_register_bundled_assemblies ((const MonoBundledAssembly **) assemblies->data); + new_argv = g_new (char *, (*ref_argc)+1); + new_argv [0] = (*ref_argv)[0]; + new_argv [1] = entry_point; + for (j = 1; j < *ref_argc; j++) + new_argv [j+1] = (*ref_argv)[j]; + *ref_argv = new_argv; + (*ref_argc)++; + + return TRUE; + +dofree: + g_free (directory); +doclose: + if (!status) + close (fd); + return status; +} + #ifdef HOST_WIN32 #include @@ -27,11 +304,13 @@ mono_main_with_options (int argc, char *argv []) int main (void) { + TCHAR szFileName[MAX_PATH]; int argc; gunichar2** argvw; gchar** argv; int i; - + DWORD count; + argvw = CommandLineToArgvW (GetCommandLine (), &argc); argv = g_new0 (gchar*, argc + 1); for (i = 0; i < argc; i++) @@ -40,6 +319,11 @@ main (void) LocalFree (argvw); + if ((count = GetModuleFileName (NULL, szFileName, MAX_PATH)) != 0){ + char *entry = g_utf16_to_utf8 (szFileName, count, NULL, NULL, NULL); + probe_embedded (entry, &argc, &argv); + } + return mono_main_with_options (argc, argv); } @@ -49,7 +333,8 @@ int main (int argc, char* argv[]) { mono_build_date = build_date; - + + probe_embedded (argv [0], &argc, &argv); return mono_main_with_options (argc, argv); }