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