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