]> gitweb.factorcode.org Git - factor.git/blob - vm/image.cpp
vm: support self-executing image file
[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(special_objects,h->special_objects,sizeof(special_objects));
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         p->tenured_size = std::max((h->data_size * 3) / 2,p->tenured_size);
20
21         init_data_heap(p->young_size,
22                 p->aging_size,
23                 p->tenured_size);
24
25         fixnum bytes_read = safe_fread((void*)data->tenured->start,1,h->data_size,file);
26
27         if((cell)bytes_read != h->data_size)
28         {
29                 std::cout << "truncated image: " << bytes_read << " bytes read, ";
30                 std::cout << h->data_size << " bytes expected\n";
31                 fatal_error("load_data_heap failed",0);
32         }
33
34         data->tenured->initial_free_list(h->data_size);
35 }
36
37 void factor_vm::load_code_heap(FILE *file, image_header *h, vm_parameters *p)
38 {
39         if(h->code_size > p->code_size)
40                 fatal_error("Code heap too small to fit image",h->code_size);
41
42         init_code_heap(p->code_size);
43
44         if(h->code_size != 0)
45         {
46                 size_t bytes_read = safe_fread(code->allocator->first_block(),1,h->code_size,file);
47                 if(bytes_read != h->code_size)
48                 {
49                         std::cout << "truncated image: " << bytes_read << " bytes read, ";
50                         std::cout << h->code_size << " bytes expected\n";
51                         fatal_error("load_code_heap failed",0);
52                 }
53         }
54
55         code->allocator->initial_free_list(h->code_size);
56         code->initialize_all_blocks_set();
57 }
58
59 struct startup_fixup {
60         cell data_offset;
61         cell code_offset;
62
63         explicit startup_fixup(cell data_offset_, cell code_offset_) :
64                 data_offset(data_offset_), code_offset(code_offset_) {}
65
66         object *fixup_data(object *obj)
67         {
68                 return (object *)((cell)obj + data_offset);
69         }
70
71         code_block *fixup_code(code_block *obj)
72         {
73                 return (code_block *)((cell)obj + code_offset);
74         }
75
76         object *translate_data(const object *obj)
77         {
78                 return fixup_data((object *)obj);
79         }
80
81         code_block *translate_code(const code_block *compiled)
82         {
83                 return fixup_code((code_block *)compiled);
84         }
85
86         cell size(const object *obj)
87         {
88                 return obj->size(*this);
89         }
90
91         cell size(code_block *compiled)
92         {
93                 return compiled->size(*this);
94         }
95 };
96
97 struct start_object_updater {
98         factor_vm *parent;
99         startup_fixup fixup;
100         slot_visitor<startup_fixup> data_visitor;
101         code_block_visitor<startup_fixup> code_visitor;
102
103         start_object_updater(factor_vm *parent_, startup_fixup fixup_) :
104                 parent(parent_),
105                 fixup(fixup_),
106                 data_visitor(slot_visitor<startup_fixup>(parent_,fixup_)),
107                 code_visitor(code_block_visitor<startup_fixup>(parent_,fixup_)) {}
108
109         void operator()(object *obj, cell size)
110         {
111                 parent->data->tenured->starts.record_object_start_offset(obj);
112
113                 data_visitor.visit_slots(obj);
114
115                 switch(obj->type())
116                 {
117                 case ALIEN_TYPE:
118                         {
119
120                                 alien *ptr = (alien *)obj;
121
122                                 if(to_boolean(ptr->base))
123                                         ptr->update_address();
124                                 else
125                                         ptr->expired = parent->true_object;
126                                 break;
127                         }
128                 case DLL_TYPE:
129                         {
130                                 parent->ffi_dlopen((dll *)obj);
131                                 break;
132                         }
133                 default:
134                         {
135                                 code_visitor.visit_object_code_block(obj);
136                                 break;
137                         }
138                 }
139         }
140 };
141
142 void factor_vm::fixup_data(cell data_offset, cell code_offset)
143 {
144         startup_fixup fixup(data_offset,code_offset);
145         slot_visitor<startup_fixup> data_workhorse(this,fixup);
146         data_workhorse.visit_roots();
147
148         start_object_updater updater(this,fixup);
149         data->tenured->iterate(updater,fixup);
150 }
151
152 struct startup_code_block_relocation_visitor {
153         factor_vm *parent;
154         startup_fixup fixup;
155         slot_visitor<startup_fixup> data_visitor;
156
157         startup_code_block_relocation_visitor(factor_vm *parent_, startup_fixup fixup_) :
158                 parent(parent_),
159                 fixup(fixup_),
160                 data_visitor(slot_visitor<startup_fixup>(parent_,fixup_)) {}
161
162         void operator()(instruction_operand op)
163         {
164                 code_block *compiled = op.compiled;
165                 cell old_offset = op.rel_offset() + (cell)compiled->entry_point() - fixup.code_offset;
166
167                 switch(op.rel_type())
168                 {
169                 case RT_LITERAL:
170                         {
171                                 cell value = op.load_value(old_offset);
172                                 if(immediate_p(value))
173                                         op.store_value(value);
174                                 else
175                                         op.store_value(RETAG(fixup.fixup_data(untag<object>(value)),TAG(value)));
176                                 break;
177                         }
178                 case RT_ENTRY_POINT:
179                 case RT_ENTRY_POINT_PIC:
180                 case RT_ENTRY_POINT_PIC_TAIL:
181                 case RT_HERE:
182                         {
183                                 cell value = op.load_value(old_offset);
184                                 cell offset = TAG(value);
185                                 code_block *compiled = (code_block *)UNTAG(value);
186                                 op.store_value((cell)fixup.fixup_code(compiled) + offset);
187                                 break;
188                         }
189                 case RT_UNTAGGED:
190                         break;
191                 default:
192                         parent->store_external_address(op);
193                         break;
194                 }
195         }
196 };
197
198 struct startup_code_block_updater {
199         factor_vm *parent;
200         startup_fixup fixup;
201
202         startup_code_block_updater(factor_vm *parent_, startup_fixup fixup_) :
203                 parent(parent_), fixup(fixup_) {}
204
205         void operator()(code_block *compiled, cell size)
206         {
207                 slot_visitor<startup_fixup> data_visitor(parent,fixup);
208                 data_visitor.visit_code_block_objects(compiled);
209
210                 startup_code_block_relocation_visitor code_visitor(parent,fixup);
211                 compiled->each_instruction_operand(code_visitor);
212         }
213 };
214
215 void factor_vm::fixup_code(cell data_offset, cell code_offset)
216 {
217         startup_fixup fixup(data_offset,code_offset);
218         startup_code_block_updater updater(this,fixup);
219         code->allocator->iterate(updater,fixup);
220 }
221
222 FILE* factor_vm::open_image(vm_parameters *p)
223 {
224         if (p->embedded_image)
225         {
226                 FILE *file = OPEN_READ(p->executable_path);
227                 if (file == NULL)
228                 {
229                         std::cout << "Cannot open embedded image" << std::endl;
230                         std::cout << strerror(errno) << std::endl;
231                         exit(1);
232                 }
233                 safe_fseek(file, -sizeof(embedded_image_footer), SEEK_END);
234                 embedded_image_footer footer;
235                 safe_fread(&footer, sizeof(embedded_image_footer), 1, file);
236                 if (footer.magic != image_magic)
237                 {
238                         std::cout << "No embedded image" << std::endl;
239                         exit(1);
240                 }
241                 safe_fseek(file, footer.image_offset, SEEK_SET);
242                 return file;
243         }
244         else
245                 return OPEN_READ(p->image_path);
246 }
247
248 /* Read an image file from disk, only done once during startup */
249 /* This function also initializes the data and code heaps */
250 void factor_vm::load_image(vm_parameters *p)
251 {
252         FILE *file = open_image(p);
253         if(file == NULL)
254         {
255                 std::cout << "Cannot open image file: " << p->image_path << std::endl;
256                 std::cout << strerror(errno) << std::endl;
257                 exit(1);
258         }
259
260         image_header h;
261         if(safe_fread(&h,sizeof(image_header),1,file) != 1)
262                 fatal_error("Cannot read image header",0);
263
264         if(h.magic != image_magic)
265                 fatal_error("Bad image: magic number check failed",h.magic);
266
267         if(h.version != image_version)
268                 fatal_error("Bad image: version number check failed",h.version);
269         
270         load_data_heap(file,&h,p);
271         load_code_heap(file,&h,p);
272
273         safe_fclose(file);
274
275         init_objects(&h);
276
277         cell data_offset = data->tenured->start - h.data_relocation_base;
278         cell code_offset = code->allocator->start - h.code_relocation_base;
279
280         fixup_data(data_offset,code_offset);
281         fixup_code(data_offset,code_offset);
282
283         /* Store image path name */
284         special_objects[OBJ_IMAGE] = allot_alien(false_object,(cell)p->image_path);
285 }
286
287 /* Save the current image to disk */
288 bool factor_vm::save_image(const vm_char *saving_filename, const vm_char *filename)
289 {
290         FILE* file;
291         image_header h;
292
293         file = OPEN_WRITE(saving_filename);
294         if(file == NULL)
295         {
296                 std::cout << "Cannot open image file: " << saving_filename << std::endl;
297                 std::cout << strerror(errno) << std::endl;
298                 return false;
299         }
300
301         h.magic = image_magic;
302         h.version = image_version;
303         h.data_relocation_base = data->tenured->start;
304         h.data_size = data->tenured->occupied_space();
305         h.code_relocation_base = code->allocator->start;
306         h.code_size = code->allocator->occupied_space();
307
308         h.true_object = true_object;
309         h.bignum_zero = bignum_zero;
310         h.bignum_pos_one = bignum_pos_one;
311         h.bignum_neg_one = bignum_neg_one;
312
313         for(cell i = 0; i < special_object_count; i++)
314                 h.special_objects[i] = (save_special_p(i) ? special_objects[i] : false_object);
315
316         bool ok = true;
317
318         if(safe_fwrite(&h,sizeof(image_header),1,file) != 1) ok = false;
319         if(safe_fwrite((void*)data->tenured->start,h.data_size,1,file) != 1) ok = false;
320         if(safe_fwrite(code->allocator->first_block(),h.code_size,1,file) != 1) ok = false;
321         safe_fclose(file);
322
323         if(!ok)
324                 std::cout << "save-image failed: " << strerror(errno) << std::endl;
325         else
326                 move_file(saving_filename,filename); 
327
328         return ok;
329 }
330
331 void factor_vm::primitive_save_image()
332 {
333         /* do a full GC to push everything into tenured space */
334         primitive_compact_gc();
335
336         data_root<byte_array> path2(ctx->pop(),this);
337         path2.untag_check(this);
338         data_root<byte_array> path1(ctx->pop(),this);
339         path1.untag_check(this);
340         save_image((vm_char *)(path1.untagged() + 1 ),(vm_char *)(path2.untagged() + 1));
341 }
342
343 void factor_vm::primitive_save_image_and_exit()
344 {
345         /* We unbox this before doing anything else. This is the only point
346         where we might throw an error, so we have to throw an error here since
347         later steps destroy the current image. */
348         data_root<byte_array> path2(ctx->pop(),this);
349         path2.untag_check(this);
350         data_root<byte_array> path1(ctx->pop(),this);
351         path1.untag_check(this);
352
353         /* strip out special_objects data which is set on startup anyway */
354         for(cell i = 0; i < special_object_count; i++)
355                 if(!save_special_p(i)) special_objects[i] = false_object;
356
357         gc(collect_compact_op,
358                 0, /* requested size */
359                 false /* discard objects only reachable from stacks */);
360
361         /* Save the image */
362         if(save_image((vm_char *)(path1.untagged() + 1), (vm_char *)(path2.untagged() + 1)))
363                 exit(0);
364         else
365                 exit(1);
366 }
367
368 bool factor_vm::embedded_image_p()
369 {
370         const vm_char *vm_path = vm_executable_path();
371         if (!vm_path)
372                 return false;
373         FILE *file = OPEN_READ(vm_path);
374         if (!file)
375                 return false;
376         safe_fseek(file, -sizeof(embedded_image_footer), SEEK_END);
377         embedded_image_footer footer;
378         safe_fread(&footer, sizeof(embedded_image_footer), 1, file);
379         fclose(file);
380         return footer.magic == image_magic;
381 }
382
383 }