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