]> gitweb.factorcode.org Git - factor.git/blob - vm/image.cpp
vm: implement frame-based SEH for 64-bit Windows
[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 }
57
58 struct data_fixupper {
59         cell offset;
60
61         explicit data_fixupper(cell offset_) : offset(offset_) {}
62
63         object *operator()(object *obj)
64         {
65                 return (object *)((char *)obj + offset);
66         }
67 };
68
69 struct code_fixupper {
70         cell offset;
71
72         explicit code_fixupper(cell offset_) : offset(offset_) {}
73
74         code_block *operator()(code_block *compiled)
75         {
76                 return (code_block *)((char *)compiled + offset);
77         }
78 };
79
80 static inline cell tuple_size_with_fixup(cell offset, object *obj)
81 {
82         tuple_layout *layout = (tuple_layout *)((char *)UNTAG(((tuple *)obj)->layout) + offset);
83         return tuple_size(layout);
84 }
85
86 struct fixup_sizer {
87         cell offset;
88
89         explicit fixup_sizer(cell offset_) : offset(offset_) {}
90
91         cell operator()(object *obj)
92         {
93                 if(obj->type() == TUPLE_TYPE)
94                         return align(tuple_size_with_fixup(offset,obj),data_alignment);
95                 else
96                         return obj->size();
97         }
98 };
99
100 struct object_fixupper {
101         factor_vm *parent;
102         cell data_offset;
103         slot_visitor<data_fixupper> data_visitor;
104         code_block_visitor<code_fixupper> code_visitor;
105
106         object_fixupper(factor_vm *parent_, cell data_offset_, cell code_offset_) :
107                 parent(parent_),
108                 data_offset(data_offset_),
109                 data_visitor(slot_visitor<data_fixupper>(parent_,data_fixupper(data_offset_))),
110                 code_visitor(code_block_visitor<code_fixupper>(parent_,code_fixupper(code_offset_))) {}
111
112         void operator()(object *obj, cell size)
113         {
114                 parent->data->tenured->starts.record_object_start_offset(obj);
115
116                 switch(obj->type())
117                 {
118                 case ALIEN_TYPE:
119                         {
120                                 cell payload_start = obj->binary_payload_start();
121                                 data_visitor.visit_slots(obj,payload_start);
122
123                                 alien *ptr = (alien *)obj;
124
125                                 if(to_boolean(ptr->base))
126                                         ptr->update_address();
127                                 else
128                                         ptr->expired = parent->true_object;
129                                 break;
130                         }
131                 case DLL_TYPE:
132                         {
133                                 cell payload_start = obj->binary_payload_start();
134                                 data_visitor.visit_slots(obj,payload_start);
135
136                                 parent->ffi_dlopen((dll *)obj);
137                                 break;
138                         }
139                 case TUPLE_TYPE:
140                         {
141                                 cell payload_start = tuple_size_with_fixup(data_offset,obj);
142                                 data_visitor.visit_slots(obj,payload_start);
143                                 break;
144                         }
145                 default:
146                         {
147                                 cell payload_start = obj->binary_payload_start();
148                                 data_visitor.visit_slots(obj,payload_start);
149                                 code_visitor.visit_object_code_block(obj);
150                                 break;
151                         }
152                 }
153         }
154 };
155
156 void factor_vm::fixup_data(cell data_offset, cell code_offset)
157 {
158         slot_visitor<data_fixupper> data_workhorse(this,data_fixupper(data_offset));
159         data_workhorse.visit_roots();
160
161         object_fixupper fixupper(this,data_offset,code_offset);
162         fixup_sizer sizer(data_offset);
163         data->tenured->iterate(fixupper,sizer);
164 }
165
166 struct code_block_fixup_relocation_visitor {
167         factor_vm *parent;
168         cell code_offset;
169         slot_visitor<data_fixupper> data_visitor;
170         code_fixupper code_visitor;
171
172         code_block_fixup_relocation_visitor(factor_vm *parent_, cell data_offset_, cell code_offset_) :
173                 parent(parent_),
174                 code_offset(code_offset_),
175                 data_visitor(slot_visitor<data_fixupper>(parent_,data_fixupper(data_offset_))),
176                 code_visitor(code_fixupper(code_offset_)) {}
177
178         void operator()(instruction_operand op)
179         {
180                 code_block *compiled = op.parent_code_block();
181                 cell old_offset = op.rel_offset() + (cell)compiled->entry_point() - code_offset;
182
183                 switch(op.rel_type())
184                 {
185                 case RT_LITERAL:
186                         op.store_value(data_visitor.visit_pointer(op.load_value(old_offset)));
187                         break;
188                 case RT_ENTRY_POINT:
189                 case RT_ENTRY_POINT_PIC:
190                 case RT_ENTRY_POINT_PIC_TAIL:
191                         op.store_code_block(code_visitor(op.load_code_block(old_offset)));
192                         break;
193                 case RT_HERE:
194                         op.store_value(op.load_value(old_offset) + code_offset);
195                         break;
196                 case RT_UNTAGGED:
197                         break;
198                 default:
199                         parent->store_external_address(op);
200                         break;
201                 }
202         }
203 };
204
205 struct code_block_fixupper {
206         factor_vm *parent;
207         cell data_offset;
208         cell code_offset;
209
210         code_block_fixupper(factor_vm *parent_, cell data_offset_, cell code_offset_) :
211                 parent(parent_),
212                 data_offset(data_offset_),
213                 code_offset(code_offset_) {}
214
215         void operator()(code_block *compiled, cell size)
216         {
217                 slot_visitor<data_fixupper> data_visitor(parent,data_fixupper(data_offset));
218                 data_visitor.visit_code_block_objects(compiled);
219
220                 code_block_fixup_relocation_visitor code_visitor(parent,data_offset,code_offset);
221                 compiled->each_instruction_operand(code_visitor);
222         }
223 };
224
225 void factor_vm::fixup_code(cell data_offset, cell code_offset)
226 {
227         code_block_fixupper fixupper(this,data_offset,code_offset);
228         code->allocator->iterate(fixupper);
229 }
230
231 /* Read an image file from disk, only done once during startup */
232 /* This function also initializes the data and code heaps */
233 void factor_vm::load_image(vm_parameters *p)
234 {
235         FILE *file = OPEN_READ(p->image_path);
236         if(file == NULL)
237         {
238                 std::cout << "Cannot open image file: " << p->image_path << std::endl;
239                 std::cout << strerror(errno) << std::endl;
240                 exit(1);
241         }
242
243         image_header h;
244         if(safe_fread(&h,sizeof(image_header),1,file) != 1)
245                 fatal_error("Cannot read image header",0);
246
247         if(h.magic != image_magic)
248                 fatal_error("Bad image: magic number check failed",h.magic);
249
250         if(h.version != image_version)
251                 fatal_error("Bad image: version number check failed",h.version);
252         
253         load_data_heap(file,&h,p);
254         load_code_heap(file,&h,p);
255
256         safe_fclose(file);
257
258         init_objects(&h);
259
260         cell data_offset = data->tenured->start - h.data_relocation_base;
261         cell code_offset = code->allocator->start - h.code_relocation_base;
262
263         fixup_data(data_offset,code_offset);
264         fixup_code(data_offset,code_offset);
265
266         /* Store image path name */
267         special_objects[OBJ_IMAGE] = allot_alien(false_object,(cell)p->image_path);
268 }
269
270 /* Save the current image to disk */
271 bool factor_vm::save_image(const vm_char *saving_filename, const vm_char *filename)
272 {
273         FILE* file;
274         image_header h;
275
276         file = OPEN_WRITE(saving_filename);
277         if(file == NULL)
278         {
279                 std::cout << "Cannot open image file: " << saving_filename << std::endl;
280                 std::cout << strerror(errno) << std::endl;
281                 return false;
282         }
283
284         h.magic = image_magic;
285         h.version = image_version;
286         h.data_relocation_base = data->tenured->start;
287         h.data_size = data->tenured->occupied_space();
288         h.code_relocation_base = code->allocator->start;
289         h.code_size = code->allocator->occupied_space();
290
291         h.true_object = true_object;
292         h.bignum_zero = bignum_zero;
293         h.bignum_pos_one = bignum_pos_one;
294         h.bignum_neg_one = bignum_neg_one;
295
296         for(cell i = 0; i < special_object_count; i++)
297                 h.special_objects[i] = (save_special_p(i) ? special_objects[i] : false_object);
298
299         bool ok = true;
300
301         if(safe_fwrite(&h,sizeof(image_header),1,file) != 1) ok = false;
302         if(safe_fwrite((void*)data->tenured->start,h.data_size,1,file) != 1) ok = false;
303         if(safe_fwrite(code->allocator->first_block(),h.code_size,1,file) != 1) ok = false;
304         safe_fclose(file);
305
306         if(!ok)
307                 std::cout << "save-image failed: " << strerror(errno) << std::endl;
308         else
309                 move_file(saving_filename,filename); 
310
311         return ok;
312 }
313
314 void factor_vm::primitive_save_image()
315 {
316         /* do a full GC to push everything into tenured space */
317         primitive_compact_gc();
318
319         data_root<byte_array> path2(ctx->pop(),this);
320         path2.untag_check(this);
321         data_root<byte_array> path1(ctx->pop(),this);
322         path1.untag_check(this);
323         save_image((vm_char *)(path1.untagged() + 1 ),(vm_char *)(path2.untagged() + 1));
324 }
325
326 void factor_vm::primitive_save_image_and_exit()
327 {
328         /* We unbox this before doing anything else. This is the only point
329         where we might throw an error, so we have to throw an error here since
330         later steps destroy the current image. */
331         data_root<byte_array> path2(ctx->pop(),this);
332         path2.untag_check(this);
333         data_root<byte_array> path1(ctx->pop(),this);
334         path1.untag_check(this);
335
336         /* strip out special_objects data which is set on startup anyway */
337         for(cell i = 0; i < special_object_count; i++)
338                 if(!save_special_p(i)) special_objects[i] = false_object;
339
340         gc(collect_compact_op,
341                 0, /* requested size */
342                 false /* discard objects only reachable from stacks */);
343
344         /* Save the image */
345         if(save_image((vm_char *)(path1.untagged() + 1), (vm_char *)(path2.untagged() + 1)))
346                 exit(0);
347         else
348                 exit(1);
349 }
350
351 }