Use flatteningization in package-data-list
[sbcl.git] / src / runtime / save.c
blob2bf784df7b8dea2791b55907500c968781f0cdae
1 /*
2 * This software is part of the SBCL system. See the README file for
3 * more information.
5 * This software is derived from the CMU CL system, which was
6 * written at Carnegie Mellon University and released into the
7 * public domain. The software is in the public domain and is
8 * provided with absolutely no warranty. See the COPYING and CREDITS
9 * files for more information.
12 #ifndef LISP_FEATURE_WIN32
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #endif
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <sys/file.h>
21 #include "sbcl.h"
22 #if defined(LISP_FEATURE_WIN32) && defined(LISP_FEATURE_SB_THREAD)
23 #include "pthreads_win32.h"
24 #else
25 #include <signal.h>
26 #endif
27 #include "runtime.h"
28 #include "os.h"
29 #include "core.h"
30 #include "globals.h"
31 #include "save.h"
32 #include "dynbind.h"
33 #include "lispregs.h"
34 #include "validate.h"
35 #include "gc-internal.h"
36 #include "thread.h"
38 #include "genesis/static-symbols.h"
39 #include "genesis/symbol.h"
41 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
42 # include <zlib.h>
43 #endif
45 /* write_runtime_options uses a simple serialization scheme that
46 * consists of one word of magic, one word indicating whether options
47 * are actually saved, and one word per struct field. */
48 static void
49 write_runtime_options(FILE *file, struct runtime_options *options)
51 size_t optarray[RUNTIME_OPTIONS_WORDS];
53 memset(&optarray, 0, sizeof(optarray));
54 optarray[0] = RUNTIME_OPTIONS_MAGIC;
56 if (options != NULL) {
57 /* optarray[1] is a flag indicating that options are present */
58 optarray[1] = 1;
59 optarray[2] = options->dynamic_space_size;
60 optarray[3] = options->thread_control_stack_size;
63 if (RUNTIME_OPTIONS_WORDS !=
64 fwrite(optarray, sizeof(size_t), RUNTIME_OPTIONS_WORDS, file)) {
65 perror("Error writing runtime options to file");
69 static void
70 write_lispobj(lispobj obj, FILE *file)
72 if (1 != fwrite(&obj, sizeof(lispobj), 1, file)) {
73 perror("Error writing to file");
77 static void
78 write_bytes_to_file(FILE * file, char *addr, long bytes, int compression)
80 if (compression == COMPRESSION_LEVEL_NONE) {
81 while (bytes > 0) {
82 sword_t count = fwrite(addr, 1, bytes, file);
83 if (count > 0) {
84 bytes -= count;
85 addr += count;
87 else {
88 perror("error writing to core file");
89 lose("core file is incomplete or corrupt\n");
92 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
93 } else if ((compression >= -1) && (compression <= 9)) {
94 # define ZLIB_BUFFER_SIZE (1u<<16)
95 z_stream stream;
96 unsigned char buf[ZLIB_BUFFER_SIZE];
97 unsigned char * written, * end;
98 long total_written = 0;
99 int ret;
100 stream.zalloc = NULL;
101 stream.zfree = NULL;
102 stream.opaque = NULL;
103 stream.avail_in = bytes;
104 stream.next_in = (void*)addr;
105 ret = deflateInit(&stream, compression);
106 if (ret != Z_OK)
107 lose("deflateInit: %i\n", ret);
108 do {
109 stream.avail_out = sizeof(buf);
110 stream.next_out = buf;
111 ret = deflate(&stream, Z_FINISH);
112 if (ret < 0) lose("zlib deflate error: %i... exiting\n", ret);
113 written = buf;
114 end = buf+sizeof(buf)-stream.avail_out;
115 total_written += end - written;
116 while (written < end) {
117 long count = fwrite(written, 1, end-written, file);
118 if (count > 0) {
119 written += count;
120 } else {
121 perror("error writing to core file");
122 lose("core file is incomplete or corrupt\n");
125 } while (stream.avail_out == 0);
126 deflateEnd(&stream);
127 printf("compressed %lu bytes into %lu at level %i\n",
128 bytes, total_written, compression);
129 # undef ZLIB_BUFFER_SIZE
130 #endif
131 } else {
132 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
133 lose("Unknown core compression level %i, exiting\n", compression);
134 #else
135 lose("zlib-compressed core support not built in this runtime\n");
136 #endif
139 if (fflush(file) != 0) {
140 perror("error writing to core file");
141 lose("core file is incomplete or corrupt\n");
146 static long
147 write_and_compress_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset,
148 int compression)
150 long here, data;
152 bytes = (bytes+os_vm_page_size-1)&~(os_vm_page_size-1);
154 #ifdef LISP_FEATURE_WIN32
155 long count;
156 /* touch every single page in the space to force it to be mapped. */
157 for (count = 0; count < bytes; count += 0x1000) {
158 volatile int temp = addr[count];
160 #endif
162 fflush(file);
163 here = ftell(file);
164 fseek(file, 0, SEEK_END);
165 data = (ftell(file)+os_vm_page_size-1)&~(os_vm_page_size-1);
166 fseek(file, data, SEEK_SET);
167 write_bytes_to_file(file, addr, bytes, compression);
168 fseek(file, here, SEEK_SET);
169 return ((data - file_offset) / os_vm_page_size) - 1;
172 static long
173 write_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset)
175 return write_and_compress_bytes(file, addr, bytes, file_offset,
176 COMPRESSION_LEVEL_NONE);
179 static void
180 output_space(FILE *file, int id, lispobj *addr, lispobj *end,
181 os_vm_offset_t file_offset,
182 int core_compression_level)
184 size_t words, bytes, data, compressed_flag;
185 static char *names[] = {NULL, "dynamic", "static", "read-only"};
187 compressed_flag
188 = ((core_compression_level != COMPRESSION_LEVEL_NONE)
189 ? DEFLATED_CORE_SPACE_ID_FLAG : 0);
191 write_lispobj(id | compressed_flag, file);
192 words = end - addr;
193 write_lispobj(words, file);
195 bytes = words * sizeof(lispobj);
197 printf("writing %lu bytes from the %s space at %p\n",
198 bytes, names[id], addr);
200 data = write_and_compress_bytes(file, (char *)addr, bytes, file_offset,
201 core_compression_level);
203 write_lispobj(data, file);
204 write_lispobj((uword_t)addr / os_vm_page_size, file);
205 write_lispobj((bytes + os_vm_page_size - 1) / os_vm_page_size, file);
208 FILE *
209 open_core_for_saving(char *filename)
211 /* Open the output file. We don't actually need the file yet, but
212 * the fopen() might fail for some reason, and we want to detect
213 * that and back out before we do anything irreversible. */
214 unlink(filename);
215 return fopen(filename, "wb");
218 boolean
219 save_to_filehandle(FILE *file, char *filename, lispobj init_function,
220 boolean make_executable,
221 boolean save_runtime_options,
222 int core_compression_level)
224 struct thread *th;
225 os_vm_offset_t core_start_pos;
227 /* Smash the enclosing state. (Once we do this, there's no good
228 * way to go back, which is a sufficient reason that this ends up
229 * being SAVE-LISP-AND-DIE instead of SAVE-LISP-AND-GO-ON). */
230 printf("[undoing binding stack and other enclosing state... ");
231 fflush(stdout);
232 for_each_thread(th) { /* XXX really? */
233 unbind_to_here((lispobj *)th->binding_stack_start,th);
234 SetSymbolValue(CURRENT_CATCH_BLOCK, 0,th);
235 SetSymbolValue(CURRENT_UNWIND_PROTECT_BLOCK, 0,th);
237 printf("done]\n");
238 fflush(stdout);
240 /* (Now we can actually start copying ourselves into the output file.) */
242 printf("[saving current Lisp image into %s:\n", filename);
243 fflush(stdout);
245 core_start_pos = ftell(file);
246 write_lispobj(CORE_MAGIC, file);
248 write_lispobj(BUILD_ID_CORE_ENTRY_TYPE_CODE, file);
249 write_lispobj(/* (We're writing the word count of the entry here, and the 2
250 * term is one word for the leading BUILD_ID_CORE_ENTRY_TYPE_CODE
251 * word and one word where we store the count itself.) */
252 2 + strlen((const char *)build_id),
253 file);
255 unsigned char *p;
256 for (p = (unsigned char *)build_id; *p; ++p)
257 write_lispobj(*p, file);
260 write_lispobj(NEW_DIRECTORY_CORE_ENTRY_TYPE_CODE, file);
261 write_lispobj(/* (word count = 3 spaces described by 5 words each, plus the
262 * entry type code, plus this count itself) */
263 (5*3)+2, file);
264 output_space(file,
265 READ_ONLY_CORE_SPACE_ID,
266 (lispobj *)READ_ONLY_SPACE_START,
267 (lispobj *)SymbolValue(READ_ONLY_SPACE_FREE_POINTER,0),
268 core_start_pos,
269 core_compression_level);
270 output_space(file,
271 STATIC_CORE_SPACE_ID,
272 (lispobj *)STATIC_SPACE_START,
273 (lispobj *)SymbolValue(STATIC_SPACE_FREE_POINTER,0),
274 core_start_pos,
275 core_compression_level);
276 #ifdef LISP_FEATURE_GENCGC
277 /* Flush the current_region, updating the tables. */
278 gc_alloc_update_all_page_tables();
279 update_dynamic_space_free_pointer();
280 #endif
281 #ifdef reg_ALLOC
282 #ifdef LISP_FEATURE_GENCGC
283 output_space(file,
284 DYNAMIC_CORE_SPACE_ID,
285 (lispobj *)DYNAMIC_SPACE_START,
286 dynamic_space_free_pointer,
287 core_start_pos,
288 core_compression_level);
289 #else
290 output_space(file,
291 DYNAMIC_CORE_SPACE_ID,
292 (lispobj *)current_dynamic_space,
293 dynamic_space_free_pointer,
294 core_start_pos,
295 core_compression_level);
296 #endif
297 #else
298 output_space(file,
299 DYNAMIC_CORE_SPACE_ID,
300 (lispobj *)DYNAMIC_SPACE_START,
301 (lispobj *)SymbolValue(ALLOCATION_POINTER,0),
302 core_start_pos,
303 core_compression_level);
304 #endif
306 write_lispobj(INITIAL_FUN_CORE_ENTRY_TYPE_CODE, file);
307 write_lispobj(3, file);
308 write_lispobj(init_function, file);
310 #ifdef LISP_FEATURE_GENCGC
312 size_t size = (last_free_page*sizeof(sword_t)+os_vm_page_size-1)
313 &~(os_vm_page_size-1);
314 uword_t *data = calloc(size, 1);
315 if (data) {
316 uword_t word;
317 sword_t offset;
318 page_index_t i;
319 for (i = 0; i < last_free_page; i++) {
320 /* Thanks to alignment requirements, the two low bits
321 * are always zero, so we can use them to store the
322 * allocation type -- region is always closed, so only
323 * the two low bits of allocation flags matter. */
324 word = page_table[i].scan_start_offset;
325 gc_assert((word & 0x03) == 0);
326 data[i] = word | (0x03 & page_table[i].allocated);
328 write_lispobj(PAGE_TABLE_CORE_ENTRY_TYPE_CODE, file);
329 write_lispobj(4, file);
330 write_lispobj(size, file);
331 offset = write_bytes(file, (char *)data, size, core_start_pos);
332 write_lispobj(offset, file);
335 #endif
337 write_lispobj(END_CORE_ENTRY_TYPE_CODE, file);
339 /* Write a trailing header, ignored when parsing the core normally.
340 * This is used to locate the start of the core when the runtime is
341 * prepended to it. */
342 fseek(file, 0, SEEK_END);
344 /* If NULL runtime options are passed to write_runtime_options,
345 * command-line processing is performed as normal in the SBCL
346 * executable. Otherwise, the saved runtime options are used and
347 * all command-line arguments are available to Lisp in
348 * SB-EXT:*POSIX-ARGV*. */
349 write_runtime_options(file,
350 (save_runtime_options ? runtime_options : NULL));
352 if (1 != fwrite(&core_start_pos, sizeof(os_vm_offset_t), 1, file)) {
353 perror("Error writing core starting position to file");
354 fclose(file);
355 } else {
356 write_lispobj(CORE_MAGIC, file);
357 fclose(file);
360 #ifndef LISP_FEATURE_WIN32
361 if (make_executable)
362 chmod (filename, 0755);
363 #endif
365 printf("done]\n");
366 exit(0);
369 /* Check if the build_id for the current runtime is present in a
370 * buffer. */
372 check_runtime_build_id(void *buf, size_t size)
374 size_t idlen;
375 char *pos;
377 idlen = strlen((const char*)build_id) - 1;
378 while ((pos = memchr(buf, build_id[0], size)) != NULL) {
379 size -= (pos + 1) - (char *)buf;
380 buf = (pos + 1);
381 if (idlen <= size && memcmp(buf, build_id + 1, idlen) == 0)
382 return 1;
385 return 0;
388 /* Slurp the executable portion of the runtime into a malloced buffer
389 * and return it. Places the size in bytes of the runtime into
390 * 'size_out'. Returns NULL if the runtime cannot be loaded from
391 * 'runtime_path'. */
392 void *
393 load_runtime(char *runtime_path, size_t *size_out)
395 void *buf = NULL;
396 FILE *input = NULL;
397 size_t size, count;
398 os_vm_offset_t core_offset;
400 core_offset = search_for_embedded_core (runtime_path);
401 if ((input = fopen(runtime_path, "rb")) == NULL) {
402 fprintf(stderr, "Unable to open runtime: %s\n", runtime_path);
403 goto lose;
406 fseek(input, 0, SEEK_END);
407 size = (size_t) ftell(input);
408 fseek(input, 0, SEEK_SET);
410 if (core_offset != -1 && size > (size_t) core_offset)
411 size = core_offset;
413 buf = successful_malloc(size);
414 if ((count = fread(buf, 1, size, input)) != size) {
415 fprintf(stderr, "Premature EOF while reading runtime.\n");
416 goto lose;
419 if (!check_runtime_build_id(buf, size)) {
420 fprintf(stderr, "Failed to locate current build_id in runtime: %s\n",
421 runtime_path);
422 goto lose;
425 fclose(input);
426 *size_out = size;
427 return buf;
429 lose:
430 if (input != NULL)
431 fclose(input);
432 if (buf != NULL)
433 free(buf);
434 return NULL;
437 boolean
438 save_runtime_to_filehandle(FILE *output, void *runtime, size_t runtime_size,
439 int application_type)
441 size_t padding;
442 void *padbytes;
444 #ifdef LISP_FEATURE_WIN32
446 PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)runtime;
447 PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((char *)dos_header +
448 dos_header->e_lfanew);
450 int sub_system;
451 switch (application_type) {
452 case 0:
453 sub_system = IMAGE_SUBSYSTEM_WINDOWS_CUI;
454 break;
455 case 1:
456 sub_system = IMAGE_SUBSYSTEM_WINDOWS_GUI;
457 break;
458 default:
459 fprintf(stderr, "Invalid application type %d\n", application_type);
460 return 0;
463 nt_header->OptionalHeader.Subsystem = sub_system;
465 #endif
467 if (runtime_size != fwrite(runtime, 1, runtime_size, output)) {
468 perror("Error saving runtime");
469 return 0;
472 padding = (os_vm_page_size - (runtime_size % os_vm_page_size)) & ~os_vm_page_size;
473 if (padding > 0) {
474 padbytes = successful_malloc(padding);
475 memset(padbytes, 0, padding);
476 if (padding != fwrite(padbytes, 1, padding, output)) {
477 perror("Error saving runtime");
478 free(padbytes);
479 return 0;
481 free(padbytes);
484 return 1;
487 FILE *
488 prepare_to_save(char *filename, boolean prepend_runtime, void **runtime_bytes,
489 size_t *runtime_size)
491 FILE *file;
492 char *runtime_path;
494 if (prepend_runtime) {
495 runtime_path = os_get_runtime_executable_path(0);
497 if (runtime_path == NULL && saved_runtime_path == NULL) {
498 fprintf(stderr, "Unable to get default runtime path.\n");
499 return NULL;
502 if (runtime_path == NULL)
503 *runtime_bytes = load_runtime(saved_runtime_path, runtime_size);
504 else {
505 *runtime_bytes = load_runtime(runtime_path, runtime_size);
506 free(runtime_path);
509 if (*runtime_bytes == NULL)
510 return 0;
513 file = open_core_for_saving(filename);
514 if (file == NULL) {
515 free(*runtime_bytes);
516 perror(filename);
517 return NULL;
520 return file;
523 boolean
524 save(char *filename, lispobj init_function, boolean prepend_runtime,
525 boolean save_runtime_options, boolean compressed, int compression_level,
526 int application_type)
528 FILE *file;
529 void *runtime_bytes = NULL;
530 size_t runtime_size;
532 file = prepare_to_save(filename, prepend_runtime, &runtime_bytes, &runtime_size);
533 if (file == NULL)
534 return 1;
536 if (prepend_runtime)
537 save_runtime_to_filehandle(file, runtime_bytes, runtime_size, application_type);
539 return save_to_filehandle(file, filename, init_function, prepend_runtime,
540 save_runtime_options,
541 compressed ? compressed : COMPRESSION_LEVEL_NONE);