]> gitweb.factorcode.org Git - factor.git/blob - vm/image.cpp
Merge branch 'master' of git://factorcode.org/git/factor
[factor.git] / vm / image.cpp
1 #include "master.hpp"
2
3 namespace factor
4 {
5
6 /* Certain special objects in the image are known to the runtime */
7 void factor_vm::init_objects(image_header *h)
8 {
9         memcpy(userenv,h->userenv,sizeof(userenv));
10
11         true_object = h->true_object;
12         bignum_zero = h->bignum_zero;
13         bignum_pos_one = h->bignum_pos_one;
14         bignum_neg_one = h->bignum_neg_one;
15 }
16
17 void factor_vm::load_data_heap(FILE *file, image_header *h, vm_parameters *p)
18 {
19         cell good_size = h->data_size + (1 << 20);
20
21         if(good_size > p->tenured_size)
22                 p->tenured_size = good_size;
23
24         init_data_heap(p->young_size,
25                 p->aging_size,
26                 p->tenured_size,
27                 p->secure_gc);
28
29         clear_gc_stats();
30
31         fixnum bytes_read = fread((void*)data->tenured->start,1,h->data_size,file);
32
33         if((cell)bytes_read != h->data_size)
34         {
35                 print_string("truncated image: ");
36                 print_fixnum(bytes_read);
37                 print_string(" bytes read, ");
38                 print_cell(h->data_size);
39                 print_string(" bytes expected\n");
40                 fatal_error("load_data_heap failed",0);
41         }
42
43         data->tenured->here = data->tenured->start + h->data_size;
44 }
45
46 void factor_vm::load_code_heap(FILE *file, image_header *h, vm_parameters *p)
47 {
48         if(h->code_size > p->code_size)
49                 fatal_error("Code heap too small to fit image",h->code_size);
50
51         init_code_heap(p->code_size);
52
53         if(h->code_size != 0)
54         {
55                 size_t bytes_read = fread(code->first_block(),1,h->code_size,file);
56                 if(bytes_read != h->code_size)
57                 {
58                         print_string("truncated image: ");
59                         print_fixnum(bytes_read);
60                         print_string(" bytes read, ");
61                         print_cell(h->code_size);
62                         print_string(" bytes expected\n");
63                         fatal_error("load_code_heap failed",0);
64                 }
65         }
66
67         code->build_free_list(h->code_size);
68 }
69
70 void factor_vm::data_fixup(cell *handle, cell data_relocation_base)
71 {
72         if(immediate_p(*handle))
73                 return;
74
75         *handle += (data->tenured->start - data_relocation_base);
76 }
77
78 template<typename Type> void factor_vm::code_fixup(Type **handle, cell code_relocation_base)
79 {
80         Type *ptr = *handle;
81         Type *new_ptr = (Type *)(((cell)ptr) + (code->seg->start - code_relocation_base));
82         *handle = new_ptr;
83 }
84
85 void factor_vm::fixup_word(word *word, cell code_relocation_base)
86 {
87         if(word->code)
88                 code_fixup(&word->code,code_relocation_base);
89         if(word->profiling)
90                 code_fixup(&word->profiling,code_relocation_base);
91         code_fixup(&word->xt,code_relocation_base);
92 }
93
94 void factor_vm::fixup_quotation(quotation *quot, cell code_relocation_base)
95 {
96         if(quot->code)
97         {
98                 code_fixup(&quot->xt,code_relocation_base);
99                 code_fixup(&quot->code,code_relocation_base);
100         }
101         else
102                 quot->xt = (void *)lazy_jit_compile;
103 }
104
105 void factor_vm::fixup_alien(alien *d)
106 {
107         if(!to_boolean(d->base)) d->expired = true_object;
108 }
109
110 struct stack_frame_fixupper {
111         factor_vm *parent;
112         cell code_relocation_base;
113
114         explicit stack_frame_fixupper(factor_vm *parent_, cell code_relocation_base_) :
115                 parent(parent_), code_relocation_base(code_relocation_base_) {}
116         void operator()(stack_frame *frame)
117         {
118                 parent->code_fixup(&frame->xt,code_relocation_base);
119                 parent->code_fixup(&FRAME_RETURN_ADDRESS(frame,parent),code_relocation_base);
120         }
121 };
122
123 void factor_vm::fixup_callstack_object(callstack *stack, cell code_relocation_base)
124 {
125         stack_frame_fixupper fixupper(this,code_relocation_base);
126         iterate_callstack_object(stack,fixupper);
127 }
128
129 struct object_fixupper {
130         factor_vm *parent;
131         cell data_relocation_base;
132
133         explicit object_fixupper(factor_vm *parent_, cell data_relocation_base_) :
134                 parent(parent_), data_relocation_base(data_relocation_base_) { }
135
136         void operator()(cell *scan)
137         {
138                 parent->data_fixup(scan,data_relocation_base);
139         }
140 };
141
142 /* Initialize an object in a newly-loaded image */
143 void factor_vm::relocate_object(object *object,
144         cell data_relocation_base,
145         cell code_relocation_base)
146 {
147         cell hi_tag = object->h.hi_tag();
148         
149         /* Tuple relocation is a bit trickier; we have to fix up the
150         layout object before we can get the tuple size, so do_slots is
151         out of the question */
152         if(hi_tag == TUPLE_TYPE)
153         {
154                 tuple *t = (tuple *)object;
155                 data_fixup(&t->layout,data_relocation_base);
156
157                 cell *scan = t->data();
158                 cell *end = (cell *)((cell)object + untagged_object_size(object));
159
160                 for(; scan < end; scan++)
161                         data_fixup(scan,data_relocation_base);
162         }
163         else
164         {
165                 object_fixupper fixupper(this,data_relocation_base);
166                 do_slots((cell)object,fixupper);
167
168                 switch(hi_tag)
169                 {
170                 case WORD_TYPE:
171                         fixup_word((word *)object,code_relocation_base);
172                         break;
173                 case QUOTATION_TYPE:
174                         fixup_quotation((quotation *)object,code_relocation_base);
175                         break;
176                 case DLL_TYPE:
177                         ffi_dlopen((dll *)object);
178                         break;
179                 case ALIEN_TYPE:
180                         fixup_alien((alien *)object);
181                         break;
182                 case CALLSTACK_TYPE:
183                         fixup_callstack_object((callstack *)object,code_relocation_base);
184                         break;
185                 }
186         }
187 }
188
189 /* Since the image might have been saved with a different base address than
190 where it is loaded, we need to fix up pointers in the image. */
191 void factor_vm::relocate_data(cell data_relocation_base, cell code_relocation_base)
192 {
193         for(cell i = 0; i < USER_ENV; i++)
194                 data_fixup(&userenv[i],data_relocation_base);
195
196         data_fixup(&true_object,data_relocation_base);
197         data_fixup(&bignum_zero,data_relocation_base);
198         data_fixup(&bignum_pos_one,data_relocation_base);
199         data_fixup(&bignum_neg_one,data_relocation_base);
200
201         cell obj = data->tenured->start;
202
203         while(obj)
204         {
205                 relocate_object((object *)obj,data_relocation_base,code_relocation_base);
206                 data->tenured->record_object_start_offset((object *)obj);
207                 obj = data->tenured->next_object_after(this,obj);
208         }
209 }
210
211 void factor_vm::fixup_code_block(code_block *compiled, cell data_relocation_base)
212 {
213         /* relocate literal table data */
214         data_fixup(&compiled->owner,data_relocation_base);
215         data_fixup(&compiled->literals,data_relocation_base);
216         data_fixup(&compiled->relocation,data_relocation_base);
217
218         relocate_code_block(compiled);
219 }
220
221 struct code_block_fixupper {
222         factor_vm *parent;
223         cell data_relocation_base;
224
225         code_block_fixupper(factor_vm *parent_, cell data_relocation_base_) :
226                 parent(parent_), data_relocation_base(data_relocation_base_) { }
227
228         void operator()(code_block *compiled)
229         {
230                 parent->fixup_code_block(compiled,data_relocation_base);
231         }
232 };
233
234 void factor_vm::relocate_code(cell data_relocation_base)
235 {
236         code_block_fixupper fixupper(this,data_relocation_base);
237         iterate_code_heap(fixupper);
238 }
239
240 /* Read an image file from disk, only done once during startup */
241 /* This function also initializes the data and code heaps */
242 void factor_vm::load_image(vm_parameters *p)
243 {
244         FILE *file = OPEN_READ(p->image_path);
245         if(file == NULL)
246         {
247                 print_string("Cannot open image file: "); print_native_string(p->image_path); nl();
248                 print_string(strerror(errno)); nl();
249                 exit(1);
250         }
251
252         image_header h;
253         if(fread(&h,sizeof(image_header),1,file) != 1)
254                 fatal_error("Cannot read image header",0);
255
256         if(h.magic != image_magic)
257                 fatal_error("Bad image: magic number check failed",h.magic);
258
259         if(h.version != image_version)
260                 fatal_error("Bad image: version number check failed",h.version);
261         
262         load_data_heap(file,&h,p);
263         load_code_heap(file,&h,p);
264
265         fclose(file);
266
267         init_objects(&h);
268
269         relocate_data(h.data_relocation_base,h.code_relocation_base);
270         relocate_code(h.data_relocation_base);
271
272         /* Store image path name */
273         userenv[IMAGE_ENV] = allot_alien(false_object,(cell)p->image_path);
274 }
275
276 /* Save the current image to disk */
277 bool factor_vm::save_image(const vm_char *filename)
278 {
279         FILE* file;
280         image_header h;
281
282         file = OPEN_WRITE(filename);
283         if(file == NULL)
284         {
285                 print_string("Cannot open image file: "); print_native_string(filename); nl();
286                 print_string(strerror(errno)); nl();
287                 return false;
288         }
289
290         h.magic = image_magic;
291         h.version = image_version;
292         h.data_relocation_base = data->tenured->start;
293         h.data_size = data->tenured->here - data->tenured->start;
294         h.code_relocation_base = code->seg->start;
295         h.code_size = code->heap_size();
296
297         h.true_object = true_object;
298         h.bignum_zero = bignum_zero;
299         h.bignum_pos_one = bignum_pos_one;
300         h.bignum_neg_one = bignum_neg_one;
301
302         for(cell i = 0; i < USER_ENV; i++)
303                 h.userenv[i] = (save_env_p(i) ? userenv[i] : false_object);
304
305         bool ok = true;
306
307         if(fwrite(&h,sizeof(image_header),1,file) != 1) ok = false;
308         if(fwrite((void*)data->tenured->start,h.data_size,1,file) != 1) ok = false;
309         if(fwrite(code->first_block(),h.code_size,1,file) != 1) ok = false;
310         if(fclose(file)) ok = false;
311
312         if(!ok)
313         {
314                 print_string("save-image failed: "); print_string(strerror(errno)); nl();
315         }
316
317         return ok;
318 }
319
320 void factor_vm::primitive_save_image()
321 {
322         /* do a full GC to push everything into tenured space */
323         primitive_compact_gc();
324
325         gc_root<byte_array> path(dpop(),this);
326         path.untag_check(this);
327         save_image((vm_char *)(path.untagged() + 1));
328 }
329
330 void factor_vm::primitive_save_image_and_exit()
331 {
332         /* We unbox this before doing anything else. This is the only point
333         where we might throw an error, so we have to throw an error here since
334         later steps destroy the current image. */
335         gc_root<byte_array> path(dpop(),this);
336         path.untag_check(this);
337
338         /* strip out userenv data which is set on startup anyway */
339         for(cell i = 0; i < USER_ENV; i++)
340                 if(!save_env_p(i)) userenv[i] = false_object;
341
342         gc(collect_full_op,
343                 0, /* requested size */
344                 false, /* discard objects only reachable from stacks */
345                 true /* compact the code heap */);
346
347         /* Save the image */
348         if(save_image((vm_char *)(path.untagged() + 1)))
349                 exit(0);
350         else
351                 exit(1);
352 }
353
354 }