2 * This software is part of the SBCL system. See the README file for
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>
22 #if defined(LISP_FEATURE_WIN32) && defined(LISP_FEATURE_SB_THREAD)
23 #include "pthreads_win32.h"
35 #include "gc-internal.h"
38 #include "pseudo-atomic.h"
40 #include "genesis/static-symbols.h"
41 #include "genesis/symbol.h"
43 #ifdef LISP_FEATURE_GENCGC
47 #ifdef LISP_FEATURE_IMMOBILE_SPACE
48 # include "immobile-space.h"
51 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
55 #define GENERAL_WRITE_FAILURE_MSG "error writing to core file"
57 /* write_runtime_options uses a simple serialization scheme that
58 * consists of one word of magic, one word indicating whether options
59 * are actually saved, and one word per struct field. */
61 write_runtime_options(FILE *file
, struct runtime_options
*options
)
63 size_t optarray
[RUNTIME_OPTIONS_WORDS
];
65 memset(&optarray
, 0, sizeof(optarray
));
66 optarray
[0] = RUNTIME_OPTIONS_MAGIC
;
68 if (options
!= NULL
) {
69 /* optarray[1] is a flag indicating that options are present */
71 optarray
[2] = options
->dynamic_space_size
;
72 optarray
[3] = options
->thread_control_stack_size
;
75 if (RUNTIME_OPTIONS_WORDS
!=
76 fwrite(optarray
, sizeof(size_t), RUNTIME_OPTIONS_WORDS
, file
)) {
77 perror("Error writing runtime options to file");
82 write_lispobj(lispobj obj
, FILE *file
)
84 if (1 != fwrite(&obj
, sizeof(lispobj
), 1, file
)) {
85 perror(GENERAL_WRITE_FAILURE_MSG
);
90 write_bytes_to_file(FILE * file
, char *addr
, long bytes
, int compression
)
92 if (compression
== COMPRESSION_LEVEL_NONE
) {
94 sword_t count
= fwrite(addr
, 1, bytes
, file
);
100 perror(GENERAL_WRITE_FAILURE_MSG
);
101 lose("core file is incomplete or corrupt\n");
104 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
105 } else if ((compression
>= -1) && (compression
<= 9)) {
106 # define ZLIB_BUFFER_SIZE (1u<<16)
108 unsigned char* buf
= successful_malloc(ZLIB_BUFFER_SIZE
);
109 unsigned char * written
, * end
;
110 long total_written
= 0;
112 stream
.zalloc
= NULL
;
114 stream
.opaque
= NULL
;
115 stream
.avail_in
= bytes
;
116 stream
.next_in
= (void*)addr
;
117 ret
= deflateInit(&stream
, compression
);
119 lose("deflateInit: %i\n", ret
);
121 stream
.avail_out
= ZLIB_BUFFER_SIZE
;
122 stream
.next_out
= buf
;
123 ret
= deflate(&stream
, Z_FINISH
);
124 if (ret
< 0) lose("zlib deflate error: %i... exiting\n", ret
);
126 end
= buf
+ZLIB_BUFFER_SIZE
-stream
.avail_out
;
127 total_written
+= end
- written
;
128 while (written
< end
) {
129 long count
= fwrite(written
, 1, end
-written
, file
);
133 perror(GENERAL_WRITE_FAILURE_MSG
);
134 lose("core file is incomplete or corrupt\n");
137 } while (stream
.avail_out
== 0);
140 printf("compressed %lu bytes into %lu at level %i\n",
141 bytes
, total_written
, compression
);
142 # undef ZLIB_BUFFER_SIZE
145 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
146 lose("Unknown core compression level %i, exiting\n", compression
);
148 lose("zlib-compressed core support not built in this runtime\n");
152 if (fflush(file
) != 0) {
153 perror(GENERAL_WRITE_FAILURE_MSG
);
154 lose("core file is incomplete or corrupt\n");
160 write_and_compress_bytes(FILE *file
, char *addr
, long bytes
, os_vm_offset_t file_offset
,
165 bytes
= (bytes
+os_vm_page_size
-1)&~(os_vm_page_size
-1);
167 #ifdef LISP_FEATURE_WIN32
169 /* touch every single page in the space to force it to be mapped. */
170 for (count
= 0; count
< bytes
; count
+= 0x1000) {
171 volatile int temp
= addr
[count
];
177 fseek(file
, 0, SEEK_END
);
178 data
= (ftell(file
)+os_vm_page_size
-1)&~(os_vm_page_size
-1);
179 fseek(file
, data
, SEEK_SET
);
180 write_bytes_to_file(file
, addr
, bytes
, compression
);
181 fseek(file
, here
, SEEK_SET
);
182 return ((data
- file_offset
) / os_vm_page_size
) - 1;
185 static long __attribute__((__unused__
))
186 write_bytes(FILE *file
, char *addr
, long bytes
, os_vm_offset_t file_offset
)
188 return write_and_compress_bytes(file
, addr
, bytes
, file_offset
,
189 COMPRESSION_LEVEL_NONE
);
192 extern struct lisp_startup_options lisp_startup_options
;
195 output_space(FILE *file
, int id
, lispobj
*addr
, lispobj
*end
,
196 os_vm_offset_t file_offset
,
197 int core_compression_level
)
199 size_t words
, bytes
, data
, compressed_flag
;
200 static char *names
[] = {NULL
, "dynamic", "static", "read-only",
201 "immobile", "immobile"};
204 = ((core_compression_level
!= COMPRESSION_LEVEL_NONE
)
205 ? DEFLATED_CORE_SPACE_ID_FLAG
: 0);
207 write_lispobj(id
| compressed_flag
, file
);
209 write_lispobj(words
, file
);
211 bytes
= words
* sizeof(lispobj
);
213 if (!lisp_startup_options
.noinform
)
214 printf("writing %lu bytes from the %s space at %p\n",
215 (long unsigned)bytes
, names
[id
], addr
);
217 data
= write_and_compress_bytes(file
, (char *)addr
, bytes
, file_offset
,
218 core_compression_level
);
220 write_lispobj(data
, file
);
221 write_lispobj((uword_t
)addr
/ 1024, file
); // units as per core.h
222 write_lispobj((bytes
+ os_vm_page_size
- 1) / os_vm_page_size
, file
);
226 open_core_for_saving(char *filename
)
228 /* Open the output file. We don't actually need the file yet, but
229 * the fopen() might fail for some reason, and we want to detect
230 * that and back out before we do anything irreversible. */
232 return fopen(filename
, "wb");
236 smash_enclosing_state(boolean verbose
) {
237 struct thread
*th
= all_threads
;
239 // Since SB-IMPL::DEINIT already checked for exactly 1 thread,
240 // losing here probably can't happen.
242 lose("Can't save image with more than one executing thread");
244 #ifdef LISP_FEATURE_X86_64
245 untune_asm_routines_for_microarch();
248 /* Smash the enclosing state. (Once we do this, there's no good
249 * way to go back, which is a sufficient reason that this ends up
250 * being SAVE-LISP-AND-DIE instead of SAVE-LISP-AND-GO-ON). */
252 printf("[undoing binding stack and other enclosing state... ");
255 unbind_to_here((lispobj
*)th
->binding_stack_start
,th
);
256 write_TLS(CURRENT_CATCH_BLOCK
, 0, th
); // If set to 0 on start, why here too?
257 write_TLS(CURRENT_UNWIND_PROTECT_BLOCK
, 0, th
);
258 if (verbose
) printf("done]\n");
262 do_destructive_cleanup_before_save(lispobj init_function
)
264 boolean verbose
= !lisp_startup_options
.noinform
;
265 // Preparing to save.
266 smash_enclosing_state(verbose
);
267 #ifdef LISP_FEATURE_IMMOBILE_SPACE
268 prepare_immobile_space_for_save(init_function
, verbose
);
273 save_to_filehandle(FILE *file
, char *filename
, lispobj init_function
,
274 boolean make_executable
,
275 boolean save_runtime_options
,
276 int core_compression_level
)
278 boolean verbose
= !lisp_startup_options
.noinform
;
280 /* (Now we can actually start copying ourselves into the output file.) */
283 printf("[saving current Lisp image into %s:\n", filename
);
287 os_vm_offset_t core_start_pos
= ftell(file
);
288 write_lispobj(CORE_MAGIC
, file
);
290 int stringlen
= strlen((const char *)build_id
);
291 int string_words
= ALIGN_UP(stringlen
, sizeof (core_entry_elt_t
))
292 / sizeof (core_entry_elt_t
);
293 int pad
= string_words
* sizeof (core_entry_elt_t
) - stringlen
;
294 /* Write 3 word entry header: a word for entry-type-code, a word for
295 * the total length in words, and a word for the string length */
296 write_lispobj(BUILD_ID_CORE_ENTRY_TYPE_CODE
, file
);
297 write_lispobj(3 + string_words
, file
);
298 write_lispobj(stringlen
, file
);
299 int nwrote
= fwrite(build_id
, 1, stringlen
, file
);
300 /* Write padding bytes to align to core_entry_elt_t */
301 while (pad
--) nwrote
+= (fputc(0xff, file
) != EOF
);
302 if (nwrote
!= (int)(sizeof (core_entry_elt_t
) * string_words
))
303 perror(GENERAL_WRITE_FAILURE_MSG
);
305 write_lispobj(NEW_DIRECTORY_CORE_ENTRY_TYPE_CODE
, file
);
306 write_lispobj(/* (word count = N spaces described by 5 words each, plus the
307 * entry type code, plus this count itself) */
308 (5 * MAX_CORE_SPACE_ID
) + 2, file
);
310 READ_ONLY_CORE_SPACE_ID
,
311 (lispobj
*)READ_ONLY_SPACE_START
,
312 read_only_space_free_pointer
,
314 core_compression_level
);
316 STATIC_CORE_SPACE_ID
,
317 (lispobj
*)STATIC_SPACE_START
,
318 static_space_free_pointer
,
320 core_compression_level
);
321 #ifdef LISP_FEATURE_GENCGC
322 /* Flush the current_region, updating the tables. */
323 gc_alloc_update_all_page_tables(1);
324 gc_assert(get_alloc_pointer() == (lispobj
*)(page_address(find_last_free_page())));
326 #ifdef LISP_FEATURE_IMMOBILE_SPACE
328 IMMOBILE_FIXEDOBJ_CORE_SPACE_ID
,
329 (lispobj
*)IMMOBILE_SPACE_START
,
330 immobile_fixedobj_free_pointer
,
332 core_compression_level
);
334 IMMOBILE_VARYOBJ_CORE_SPACE_ID
,
335 (lispobj
*)IMMOBILE_VARYOBJ_SUBSPACE_START
,
336 immobile_space_free_pointer
,
338 core_compression_level
);
341 DYNAMIC_CORE_SPACE_ID
,
342 current_dynamic_space
,
343 (lispobj
*)get_alloc_pointer(),
345 core_compression_level
);
347 write_lispobj(INITIAL_FUN_CORE_ENTRY_TYPE_CODE
, file
);
348 write_lispobj(3, file
);
349 write_lispobj(init_function
, file
);
351 #ifdef LISP_FEATURE_GENCGC
353 extern void gc_store_corefile_ptes(struct corefile_pte
*);
354 size_t true_size
= sizeof last_free_page
355 + (last_free_page
* sizeof(struct corefile_pte
));
356 size_t rounded_size
= ALIGN_UP(true_size
, os_vm_page_size
);
357 char* data
= successful_malloc(rounded_size
);
358 *(page_index_t
*)data
= last_free_page
;
359 struct corefile_pte
*ptes
= (struct corefile_pte
*)(data
+ sizeof(page_index_t
));
360 gc_store_corefile_ptes(ptes
);
361 write_lispobj(PAGE_TABLE_CORE_ENTRY_TYPE_CODE
, file
);
362 write_lispobj(4, file
);
363 write_lispobj(rounded_size
, file
);
364 /* Clear unwritten bytes in the malloc'd range. They're probably zero
365 * because malloc of large blocks is usually just an mmap(),
366 * but we can't be certain the memory was freshly allocated */
367 char* clear_from
= (char*)&ptes
[last_free_page
];
368 char* clear_to
= data
+ rounded_size
;
369 memset(clear_from
, 0, clear_to
-clear_from
);
370 sword_t offset
= write_bytes(file
, data
, rounded_size
, core_start_pos
);
371 write_lispobj(offset
, file
);
375 write_lispobj(END_CORE_ENTRY_TYPE_CODE
, file
);
377 /* Write a trailing header, ignored when parsing the core normally.
378 * This is used to locate the start of the core when the runtime is
379 * prepended to it. */
380 fseek(file
, 0, SEEK_END
);
382 /* If NULL runtime options are passed to write_runtime_options,
383 * command-line processing is performed as normal in the SBCL
384 * executable. Otherwise, the saved runtime options are used and
385 * all command-line arguments are available to Lisp in
386 * SB-EXT:*POSIX-ARGV*. */
387 write_runtime_options(file
,
388 (save_runtime_options
? runtime_options
: NULL
));
390 if (1 != fwrite(&core_start_pos
, sizeof(os_vm_offset_t
), 1, file
)) {
391 perror("Error writing core starting position to file");
394 write_lispobj(CORE_MAGIC
, file
);
398 #ifndef LISP_FEATURE_WIN32
400 chmod (filename
, 0755);
403 if (verbose
) printf("done]\n");
407 /* Check if the build_id for the current runtime is present in a
410 check_runtime_build_id(void *buf
, size_t size
)
415 idlen
= strlen((const char*)build_id
) - 1;
416 while ((pos
= memchr(buf
, build_id
[0], size
)) != NULL
) {
417 size
-= (pos
+ 1) - (char *)buf
;
419 if (idlen
<= size
&& memcmp(buf
, build_id
+ 1, idlen
) == 0)
426 /* Slurp the executable portion of the runtime into a malloced buffer
427 * and return it. Places the size in bytes of the runtime into
428 * 'size_out'. Returns NULL if the runtime cannot be loaded from
431 load_runtime(char *runtime_path
, size_t *size_out
)
436 os_vm_offset_t core_offset
;
438 core_offset
= search_for_embedded_core (runtime_path
);
439 if ((input
= fopen(runtime_path
, "rb")) == NULL
) {
440 fprintf(stderr
, "Unable to open runtime: %s\n", runtime_path
);
444 fseek(input
, 0, SEEK_END
);
445 size
= (size_t) ftell(input
);
446 fseek(input
, 0, SEEK_SET
);
448 if (core_offset
!= -1 && size
> (size_t) core_offset
)
451 buf
= successful_malloc(size
);
452 if ((count
= fread(buf
, 1, size
, input
)) != size
) {
453 fprintf(stderr
, "Premature EOF while reading runtime.\n");
457 if (!check_runtime_build_id(buf
, size
)) {
458 fprintf(stderr
, "Failed to locate current build_id in runtime: %s\n",
476 save_runtime_to_filehandle(FILE *output
, void *runtime
, size_t runtime_size
,
477 int application_type
)
482 #ifdef LISP_FEATURE_WIN32
484 PIMAGE_DOS_HEADER dos_header
= (PIMAGE_DOS_HEADER
)runtime
;
485 PIMAGE_NT_HEADERS nt_header
= (PIMAGE_NT_HEADERS
)((char *)dos_header
+
486 dos_header
->e_lfanew
);
489 switch (application_type
) {
491 sub_system
= IMAGE_SUBSYSTEM_WINDOWS_CUI
;
494 sub_system
= IMAGE_SUBSYSTEM_WINDOWS_GUI
;
497 fprintf(stderr
, "Invalid application type %d\n", application_type
);
501 nt_header
->OptionalHeader
.Subsystem
= sub_system
;
505 if (runtime_size
!= fwrite(runtime
, 1, runtime_size
, output
)) {
506 perror("Error saving runtime");
510 padding
= (os_vm_page_size
- (runtime_size
% os_vm_page_size
)) & ~os_vm_page_size
;
512 padbytes
= successful_malloc(padding
);
513 memset(padbytes
, 0, padding
);
514 if (padding
!= fwrite(padbytes
, 1, padding
, output
)) {
515 perror("Error saving runtime");
526 prepare_to_save(char *filename
, boolean prepend_runtime
, void **runtime_bytes
,
527 size_t *runtime_size
)
532 if (prepend_runtime
) {
533 runtime_path
= os_get_runtime_executable_path(0);
535 if (runtime_path
== NULL
&& saved_runtime_path
== NULL
) {
536 fprintf(stderr
, "Unable to get default runtime path.\n");
540 if (runtime_path
== NULL
)
541 *runtime_bytes
= load_runtime(saved_runtime_path
, runtime_size
);
543 *runtime_bytes
= load_runtime(runtime_path
, runtime_size
);
547 if (*runtime_bytes
== NULL
)
551 file
= open_core_for_saving(filename
);
553 free(*runtime_bytes
);
561 #ifdef LISP_FEATURE_CHENEYGC
563 save(char *filename
, lispobj init_function
, boolean prepend_runtime
,
564 boolean save_runtime_options
, boolean compressed
, int compression_level
,
565 int application_type
)
568 void *runtime_bytes
= NULL
;
571 file
= prepare_to_save(filename
, prepend_runtime
, &runtime_bytes
, &runtime_size
);
576 save_runtime_to_filehandle(file
, runtime_bytes
, runtime_size
, application_type
);
578 do_destructive_cleanup_before_save(init_function
);
579 return save_to_filehandle(file
, filename
, init_function
, prepend_runtime
,
580 save_runtime_options
,
581 compressed
? compressed
: COMPRESSION_LEVEL_NONE
);