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"
39 #include "genesis/static-symbols.h"
40 #include "genesis/symbol.h"
42 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
46 /* write_runtime_options uses a simple serialization scheme that
47 * consists of one word of magic, one word indicating whether options
48 * are actually saved, and one word per struct field. */
50 write_runtime_options(FILE *file
, struct runtime_options
*options
)
52 size_t optarray
[RUNTIME_OPTIONS_WORDS
];
54 memset(&optarray
, 0, sizeof(optarray
));
55 optarray
[0] = RUNTIME_OPTIONS_MAGIC
;
57 if (options
!= NULL
) {
58 /* optarray[1] is a flag indicating that options are present */
60 optarray
[2] = options
->dynamic_space_size
;
61 optarray
[3] = options
->thread_control_stack_size
;
64 if (RUNTIME_OPTIONS_WORDS
!=
65 fwrite(optarray
, sizeof(size_t), RUNTIME_OPTIONS_WORDS
, file
)) {
66 perror("Error writing runtime options to file");
71 write_lispobj(lispobj obj
, FILE *file
)
73 if (1 != fwrite(&obj
, sizeof(lispobj
), 1, file
)) {
74 perror("Error writing to file");
79 write_bytes_to_file(FILE * file
, char *addr
, long bytes
, int compression
)
81 if (compression
== COMPRESSION_LEVEL_NONE
) {
83 sword_t count
= fwrite(addr
, 1, bytes
, file
);
89 perror("error writing to core file");
90 lose("core file is incomplete or corrupt\n");
93 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
94 } else if ((compression
>= -1) && (compression
<= 9)) {
95 # define ZLIB_BUFFER_SIZE (1u<<16)
97 unsigned char buf
[ZLIB_BUFFER_SIZE
];
98 unsigned char * written
, * end
;
99 long total_written
= 0;
101 stream
.zalloc
= NULL
;
103 stream
.opaque
= NULL
;
104 stream
.avail_in
= bytes
;
105 stream
.next_in
= (void*)addr
;
106 ret
= deflateInit(&stream
, compression
);
108 lose("deflateInit: %i\n", ret
);
110 stream
.avail_out
= sizeof(buf
);
111 stream
.next_out
= buf
;
112 ret
= deflate(&stream
, Z_FINISH
);
113 if (ret
< 0) lose("zlib deflate error: %i... exiting\n", ret
);
115 end
= buf
+sizeof(buf
)-stream
.avail_out
;
116 total_written
+= end
- written
;
117 while (written
< end
) {
118 long count
= fwrite(written
, 1, end
-written
, file
);
122 perror("error writing to core file");
123 lose("core file is incomplete or corrupt\n");
126 } while (stream
.avail_out
== 0);
128 printf("compressed %lu bytes into %lu at level %i\n",
129 bytes
, total_written
, compression
);
130 # undef ZLIB_BUFFER_SIZE
133 #ifdef LISP_FEATURE_SB_CORE_COMPRESSION
134 lose("Unknown core compression level %i, exiting\n", compression
);
136 lose("zlib-compressed core support not built in this runtime\n");
140 if (fflush(file
) != 0) {
141 perror("error writing to core file");
142 lose("core file is incomplete or corrupt\n");
148 write_and_compress_bytes(FILE *file
, char *addr
, long bytes
, os_vm_offset_t file_offset
,
153 bytes
= (bytes
+os_vm_page_size
-1)&~(os_vm_page_size
-1);
155 #ifdef LISP_FEATURE_WIN32
157 /* touch every single page in the space to force it to be mapped. */
158 for (count
= 0; count
< bytes
; count
+= 0x1000) {
159 volatile int temp
= addr
[count
];
165 fseek(file
, 0, SEEK_END
);
166 data
= (ftell(file
)+os_vm_page_size
-1)&~(os_vm_page_size
-1);
167 fseek(file
, data
, SEEK_SET
);
168 write_bytes_to_file(file
, addr
, bytes
, compression
);
169 fseek(file
, here
, SEEK_SET
);
170 return ((data
- file_offset
) / os_vm_page_size
) - 1;
173 static long __attribute__((__unused__
))
174 write_bytes(FILE *file
, char *addr
, long bytes
, os_vm_offset_t file_offset
)
176 return write_and_compress_bytes(file
, addr
, bytes
, file_offset
,
177 COMPRESSION_LEVEL_NONE
);
180 extern struct lisp_startup_options lisp_startup_options
;
183 output_space(FILE *file
, int id
, lispobj
*addr
, lispobj
*end
,
184 os_vm_offset_t file_offset
,
185 int core_compression_level
)
187 size_t words
, bytes
, data
, compressed_flag
;
188 static char *names
[] = {NULL
, "dynamic", "static", "read-only",
189 "immobile", "immobile"};
192 = ((core_compression_level
!= COMPRESSION_LEVEL_NONE
)
193 ? DEFLATED_CORE_SPACE_ID_FLAG
: 0);
195 write_lispobj(id
| compressed_flag
, file
);
197 write_lispobj(words
, file
);
199 bytes
= words
* sizeof(lispobj
);
201 if (!lisp_startup_options
.noinform
)
202 printf("writing %lu bytes from the %s space at %p\n",
203 (long unsigned)bytes
, names
[id
], addr
);
205 data
= write_and_compress_bytes(file
, (char *)addr
, bytes
, file_offset
,
206 core_compression_level
);
208 write_lispobj(data
, file
);
209 write_lispobj((uword_t
)addr
/ os_vm_page_size
, file
);
210 write_lispobj((bytes
+ os_vm_page_size
- 1) / os_vm_page_size
, file
);
214 open_core_for_saving(char *filename
)
216 /* Open the output file. We don't actually need the file yet, but
217 * the fopen() might fail for some reason, and we want to detect
218 * that and back out before we do anything irreversible. */
220 return fopen(filename
, "wb");
223 #ifdef LISP_FEATURE_IMMOBILE_SPACE
224 extern void prepare_immobile_space_for_save();
225 # define N_SPACES_TO_SAVE 5
226 # ifdef LISP_FEATURE_IMMOBILE_CODE
227 lispobj code_component_order
;
228 extern void defrag_immobile_space(lispobj
);
231 # define N_SPACES_TO_SAVE 3
234 save_to_filehandle(FILE *file
, char *filename
, lispobj init_function
,
235 boolean make_executable
,
236 boolean save_runtime_options
,
237 int core_compression_level
)
240 os_vm_offset_t core_start_pos
;
241 boolean verbose
= !lisp_startup_options
.noinform
;
243 #ifdef LISP_FEATURE_X86_64
244 untune_asm_routines_for_microarch();
247 /* Smash the enclosing state. (Once we do this, there's no good
248 * way to go back, which is a sufficient reason that this ends up
249 * being SAVE-LISP-AND-DIE instead of SAVE-LISP-AND-GO-ON). */
251 printf("[undoing binding stack and other enclosing state... ");
254 for_each_thread(th
) { /* XXX really? */
255 unbind_to_here((lispobj
*)th
->binding_stack_start
,th
);
256 SetSymbolValue(CURRENT_CATCH_BLOCK
, 0,th
);
257 SetSymbolValue(CURRENT_UNWIND_PROTECT_BLOCK
, 0,th
);
259 if (verbose
) printf("done]\n");
260 #ifdef LISP_FEATURE_IMMOBILE_CODE
261 // It's better to wait to defrag until after the binding stack is undone,
262 // because we explicitly don't fixup code refs from stacks.
263 // i.e. if there *were* something on the binding stack that cared that code
264 // moved, it would be wrong. This way we can be sure we don't care.
265 if (code_component_order
) {
266 // Assert that defrag will not move the init_function
267 gc_assert(!immobile_space_p(init_function
));
269 printf("[defragmenting immobile space... ");
272 defrag_immobile_space(code_component_order
);
273 if (verbose
) printf("done]\n");
277 /* (Now we can actually start copying ourselves into the output file.) */
280 printf("[saving current Lisp image into %s:\n", filename
);
284 core_start_pos
= ftell(file
);
285 write_lispobj(CORE_MAGIC
, file
);
287 write_lispobj(BUILD_ID_CORE_ENTRY_TYPE_CODE
, file
);
288 write_lispobj(/* (We're writing the word count of the entry here, and the 2
289 * term is one word for the leading BUILD_ID_CORE_ENTRY_TYPE_CODE
290 * word and one word where we store the count itself.) */
291 2 + strlen((const char *)build_id
),
295 for (p
= (unsigned char *)build_id
; *p
; ++p
)
296 write_lispobj(*p
, file
);
299 write_lispobj(NEW_DIRECTORY_CORE_ENTRY_TYPE_CODE
, file
);
300 write_lispobj(/* (word count = N spaces described by 5 words each, plus the
301 * entry type code, plus this count itself) */
302 (5*N_SPACES_TO_SAVE
)+2, file
);
304 READ_ONLY_CORE_SPACE_ID
,
305 (lispobj
*)READ_ONLY_SPACE_START
,
306 (lispobj
*)SymbolValue(READ_ONLY_SPACE_FREE_POINTER
,0),
308 core_compression_level
);
310 STATIC_CORE_SPACE_ID
,
311 (lispobj
*)STATIC_SPACE_START
,
312 (lispobj
*)SymbolValue(STATIC_SPACE_FREE_POINTER
,0),
314 core_compression_level
);
315 #ifdef LISP_FEATURE_GENCGC
316 /* Flush the current_region, updating the tables. */
317 gc_alloc_update_all_page_tables(1);
318 update_dynamic_space_free_pointer();
320 #ifdef LISP_FEATURE_IMMOBILE_SPACE
321 prepare_immobile_space_for_save();
323 IMMOBILE_FIXEDOBJ_CORE_SPACE_ID
,
324 (lispobj
*)IMMOBILE_SPACE_START
,
325 (lispobj
*)SymbolValue(IMMOBILE_FIXEDOBJ_FREE_POINTER
,0),
327 core_compression_level
);
329 IMMOBILE_VARYOBJ_CORE_SPACE_ID
,
330 (lispobj
*)IMMOBILE_VARYOBJ_SUBSPACE_START
,
331 (lispobj
*)SymbolValue(IMMOBILE_SPACE_FREE_POINTER
,0),
333 core_compression_level
);
336 #ifdef LISP_FEATURE_GENCGC
338 DYNAMIC_CORE_SPACE_ID
,
339 (lispobj
*)DYNAMIC_SPACE_START
,
340 dynamic_space_free_pointer
,
342 core_compression_level
);
345 DYNAMIC_CORE_SPACE_ID
,
346 (lispobj
*)current_dynamic_space
,
347 dynamic_space_free_pointer
,
349 core_compression_level
);
353 DYNAMIC_CORE_SPACE_ID
,
354 (lispobj
*)DYNAMIC_SPACE_START
,
355 (lispobj
*)SymbolValue(ALLOCATION_POINTER
,0),
357 core_compression_level
);
360 write_lispobj(INITIAL_FUN_CORE_ENTRY_TYPE_CODE
, file
);
361 write_lispobj(3, file
);
362 write_lispobj(init_function
, file
);
364 #ifdef LISP_FEATURE_GENCGC
366 size_t size
= (last_free_page
*sizeof(sword_t
)+os_vm_page_size
-1)
367 &~(os_vm_page_size
-1);
368 uword_t
*data
= calloc(size
, 1);
373 for (i
= 0; i
< last_free_page
; i
++) {
374 /* Thanks to alignment requirements, the two low bits
375 * are always zero, so we can use them to store the
376 * allocation type -- region is always closed, so only
377 * the two low bits of allocation flags matter. */
378 word
= page_scan_start_offset(i
);
379 gc_assert((word
& 0x03) == 0);
380 data
[i
] = word
| (0x03 & page_table
[i
].allocated
);
382 write_lispobj(PAGE_TABLE_CORE_ENTRY_TYPE_CODE
, file
);
383 write_lispobj(4, file
);
384 write_lispobj(size
, file
);
385 offset
= write_bytes(file
, (char *)data
, size
, core_start_pos
);
386 write_lispobj(offset
, file
);
391 write_lispobj(END_CORE_ENTRY_TYPE_CODE
, file
);
393 /* Write a trailing header, ignored when parsing the core normally.
394 * This is used to locate the start of the core when the runtime is
395 * prepended to it. */
396 fseek(file
, 0, SEEK_END
);
398 /* If NULL runtime options are passed to write_runtime_options,
399 * command-line processing is performed as normal in the SBCL
400 * executable. Otherwise, the saved runtime options are used and
401 * all command-line arguments are available to Lisp in
402 * SB-EXT:*POSIX-ARGV*. */
403 write_runtime_options(file
,
404 (save_runtime_options
? runtime_options
: NULL
));
406 if (1 != fwrite(&core_start_pos
, sizeof(os_vm_offset_t
), 1, file
)) {
407 perror("Error writing core starting position to file");
410 write_lispobj(CORE_MAGIC
, file
);
414 #ifndef LISP_FEATURE_WIN32
416 chmod (filename
, 0755);
419 if (verbose
) printf("done]\n");
422 #undef N_SPACES_TO_SAVE
424 /* Check if the build_id for the current runtime is present in a
427 check_runtime_build_id(void *buf
, size_t size
)
432 idlen
= strlen((const char*)build_id
) - 1;
433 while ((pos
= memchr(buf
, build_id
[0], size
)) != NULL
) {
434 size
-= (pos
+ 1) - (char *)buf
;
436 if (idlen
<= size
&& memcmp(buf
, build_id
+ 1, idlen
) == 0)
443 /* Slurp the executable portion of the runtime into a malloced buffer
444 * and return it. Places the size in bytes of the runtime into
445 * 'size_out'. Returns NULL if the runtime cannot be loaded from
448 load_runtime(char *runtime_path
, size_t *size_out
)
453 os_vm_offset_t core_offset
;
455 core_offset
= search_for_embedded_core (runtime_path
);
456 if ((input
= fopen(runtime_path
, "rb")) == NULL
) {
457 fprintf(stderr
, "Unable to open runtime: %s\n", runtime_path
);
461 fseek(input
, 0, SEEK_END
);
462 size
= (size_t) ftell(input
);
463 fseek(input
, 0, SEEK_SET
);
465 if (core_offset
!= -1 && size
> (size_t) core_offset
)
468 buf
= successful_malloc(size
);
469 if ((count
= fread(buf
, 1, size
, input
)) != size
) {
470 fprintf(stderr
, "Premature EOF while reading runtime.\n");
474 if (!check_runtime_build_id(buf
, size
)) {
475 fprintf(stderr
, "Failed to locate current build_id in runtime: %s\n",
493 save_runtime_to_filehandle(FILE *output
, void *runtime
, size_t runtime_size
,
494 int application_type
)
499 #ifdef LISP_FEATURE_WIN32
501 PIMAGE_DOS_HEADER dos_header
= (PIMAGE_DOS_HEADER
)runtime
;
502 PIMAGE_NT_HEADERS nt_header
= (PIMAGE_NT_HEADERS
)((char *)dos_header
+
503 dos_header
->e_lfanew
);
506 switch (application_type
) {
508 sub_system
= IMAGE_SUBSYSTEM_WINDOWS_CUI
;
511 sub_system
= IMAGE_SUBSYSTEM_WINDOWS_GUI
;
514 fprintf(stderr
, "Invalid application type %d\n", application_type
);
518 nt_header
->OptionalHeader
.Subsystem
= sub_system
;
522 if (runtime_size
!= fwrite(runtime
, 1, runtime_size
, output
)) {
523 perror("Error saving runtime");
527 padding
= (os_vm_page_size
- (runtime_size
% os_vm_page_size
)) & ~os_vm_page_size
;
529 padbytes
= successful_malloc(padding
);
530 memset(padbytes
, 0, padding
);
531 if (padding
!= fwrite(padbytes
, 1, padding
, output
)) {
532 perror("Error saving runtime");
543 prepare_to_save(char *filename
, boolean prepend_runtime
, void **runtime_bytes
,
544 size_t *runtime_size
)
549 if (prepend_runtime
) {
550 runtime_path
= os_get_runtime_executable_path(0);
552 if (runtime_path
== NULL
&& saved_runtime_path
== NULL
) {
553 fprintf(stderr
, "Unable to get default runtime path.\n");
557 if (runtime_path
== NULL
)
558 *runtime_bytes
= load_runtime(saved_runtime_path
, runtime_size
);
560 *runtime_bytes
= load_runtime(runtime_path
, runtime_size
);
564 if (*runtime_bytes
== NULL
)
568 file
= open_core_for_saving(filename
);
570 free(*runtime_bytes
);
579 save(char *filename
, lispobj init_function
, boolean prepend_runtime
,
580 boolean save_runtime_options
, boolean compressed
, int compression_level
,
581 int application_type
)
584 void *runtime_bytes
= NULL
;
587 file
= prepare_to_save(filename
, prepend_runtime
, &runtime_bytes
, &runtime_size
);
592 save_runtime_to_filehandle(file
, runtime_bytes
, runtime_size
, application_type
);
594 return save_to_filehandle(file
, filename
, init_function
, prepend_runtime
,
595 save_runtime_options
,
596 compressed
? compressed
: COMPRESSION_LEVEL_NONE
);