0.9.13.4:
[sbcl.git] / src / runtime / save.c
1 /*
2  * This software is part of the SBCL system. See the README file for
3  * more information.
4  *
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.
10  */
11
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 <signal.h>
20 #include <sys/file.h>
21
22 #include "sbcl.h"
23 #include "runtime.h"
24 #include "os.h"
25 #include "core.h"
26 #include "globals.h"
27 #include "save.h"
28 #include "dynbind.h"
29 #include "lispregs.h"
30 #include "validate.h"
31 #include "gc-internal.h"
32 #include "thread.h"
33
34 #include "genesis/static-symbols.h"
35 #include "genesis/symbol.h"
36
37 static void
38 write_lispobj(lispobj obj, FILE *file)
39 {
40     fwrite(&obj, sizeof(lispobj), 1, file);
41 }
42
43 static long
44 write_bytes(FILE *file, char *addr, long bytes, os_vm_offset_t file_offset)
45 {
46     long count, here, data;
47
48     bytes = (bytes+os_vm_page_size-1)&~(os_vm_page_size-1);
49
50 #ifdef LISP_FEATURE_WIN32
51     /* touch every single page in the space to force it to be mapped. */
52     for (count = 0; count < bytes; count += 0x1000) {
53         volatile int temp = addr[count];
54     }
55 #endif
56
57     fflush(file);
58     here = ftell(file);
59     fseek(file, 0, SEEK_END);
60     data = (ftell(file)+os_vm_page_size-1)&~(os_vm_page_size-1);
61     fseek(file, data, SEEK_SET);
62
63     while (bytes > 0) {
64         count = fwrite(addr, 1, bytes, file);
65         if (count > 0) {
66             bytes -= count;
67             addr += count;
68         }
69         else {
70             perror("error writing to save file");
71             bytes = 0;
72         }
73     }
74     fflush(file);
75     fseek(file, here, SEEK_SET);
76     return ((data - file_offset) / os_vm_page_size) - 1;
77 }
78
79 static void
80 output_space(FILE *file, int id, lispobj *addr, lispobj *end, os_vm_offset_t file_offset)
81 {
82     int words, bytes, data;
83     static char *names[] = {NULL, "dynamic", "static", "read-only"};
84
85     write_lispobj(id, file);
86     words = end - addr;
87     write_lispobj(words, file);
88
89     bytes = words * sizeof(lispobj);
90
91     printf("writing %d bytes from the %s space at 0x%08lx\n",
92            bytes, names[id], (unsigned long)addr);
93
94     data = write_bytes(file, (char *)addr, bytes, file_offset);
95
96     write_lispobj(data, file);
97     write_lispobj((long)addr / os_vm_page_size, file);
98     write_lispobj((bytes + os_vm_page_size - 1) / os_vm_page_size, file);
99 }
100
101 FILE *
102 open_core_for_saving(char *filename)
103 {
104     /* Open the output file. We don't actually need the file yet, but
105      * the fopen() might fail for some reason, and we want to detect
106      * that and back out before we do anything irreversible. */
107     unlink(filename);
108     return fopen(filename, "wb");
109 }
110
111 boolean
112 save_to_filehandle(FILE *file, char *filename, lispobj init_function,
113                    boolean make_executable)
114 {
115     struct thread *th;
116     os_vm_offset_t core_start_pos, core_end_pos, core_size;
117
118     /* Smash the enclosing state. (Once we do this, there's no good
119      * way to go back, which is a sufficient reason that this ends up
120      * being SAVE-LISP-AND-DIE instead of SAVE-LISP-AND-GO-ON). */
121     printf("[undoing binding stack and other enclosing state... ");
122     fflush(stdout);
123     for_each_thread(th) {       /* XXX really? */
124         unbind_to_here((lispobj *)th->binding_stack_start,th);
125         SetSymbolValue(CURRENT_CATCH_BLOCK, 0,th);
126         SetSymbolValue(CURRENT_UNWIND_PROTECT_BLOCK, 0,th);
127     }
128     printf("done]\n");
129     fflush(stdout);
130
131     /* (Now we can actually start copying ourselves into the output file.) */
132
133     printf("[saving current Lisp image into %s:\n", filename);
134     fflush(stdout);
135
136     core_start_pos = ftell(file);
137     write_lispobj(CORE_MAGIC, file);
138
139     write_lispobj(VERSION_CORE_ENTRY_TYPE_CODE, file);
140     write_lispobj(3, file);
141     write_lispobj(SBCL_CORE_VERSION_INTEGER, file);
142
143     write_lispobj(BUILD_ID_CORE_ENTRY_TYPE_CODE, file);
144     write_lispobj(/* (We're writing the word count of the entry here, and the 2
145           * term is one word for the leading BUILD_ID_CORE_ENTRY_TYPE_CODE
146           * word and one word where we store the count itself.) */
147          2 + strlen((const char *)build_id),
148          file);
149     {
150         unsigned char *p;
151         for (p = build_id; *p; ++p)
152             write_lispobj(*p, file);
153     }
154
155     write_lispobj(NEW_DIRECTORY_CORE_ENTRY_TYPE_CODE, file);
156     write_lispobj(/* (word count = 3 spaces described by 5 words each, plus the
157           * entry type code, plus this count itself) */
158          (5*3)+2, file);
159     output_space(file,
160                  READ_ONLY_CORE_SPACE_ID,
161                  (lispobj *)READ_ONLY_SPACE_START,
162                  (lispobj *)SymbolValue(READ_ONLY_SPACE_FREE_POINTER,0),
163                  core_start_pos);
164     output_space(file,
165                  STATIC_CORE_SPACE_ID,
166                  (lispobj *)STATIC_SPACE_START,
167                  (lispobj *)SymbolValue(STATIC_SPACE_FREE_POINTER,0),
168                  core_start_pos);
169 #ifdef LISP_FEATURE_GENCGC
170     /* Flush the current_region, updating the tables. */
171     gc_alloc_update_all_page_tables();
172     update_dynamic_space_free_pointer();
173 #endif
174 #ifdef reg_ALLOC
175 #ifdef LISP_FEATURE_GENCGC
176     output_space(file,
177                  DYNAMIC_CORE_SPACE_ID,
178                  (lispobj *)DYNAMIC_SPACE_START,
179                  dynamic_space_free_pointer,
180                  core_start_pos);
181 #else
182     output_space(file,
183                  DYNAMIC_CORE_SPACE_ID,
184                  (lispobj *)current_dynamic_space,
185                  dynamic_space_free_pointer,
186                  core_start_pos);
187 #endif
188 #else
189     output_space(file,
190                  DYNAMIC_CORE_SPACE_ID,
191                  (lispobj *)DYNAMIC_SPACE_START,
192                  (lispobj *)SymbolValue(ALLOCATION_POINTER,0),
193                  core_start_pos);
194 #endif
195
196     write_lispobj(INITIAL_FUN_CORE_ENTRY_TYPE_CODE, file);
197     write_lispobj(3, file);
198     write_lispobj(init_function, file);
199
200 #ifdef LISP_FEATURE_GENCGC
201     {
202         size_t size = (last_free_page*sizeof(long)+os_vm_page_size-1)
203             &~(os_vm_page_size-1);
204         long *data = calloc(size, 1);
205         if (data) {
206             long offset;
207             int i;
208             for (i = 0; i < last_free_page; i++) {
209                 data[i] = page_table[i].first_object_offset;
210             }
211             write_lispobj(PAGE_TABLE_CORE_ENTRY_TYPE_CODE, file);
212             write_lispobj(4, file);
213             write_lispobj(size, file);
214             offset = write_bytes(file, (char *) data, size, core_start_pos);
215             write_lispobj(offset, file);
216         }
217     }
218 #endif
219
220     write_lispobj(END_CORE_ENTRY_TYPE_CODE, file);
221
222     /* Write a trailing header, ignored when parsing the core normally.
223      * This is used to locate the start of the core when the runtime is
224      * prepended to it. */
225     fseek(file, 0, SEEK_END);
226     core_end_pos = ftell(file);
227     core_size = core_end_pos - core_start_pos;
228
229     fwrite(&core_size, sizeof(os_vm_offset_t), 1, file);
230     write_lispobj(CORE_MAGIC, file);
231     fclose(file);
232
233 #ifndef LISP_FEATURE_WIN32
234     if (make_executable)
235         chmod (filename, 0755);
236 #endif
237
238     printf("done]\n");
239     exit(0);
240 }
241
242 /* Slurp the executable portion of the runtime into a malloced buffer
243  * and return it.  Places the size in bytes of the runtime into
244  * 'size_out'.  Returns NULL if the runtime cannot be loaded from
245  * 'runtime_path'. */
246 void *
247 load_runtime(char *runtime_path, size_t *size_out)
248 {
249     void *buf = NULL;
250     FILE *input = NULL;
251     size_t size, count;
252     os_vm_offset_t core_offset;
253
254     core_offset = search_for_embedded_core (runtime_path);
255     if ((input = fopen(runtime_path, "rb")) == NULL) {
256         fprintf(stderr, "Unable to open runtime: %s\n", runtime_path);
257         goto lose;
258     }
259
260     fseek(input, 0, SEEK_END);
261     size = (size_t) ftell(input);
262     fseek(input, 0, SEEK_SET);
263
264     if (core_offset != -1 && size > core_offset)
265         size = core_offset;
266
267     buf = successful_malloc(size);
268     if ((count = fread(buf, 1, size, input)) != size) {
269         fprintf(stderr, "Premature EOF while reading runtime.\n");
270         goto lose;
271     }
272
273     fclose(input);
274     *size_out = size;
275     return buf;
276
277 lose:
278     if (input != NULL)
279         fclose(input);
280     if (buf != NULL)
281         free(buf);
282     return NULL;
283 }
284
285 boolean
286 save_runtime_to_filehandle(FILE *output, void *runtime, size_t runtime_size)
287 {
288     size_t padding;
289     void *padbytes;
290
291     fwrite(runtime, 1, runtime_size, output);
292
293     padding = (os_vm_page_size - (runtime_size % os_vm_page_size)) & ~os_vm_page_size;
294     if (padding > 0) {
295         padbytes = successful_malloc(padding);
296         memset(padbytes, 0, padding);
297         fwrite(padbytes, 1, padding, output);
298         free(padbytes);
299     }
300
301     return 1;
302 }
303
304 FILE *
305 prepare_to_save(char *filename, boolean prepend_runtime, void **runtime_bytes,
306                 size_t *runtime_size)
307 {
308     FILE *file;
309     char *runtime_path;
310
311     if (prepend_runtime) {
312         runtime_path = os_get_runtime_executable_path();
313
314         if (runtime_path == NULL) {
315             fprintf(stderr, "Unable to get default runtime path.\n");
316             return NULL;
317         }
318
319         *runtime_bytes = load_runtime(runtime_path, runtime_size);
320         free(runtime_path);
321
322         if (*runtime_bytes == NULL)
323             return 0;
324     }
325
326     file = open_core_for_saving(filename);
327     if (file == NULL) {
328         free(*runtime_bytes);
329         perror(filename);
330         return NULL;
331     }
332
333     return file;
334 }
335
336 boolean
337 save(char *filename, lispobj init_function, boolean prepend_runtime)
338 {
339     FILE *file;
340     void *runtime_bytes = NULL;
341     size_t runtime_size;
342
343     file = prepare_to_save(filename, prepend_runtime, &runtime_bytes, &runtime_size);
344     if (file == NULL)
345         return 1;
346
347     if (prepend_runtime)
348         save_runtime_to_filehandle(file, runtime_bytes, runtime_size);
349
350     return save_to_filehandle(file, filename, init_function, prepend_runtime);
351 }