diff options
Diffstat (limited to 'src/emacs.c')
-rw-r--r-- | src/emacs.c | 304 |
1 files changed, 252 insertions, 52 deletions
diff --git a/src/emacs.c b/src/emacs.c index 221b074afc9..9c88b6e3f17 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -118,6 +118,9 @@ extern char etext; #include <sys/resource.h> #endif +#include "pdumper.h" +#include "epaths.h" + static const char emacs_version[] = PACKAGE_VERSION; static const char emacs_copyright[] = COPYRIGHT; static const char emacs_bugreport[] = PACKAGE_BUGREPORT; @@ -130,19 +133,9 @@ Lisp_Object empty_unibyte_string, empty_multibyte_string; Lisp_Object Vlibrary_cache; #endif -/* Set after Emacs has started up the first time. - Prevents reinitialization of the Lisp world and keymaps - on subsequent starts. */ +struct gflags gflags; bool initialized; -#ifndef CANNOT_DUMP -/* Set to true if this instance of Emacs might dump. */ -# ifndef DOUG_LEA_MALLOC -static -# endif -bool might_dump; -#endif - /* If true, Emacs should not attempt to use a window-specific code, but instead should use the virtual terminal under which it was started. */ bool inhibit_window_system; @@ -519,8 +512,7 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd) etc_exists = Ffile_exists_p (tem); if (!NILP (etc_exists)) { - Vinstallation_directory - = Ffile_name_as_directory (dir); + Vinstallation_directory = Ffile_name_as_directory (dir); break; } } @@ -545,8 +537,7 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd) if (!NILP (etc_exists)) { tem = Fexpand_file_name (build_string (".."), dir); - Vinstallation_directory - = Ffile_name_as_directory (tem); + Vinstallation_directory = Ffile_name_as_directory (tem); break; } } @@ -659,6 +650,43 @@ argmatch (char **argv, int argc, const char *sstr, const char *lstr, } } +static bool +string_starts_with_p (const char* string, const char* prefix) +{ + return strncmp (string, prefix, strlen (prefix)) == 0; +} + +/* Return the value of GNU-style long argument ARGUMENT if given on + command line. ARGUMENT must begin with "-". If ARGUMENT is not + given, return NULL. */ +static char * +find_argument (const char *argument, int argc, char **argv) +{ + char *found = NULL; + int i; + + eassert (argument[0] == '-'); + + for (i = 1; i < argc; ++i) + if (string_starts_with_p (argv[i], argument) && + ((argv[i] + strlen (argument))[0] == '=' || + (argv[i] + strlen (argument))[0] == '\0')) + { + int j = i; + found = argv[j++] + strlen (argument); + if (*found == '=') + ++found; + else if (i < argc) + found = argv[j++]; + else + fatal ("no argument given for %s", argument); + break; + } + else if (strcmp (argv[i], "--") == 0) + break; + return found; +} + /* Close standard output and standard error, reporting any write errors as best we can. This is intended for use with atexit. */ static void @@ -677,6 +705,114 @@ close_output_streams (void) _exit (EXIT_FAILURE); } +#ifdef HAVE_PDUMPER + +static const char * +dump_error_to_string (enum pdumper_load_result result) +{ + switch (result) + { + case PDUMPER_LOAD_SUCCESS: + return "success"; + case PDUMPER_LOAD_OOM: + return "out of memory"; + case PDUMPER_NOT_LOADED: + return "not loaded"; + case PDUMPER_LOAD_FILE_NOT_FOUND: + return "could not open file"; + case PDUMPER_LOAD_BAD_FILE_TYPE: + return "not a dump file"; + case PDUMPER_LOAD_FAILED_DUMP: + return "dump file is result of failed dump attempt"; + case PDUMPER_LOAD_VERSION_MISMATCH: + return "not built for this Emacs executable"; + default: + return "generic error"; + } +} + +#define PDUMP_FILE_ARG "--dump-file" + +static enum pdumper_load_result +load_pdump (int argc, char **argv) +{ + const char *const suffix = ".pdmp"; + const char *const argv0_base = "emacs"; + enum pdumper_load_result result; +#ifdef WINDOWSNT + size_t argv0_len; +#endif + + /* TODO: maybe more thoroughly scrub process environment in order to + make this use case (loading a pdumper image in an unexeced emacs) + possible? Right now, we assume that things we don't touch are + zero-initialized, and in an unexeced Emacs, this assumption + doesn't hold. */ + if (initialized) + fatal ("cannot load pdumper image in unexeced Emacs"); + + /* Look for an explicitly-specified dump file. */ + const char *path_exec = PATH_EXEC; + char *dump_file = find_argument (PDUMP_FILE_ARG, argc, argv); + + result = PDUMPER_NOT_LOADED; + if (dump_file) + result = pdumper_load (dump_file); + + if (dump_file && result != PDUMPER_LOAD_SUCCESS) + fatal ("could not load dump file \"%s\": %s", + dump_file, dump_error_to_string (result)); + + if (result == PDUMPER_LOAD_SUCCESS) + goto out; + + /* Look for a dump file in the same directory as the executable; it + should have the same basename. */ + + dump_file = alloca (strlen (argv[0]) + strlen (suffix) + 1); +#ifdef WINDOWSNT + /* Remove the .exe extension if present. */ + argv0_len = strlen (argv[0]); + if (argv0_len >= 4 && c_strcasecmp (argv[0] + argv0_len - 4, ".exe") == 0) + sprintf (dump_file, "%.*s%s", argv0_len - 4, argv[0], suffix); + else +#endif + sprintf (dump_file, "%s%s", argv[0], suffix); + + result = pdumper_load (dump_file); + if (result == PDUMPER_LOAD_SUCCESS) + goto out; + + if (result != PDUMPER_LOAD_FILE_NOT_FOUND) + fatal ("could not load dump file \"%s\": %s", + dump_file, dump_error_to_string (result)); + + /* Finally, look for "emacs.pdmp" in PATH_EXEC. We hardcode + "emacs" in "emacs.pdmp" so that the Emacs binary still works + if the user copies and renames it. + + FIXME: this doesn't work with emacs-XX.YY.ZZ.pdmp versioned files. */ +#ifdef WINDOWSNT + /* On MS-Windows, PATH_EXEC normally starts with a literal + "%emacs_dir%", so it will never work without some tweaking. */ + path_exec = w32_relocate (path_exec); +#endif + dump_file = alloca (strlen (path_exec) + + 1 + + strlen (argv0_base) + + strlen (suffix) + + 1); + sprintf (dump_file, "%s%c%s%s", + path_exec, DIRECTORY_SEP, argv0_base, suffix); + result = pdumper_load (dump_file); + if (result != PDUMPER_LOAD_SUCCESS) + dump_file = NULL; + + out: + return result; +} +#endif /* HAVE_PDUMPER */ + /* ARGSUSED */ int main (int argc, char **argv) @@ -686,7 +822,6 @@ main (int argc, char **argv) void *stack_bottom_variable; bool do_initial_setlocale; - bool dumping; int skip_args = 0; bool no_loadup = false; char *junk = 0; @@ -702,25 +837,62 @@ main (int argc, char **argv) /* Record (approximately) where the stack begins. */ stack_bottom = (char *) &stack_bottom_variable; -#ifndef CANNOT_DUMP - dumping = !initialized && (strcmp (argv[argc - 1], "dump") == 0 - || strcmp (argv[argc - 1], "bootstrap") == 0); -#else - dumping = false; + const char *dump_mode = NULL; + const char *temacs = find_argument ("--temacs", argc, argv); +#ifdef HAVE_PDUMPER + bool attempt_load_pdump = false; #endif - argc = maybe_disable_address_randomization (dumping, argc, argv); - + /* Look for this argument first, before any heap allocation, so we + can set heap flags properly if we're going to unexec. */ + if (!initialized && temacs) + { #ifndef CANNOT_DUMP - might_dump = !initialized; - -# ifdef GNU_LINUX - if (!initialized) + if (strcmp (temacs, "dump") == 0 || + strcmp (temacs, "bootstrap") == 0) + gflags.will_dump_with_unexec_ = true; +#endif +#ifdef HAVE_PDUMPER + if (strcmp (temacs, "pdump") == 0 || + strcmp (temacs, "pbootstrap") == 0) + gflags.will_dump_with_pdumper_ = true; +#endif +#if defined (HAVE_PDUMPER) || !defined (CANNOT_DUMP) + if (strcmp (temacs, "bootstrap") == 0 || + strcmp (temacs, "pbootstrap") == 0) + gflags.will_bootstrap_ = true; + gflags.will_dump_ = + will_dump_with_pdumper_p () || + will_dump_with_unexec_p (); + if (will_dump_p ()) + dump_mode = temacs; +#endif + if (!dump_mode) + fatal ("Invalid temacs mode '%s'", temacs); + } + else if (temacs) { - char *heap_start = my_heap_start (); - heap_bss_diff = heap_start - max (my_endbss, my_endbss_static); + fatal ("--temacs not supported for unexeced emacs"); } -# endif + else if (initialized) + { +#ifdef HAVE_PDUMPER + if (find_argument (PDUMP_FILE_ARG, argc, argv)) + fatal ("%s not supported in unexeced emacs", PDUMP_FILE_ARG); +#endif + } + else + { + eassert (!initialized); + eassert (!temacs); +#ifdef PDUMP_FILE_ARG + attempt_load_pdump = true; +#endif + } + +#ifndef CANNOT_DUMP + if (!will_dump_with_unexec_p ()) + gflags.will_not_unexec_ = true; #endif #if defined WINDOWSNT || defined HAVE_NTGUI @@ -742,6 +914,22 @@ main (int argc, char **argv) w32_init_main_thread (); #endif +#ifdef HAVE_PDUMPER + if (attempt_load_pdump) + load_pdump (argc, argv); +#endif + + argc = maybe_disable_address_randomization ( + will_dump_with_unexec_p (), argc, argv); + +#if defined (GNU_LINUX) && !defined (CANNOT_DUMP) + if (!initialized) + { + char *heap_start = my_heap_start (); + heap_bss_diff = heap_start - max (my_endbss, my_endbss_static); + } +#endif + #ifdef RUN_TIME_REMAP if (initialized) run_time_remap (argv[0]); @@ -850,10 +1038,7 @@ main (int argc, char **argv) frames. */ int extra = (30 * 1000) * 50; - bool try_to_grow_stack = true; -#ifndef CANNOT_DUMP - try_to_grow_stack = !noninteractive || initialized; -#endif + bool try_to_grow_stack = !noninteractive || initialized; if (try_to_grow_stack) { @@ -1184,17 +1369,15 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem #if defined HAVE_PTHREAD && !defined SYSTEM_MALLOC \ && !defined DOUG_LEA_MALLOC && !defined HYBRID_MALLOC -# ifndef CANNOT_DUMP /* Do not make gmalloc thread-safe when creating bootstrap-emacs, as that causes an infinite recursive loop with FreeBSD. See Bug#14569. The part of this bug involving Cygwin is no longer relevant, now that Cygwin defines HYBRID_MALLOC. */ - if (!noninteractive || initialized) -# endif + if (!noninteractive || !will_dump_p ()) malloc_enable_thread (); #endif - init_signals (dumping); + init_signals (); noninteractive1 = noninteractive; @@ -1204,7 +1387,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem { init_alloc_once (); init_threads_once (); - init_obarray (); + init_obarray_once (); init_eval_once (); init_charset_once (); init_coding_once (); @@ -1242,7 +1425,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem /* Before init_window_once, because it sets up the Vcoding_system_hash_table. */ syms_of_coding (); /* This should be after syms_of_fileio. */ - + init_frame_once (); /* Before init_window_once. */ init_window_once (); /* Init the window system. */ #ifdef HAVE_WINDOW_SYSTEM init_fringe_once (); /* Swap bitmaps if necessary. */ @@ -1282,7 +1465,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem bool module_assertions = argmatch (argv, argc, "-module-assertions", "--module-assertions", 15, NULL, &skip_args); - if (dumping && module_assertions) + if (will_dump_p () && module_assertions) { fputs ("Module assertions are not supported during dumping\n", stderr); exit (1); @@ -1419,7 +1602,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem /* egetenv is a pretty low-level facility, which may get called in many circumstances; it seems flimsy to put off initializing it until calling init_callproc. Do not do it when dumping. */ - if (! dumping) + if (!will_dump_p ()) set_initial_environment (); #ifdef WINDOWSNT @@ -1433,7 +1616,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem variables from the parent process without modifications from Emacs. */ init_environment (argv); - init_ntproc (dumping); /* must precede init_editfns. */ + init_ntproc (will_dump_p ()); /* must precede init_editfns. */ #endif /* AIX crashes are reported in system versions 3.2.3 and 3.2.4 @@ -1445,7 +1628,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem #endif /* Init buffer storage and default directory of main buffer. */ - init_buffer (initialized); + init_buffer (); init_callproc_1 (); /* Must precede init_cmdargs and init_sys_modes. */ @@ -1620,6 +1803,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem syms_of_threads (); syms_of_profiler (); + syms_of_pdumper (); #ifdef HAVE_JSON syms_of_json (); @@ -1650,7 +1834,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem init_charset (); /* This calls putenv and so must precede init_process_emacs. */ - init_timefns (dumping); + init_timefns (); /* This sets Voperating_system_release, which init_process_emacs uses. */ init_editfns (); @@ -1669,10 +1853,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem init_process_emacs (sockfd); init_keyboard (); /* This too must precede init_sys_modes. */ - if (!noninteractive) - init_display (); /* Determine terminal type. Calls init_sys_modes. */ + init_display (); /* Determine terminal type. Calls init_sys_modes. */ #if HAVE_W32NOTIFY - else + if (noninteractive) init_crit (); /* w32notify.c needs this in batch mode. */ #endif /* HAVE_W32NOTIFY */ init_xdisp (); @@ -1716,7 +1899,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem moncontrol (0); #endif - initialized = 1; + initialized = true; + + if (dump_mode) + Vdump_mode = build_string (dump_mode); /* Enter editor command loop. This never returns. */ Frecursive_edit (); @@ -2166,8 +2352,11 @@ You must run Emacs in batch mode in order to dump it. */) if (! noninteractive) error ("Dumping Emacs works only in batch mode"); - if (!might_dump) - error ("Emacs can be dumped only once"); + if (dumped_with_unexec_p ()) + error ("Emacs can be dumped using unexec only once"); + + if (definitely_will_not_unexec_p ()) + error ("This Emacs instance was not started in temacs mode"); #if defined GNU_LINUX && !defined CANNOT_DUMP @@ -2231,12 +2420,19 @@ You must run Emacs in batch mode in order to dump it. */) #endif /* not WINDOWSNT */ #endif /* not SYSTEM_MALLOC and not HYBRID_MALLOC */ + struct gflags old_gflags = gflags; + gflags.will_dump_ = false; + gflags.will_dump_with_unexec_ = false; + gflags.dumped_with_unexec_ = true; + alloc_unexec_pre (); unexec (SSDATA (filename), !NILP (symfile) ? SSDATA (symfile) : 0); alloc_unexec_post (); + gflags = old_gflags; + #ifdef WINDOWSNT Vlibrary_cache = Qnil; #endif @@ -2250,6 +2446,7 @@ You must run Emacs in batch mode in order to dump it. */) } #endif /* not CANNOT_DUMP */ + #if HAVE_SETLOCALE /* Recover from setlocale (LC_ALL, ""). */ @@ -2585,7 +2782,7 @@ Don't rely on it for testing whether a feature you want to use is available. */ Vsystem_configuration_features = build_string (EMACS_CONFIG_FEATURES); DEFVAR_BOOL ("noninteractive", noninteractive1, - doc: /* Non-nil means Emacs is running without interactive terminal. */); + doc: /* Non-nil means Emacs is running without interactive terminal. */); DEFVAR_LISP ("kill-emacs-hook", Vkill_emacs_hook, doc: /* Hook run when `kill-emacs' is called. @@ -2670,6 +2867,9 @@ component .BUILD is present. This is now stored separately in doc: /* Address of mailing list for GNU Emacs bugs. */); Vreport_emacs_bug_address = build_string (emacs_bugreport); + DEFVAR_LISP ("dump-mode", Vdump_mode, + doc: /* Non-nil when Emacs is dumping itself. */); + DEFVAR_LISP ("dynamic-library-alist", Vdynamic_library_alist, doc: /* Alist of dynamic libraries vs external files implementing them. Each element is a list (LIBRARY FILE...), where the car is a symbol |