]> gitweb.factorcode.org Git - factor.git/blob - vm/image.cpp
VM: Fixup cast formatting after clang-format
[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(code->allocator->first_block(), 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 FILE* factor_vm::open_image(vm_parameters* p) {
204   if (p->embedded_image) {
205     FILE* file = OPEN_READ(p->executable_path);
206     if (file == NULL) {
207       std::cout << "Cannot open embedded image" << std::endl;
208       std::cout << strerror(errno) << std::endl;
209       exit(1);
210     }
211     embedded_image_footer footer;
212     if (!read_embedded_image_footer(file, &footer)) {
213       std::cout << "No embedded image" << std::endl;
214       exit(1);
215     }
216     safe_fseek(file, (off_t)footer.image_offset, SEEK_SET);
217     return file;
218   } else
219     return OPEN_READ(p->image_path);
220 }
221
222 /* Read an image file from disk, only done once during startup */
223 /* This function also initializes the data and code heaps */
224 void factor_vm::load_image(vm_parameters* p) {
225   FILE* file = open_image(p);
226   if (file == NULL) {
227     std::cout << "Cannot open image file: " << p->image_path << std::endl;
228     std::cout << strerror(errno) << std::endl;
229     exit(1);
230   }
231
232   image_header h;
233   if (safe_fread(&h, sizeof(image_header), 1, file) != 1)
234     fatal_error("Cannot read image header", 0);
235
236   if (h.magic != image_magic)
237     fatal_error("Bad image: magic number check failed", h.magic);
238
239   if (h.version != image_version)
240     fatal_error("Bad image: version number check failed", h.version);
241
242   load_data_heap(file, &h, p);
243   load_code_heap(file, &h, p);
244
245   safe_fclose(file);
246
247   init_objects(&h);
248
249   cell data_offset = data->tenured->start - h.data_relocation_base;
250   cell code_offset = code->allocator->start - h.code_relocation_base;
251
252   fixup_data(data_offset, code_offset);
253   fixup_code(data_offset, code_offset);
254
255   /* Store image path name */
256   special_objects[OBJ_IMAGE] = allot_alien(false_object, (cell)p->image_path);
257 }
258
259 /* Save the current image to disk */
260 bool factor_vm::save_image(const vm_char* saving_filename,
261                            const vm_char* filename) {
262   FILE* file;
263   image_header h;
264
265   file = OPEN_WRITE(saving_filename);
266   if (file == NULL) {
267     std::cout << "Cannot open image file: " << saving_filename << std::endl;
268     std::cout << strerror(errno) << std::endl;
269     return false;
270   }
271
272   h.magic = image_magic;
273   h.version = image_version;
274   h.data_relocation_base = data->tenured->start;
275   h.data_size = data->tenured->occupied_space();
276   h.code_relocation_base = code->allocator->start;
277   h.code_size = code->allocator->occupied_space();
278
279   h.true_object = true_object;
280   h.bignum_zero = bignum_zero;
281   h.bignum_pos_one = bignum_pos_one;
282   h.bignum_neg_one = bignum_neg_one;
283
284   for (cell i = 0; i < special_object_count; i++)
285     h.special_objects[i] =
286         (save_special_p(i) ? special_objects[i] : false_object);
287
288   bool ok = true;
289
290   if (safe_fwrite(&h, sizeof(image_header), 1, file) != 1)
291     ok = false;
292   if (safe_fwrite((void*)data->tenured->start, h.data_size, 1, file) != 1)
293     ok = false;
294   if (safe_fwrite(code->allocator->first_block(), h.code_size, 1, file) != 1)
295     ok = false;
296   safe_fclose(file);
297
298   if (!ok)
299     std::cout << "save-image failed: " << strerror(errno) << std::endl;
300   else
301     move_file(saving_filename, filename);
302
303   return ok;
304 }
305
306 void factor_vm::primitive_save_image() {
307   /* do a full GC to push everything into tenured space */
308   primitive_compact_gc();
309
310   data_root<byte_array> path2(ctx->pop(), this);
311   path2.untag_check(this);
312   data_root<byte_array> path1(ctx->pop(), this);
313   path1.untag_check(this);
314   save_image((vm_char*)(path1.untagged() + 1),
315              (vm_char*)(path2.untagged() + 1));
316 }
317
318 void factor_vm::primitive_save_image_and_exit() {
319   /* We unbox this before doing anything else. This is the only point
320      where we might throw an error, so we have to throw an error here since
321      later steps destroy the current image. */
322   data_root<byte_array> path2(ctx->pop(), this);
323   path2.untag_check(this);
324   data_root<byte_array> path1(ctx->pop(), this);
325   path1.untag_check(this);
326
327   /* strip out special_objects data which is set on startup anyway */
328   for (cell i = 0; i < special_object_count; i++)
329     if (!save_special_p(i))
330       special_objects[i] = false_object;
331
332   gc(collect_compact_op, 0, /* requested size */
333      false /* discard objects only reachable from stacks */);
334
335   /* Save the image */
336   if (save_image((vm_char*)(path1.untagged() + 1),
337                  (vm_char*)(path2.untagged() + 1)))
338     exit(0);
339   else
340     exit(1);
341 }
342
343 bool factor_vm::embedded_image_p() {
344   const vm_char* vm_path = vm_executable_path();
345   if (!vm_path)
346     return false;
347   FILE* file = OPEN_READ(vm_path);
348   if (!file)
349     return false;
350   embedded_image_footer footer;
351   bool embedded_p = read_embedded_image_footer(file, &footer);
352   fclose(file);
353   return embedded_p;
354 }
355
356 }