#include "master.hpp"
-namespace factor
-{
-
-/* Certain special objects in the image are known to the runtime */
-void factor_vm::init_objects(image_header *h)
-{
- memcpy(special_objects,h->special_objects,sizeof(special_objects));
-
- true_object = h->true_object;
- bignum_zero = h->bignum_zero;
- bignum_pos_one = h->bignum_pos_one;
- bignum_neg_one = h->bignum_neg_one;
+namespace factor {
+
+bool factor_arg(const vm_char* str, const vm_char* arg, cell* value) {
+ int val;
+ if (SSCANF(str, arg, &val) > 0) {
+ *value = val;
+ return true;
+ }
+ return false;
}
-void factor_vm::load_data_heap(FILE *file, image_header *h, vm_parameters *p)
-{
- p->tenured_size = std::max((h->data_size * 3) / 2,p->tenured_size);
+vm_parameters::vm_parameters() {
+ embedded_image = false;
+ image_path = NULL;
+ executable_path = NULL;
- init_data_heap(p->young_size,
- p->aging_size,
- p->tenured_size);
+ datastack_size = 32 * sizeof(cell);
+ retainstack_size = 32 * sizeof(cell);
- fixnum bytes_read = fread((void*)data->tenured->start,1,h->data_size,file);
+#if defined(FACTOR_PPC)
+ callstack_size = 256 * sizeof(cell);
+#else
+ callstack_size = 128 * sizeof(cell);
+#endif
- if((cell)bytes_read != h->data_size)
- {
- std::cout << "truncated image: " << bytes_read << " bytes read, ";
- std::cout << h->data_size << " bytes expected\n";
- fatal_error("load_data_heap failed",0);
- }
+ code_size = 96;
+ young_size = sizeof(cell) / 4;
+ aging_size = sizeof(cell) / 2;
+ tenured_size = 24 * sizeof(cell);
- data->tenured->initial_free_list(h->data_size);
-}
-
-void factor_vm::load_code_heap(FILE *file, image_header *h, vm_parameters *p)
-{
- if(h->code_size > p->code_size)
- fatal_error("Code heap too small to fit image",h->code_size);
-
- init_code_heap(p->code_size);
-
- if(h->code_size != 0)
- {
- size_t bytes_read = fread(code->allocator->first_block(),1,h->code_size,file);
- if(bytes_read != h->code_size)
- {
- std::cout << "truncated image: " << bytes_read << " bytes read, ";
- std::cout << h->code_size << " bytes expected\n";
- fatal_error("load_code_heap failed",0);
- }
- }
-
- code->allocator->initial_free_list(h->code_size);
-}
+ max_pic_size = 3;
-struct data_fixupper {
- cell offset;
-
- explicit data_fixupper(cell offset_) : offset(offset_) {}
-
- object *operator()(object *obj)
- {
- return (object *)((char *)obj + offset);
- }
-};
+ fep = false;
+ signals = true;
-struct code_fixupper {
- cell offset;
+#ifdef WINDOWS
+ console = GetConsoleWindow() != NULL;
+#else
+ console = true;
+#endif
- explicit code_fixupper(cell offset_) : offset(offset_) {}
-
- code_block *operator()(code_block *compiled)
- {
- return (code_block *)((char *)compiled + offset);
- }
-};
-
-static inline cell tuple_size_with_fixup(cell offset, object *obj)
-{
- tuple_layout *layout = (tuple_layout *)((char *)UNTAG(((tuple *)obj)->layout) + offset);
- return tuple_size(layout);
+ callback_size = 256;
}
-struct fixup_sizer {
- cell offset;
+vm_parameters::~vm_parameters() {
+ free((vm_char *)image_path);
+ free((vm_char *)executable_path);
+}
- explicit fixup_sizer(cell offset_) : offset(offset_) {}
+void vm_parameters::init_from_args(int argc, vm_char** argv) {
+ int i = 0;
+
+ for (i = 1; i < argc; i++) {
+ vm_char* arg = argv[i];
+ if (STRCMP(arg, STRING_LITERAL("--")) == 0)
+ break;
+ else if (factor_arg(arg, STRING_LITERAL("-datastack=%d"),
+ &datastack_size))
+ ;
+ else if (factor_arg(arg, STRING_LITERAL("-retainstack=%d"),
+ &retainstack_size))
+ ;
+ else if (factor_arg(arg, STRING_LITERAL("-callstack=%d"),
+ &callstack_size))
+ ;
+ else if (factor_arg(arg, STRING_LITERAL("-young=%d"),
+ &young_size))
+ ;
+ else if (factor_arg(arg, STRING_LITERAL("-aging=%d"),
+ &aging_size))
+ ;
+ else if (factor_arg(arg, STRING_LITERAL("-tenured=%d"),
+ &tenured_size))
+ ;
+ else if (factor_arg(arg, STRING_LITERAL("-codeheap=%d"),
+ &code_size))
+ ;
+ else if (factor_arg(arg, STRING_LITERAL("-pic=%d"),
+ &max_pic_size))
+ ;
+ else if (factor_arg(arg, STRING_LITERAL("-callbacks=%d"),
+ &callback_size))
+ ;
+ else if (STRNCMP(arg, STRING_LITERAL("-i="), 3) == 0) {
+ // In case you specify -i more than once.
+ if (image_path) {
+ free((vm_char *)image_path);
+ }
+ image_path = safe_strdup(arg + 3);
+ }
+ else if (STRCMP(arg, STRING_LITERAL("-fep")) == 0)
+ fep = true;
+ else if (STRCMP(arg, STRING_LITERAL("-no-signals")) == 0)
+ signals = false;
+ }
+}
- cell operator()(object *obj)
- {
- if(obj->type() == TUPLE_TYPE)
- return align(tuple_size_with_fixup(offset,obj),data_alignment);
- else
- return obj->size();
- }
-};
+void factor_vm::load_data_heap(FILE* file, image_header* h, vm_parameters* p) {
+ p->tenured_size = std::max((h->data_size * 3) / 2, p->tenured_size);
-struct object_fixupper {
- factor_vm *parent;
- cell data_offset;
- slot_visitor<data_fixupper> data_visitor;
- code_block_visitor<code_fixupper> code_visitor;
-
- object_fixupper(factor_vm *parent_, cell data_offset_, cell code_offset_) :
- parent(parent_),
- data_offset(data_offset_),
- data_visitor(slot_visitor<data_fixupper>(parent_,data_fixupper(data_offset_))),
- code_visitor(code_block_visitor<code_fixupper>(parent_,code_fixupper(code_offset_))) {}
-
- void operator()(object *obj, cell size)
- {
- parent->data->tenured->starts.record_object_start_offset(obj);
-
- switch(obj->type())
- {
- case ALIEN_TYPE:
- {
- cell payload_start = obj->binary_payload_start();
- data_visitor.visit_slots(obj,payload_start);
-
- alien *ptr = (alien *)obj;
-
- if(to_boolean(ptr->base))
- ptr->update_address();
- else
- ptr->expired = parent->true_object;
- break;
- }
- case DLL_TYPE:
- {
- cell payload_start = obj->binary_payload_start();
- data_visitor.visit_slots(obj,payload_start);
-
- parent->ffi_dlopen((dll *)obj);
- break;
- }
- case TUPLE_TYPE:
- {
- cell payload_start = tuple_size_with_fixup(data_offset,obj);
- data_visitor.visit_slots(obj,payload_start);
- break;
- }
- default:
- {
- cell payload_start = obj->binary_payload_start();
- data_visitor.visit_slots(obj,payload_start);
- code_visitor.visit_object_code_block(obj);
- break;
- }
- }
- }
-};
+ data_heap *d = new data_heap(&nursery,
+ p->young_size, p->aging_size, p->tenured_size);
+ set_data_heap(d);
+ fixnum bytes_read =
+ raw_fread((void*)data->tenured->start, 1, h->data_size, file);
-void factor_vm::fixup_data(cell data_offset, cell code_offset)
-{
- slot_visitor<data_fixupper> data_workhorse(this,data_fixupper(data_offset));
- data_workhorse.visit_roots();
+ if ((cell)bytes_read != h->data_size) {
+ std::cout << "truncated image: " << bytes_read << " bytes read, ";
+ std::cout << h->data_size << " bytes expected\n";
+ fatal_error("load_data_heap failed", 0);
+ }
- object_fixupper fixupper(this,data_offset,code_offset);
- fixup_sizer sizer(data_offset);
- data->tenured->iterate(fixupper,sizer);
+ data->tenured->initial_free_list(h->data_size);
}
-struct code_block_fixup_relocation_visitor {
- factor_vm *parent;
- cell code_offset;
- slot_visitor<data_fixupper> data_visitor;
- code_fixupper code_visitor;
-
- code_block_fixup_relocation_visitor(factor_vm *parent_, cell data_offset_, cell code_offset_) :
- parent(parent_),
- code_offset(code_offset_),
- data_visitor(slot_visitor<data_fixupper>(parent_,data_fixupper(data_offset_))),
- code_visitor(code_fixupper(code_offset_)) {}
-
- void operator()(instruction_operand op)
- {
- code_block *compiled = op.parent_code_block();
- cell old_offset = op.rel_offset() + (cell)compiled->entry_point() - code_offset;
-
- switch(op.rel_type())
- {
- case RT_LITERAL:
- op.store_value(data_visitor.visit_pointer(op.load_value(old_offset)));
- break;
- case RT_ENTRY_POINT:
- case RT_ENTRY_POINT_PIC:
- case RT_ENTRY_POINT_PIC_TAIL:
- op.store_code_block(code_visitor(op.load_code_block(old_offset)));
- break;
- case RT_HERE:
- op.store_value(op.load_value(old_offset) + code_offset);
- break;
- case RT_UNTAGGED:
- break;
- default:
- parent->store_external_address(op);
- break;
- }
- }
-};
+void factor_vm::load_code_heap(FILE* file, image_header* h, vm_parameters* p) {
+ if (h->code_size > p->code_size)
+ fatal_error("Code heap too small to fit image", h->code_size);
-struct code_block_fixupper {
- factor_vm *parent;
- cell data_offset;
- cell code_offset;
+ code = new code_heap(p->code_size);
- code_block_fixupper(factor_vm *parent_, cell data_offset_, cell code_offset_) :
- parent(parent_),
- data_offset(data_offset_),
- code_offset(code_offset_) {}
+ if (h->code_size != 0) {
+ size_t bytes_read =
+ raw_fread((void*)code->allocator->start, 1, h->code_size, file);
+ if (bytes_read != h->code_size) {
+ std::cout << "truncated image: " << bytes_read << " bytes read, ";
+ std::cout << h->code_size << " bytes expected\n";
+ fatal_error("load_code_heap failed", 0);
+ }
+ }
- void operator()(code_block *compiled, cell size)
- {
- slot_visitor<data_fixupper> data_visitor(parent,data_fixupper(data_offset));
- data_visitor.visit_code_block_objects(compiled);
+ code->allocator->initial_free_list(h->code_size);
+ code->initialize_all_blocks_set();
+}
- code_block_fixup_relocation_visitor code_visitor(parent,data_offset,code_offset);
- compiled->each_instruction_operand(code_visitor);
- }
-};
+struct startup_fixup {
+ static const bool translated_code_block_map = true;
-void factor_vm::fixup_code(cell data_offset, cell code_offset)
-{
- code_block_fixupper fixupper(this,data_offset,code_offset);
- code->allocator->iterate(fixupper);
-}
+ cell data_offset;
+ cell code_offset;
-/* Read an image file from disk, only done once during startup */
-/* This function also initializes the data and code heaps */
-void factor_vm::load_image(vm_parameters *p)
-{
- FILE *file = OPEN_READ(p->image_path);
- if(file == NULL)
- {
- std::cout << "Cannot open image file: " << p->image_path << std::endl;
- std::cout << strerror(errno) << std::endl;
- exit(1);
- }
+ startup_fixup(cell data_offset, cell code_offset)
+ : data_offset(data_offset), code_offset(code_offset) {}
- image_header h;
- if(fread(&h,sizeof(image_header),1,file) != 1)
- fatal_error("Cannot read image header",0);
+ object* fixup_data(object* obj) {
+ return (object*)((cell)obj + data_offset);
+ }
- if(h.magic != image_magic)
- fatal_error("Bad image: magic number check failed",h.magic);
+ code_block* fixup_code(code_block* obj) {
+ return (code_block*)((cell)obj + code_offset);
+ }
- if(h.version != image_version)
- fatal_error("Bad image: version number check failed",h.version);
-
- load_data_heap(file,&h,p);
- load_code_heap(file,&h,p);
+ object* translate_data(const object* obj) {
+ return fixup_data((object*)obj);
+ }
- fclose(file);
+ code_block* translate_code(const code_block* compiled) {
+ return fixup_code((code_block*)compiled);
+ }
- init_objects(&h);
+ cell size(const object* obj) {
+ return obj->size(*this);
+ }
- cell data_offset = data->tenured->start - h.data_relocation_base;
- cell code_offset = code->seg->start - h.code_relocation_base;
+ cell size(code_block* compiled) {
+ return compiled->size(*this);
+ }
+};
- fixup_data(data_offset,code_offset);
- fixup_code(data_offset,code_offset);
+void factor_vm::fixup_heaps(cell data_offset, cell code_offset) {
+ startup_fixup fixup(data_offset, code_offset);
+ slot_visitor<startup_fixup> visitor(this, fixup);
+ visitor.visit_all_roots();
+
+ auto start_object_updater = [&](object *obj, cell size) {
+ (void)size;
+ data->tenured->starts.record_object_start_offset(obj);
+ visitor.visit_slots(obj);
+ switch (obj->type()) {
+ case ALIEN_TYPE: {
+ alien* ptr = (alien*)obj;
+ if (to_boolean(ptr->base))
+ ptr->update_address();
+ else
+ ptr->expired = special_objects[OBJ_CANONICAL_TRUE];
+ break;
+ }
+ case DLL_TYPE: {
+ ffi_dlopen((dll*)obj);
+ break;
+ }
+ default: {
+ visitor.visit_object_code_block(obj);
+ break;
+ }
+ }
+ };
+ data->tenured->iterate(start_object_updater, fixup);
+
+ auto updater = [&](code_block* compiled, cell size) {
+ (void)size;
+ visitor.visit_code_block_objects(compiled);
+ cell rel_base = compiled->entry_point() - fixup.code_offset;
+ visitor.visit_instruction_operands(compiled, rel_base);
+ };
+ code->allocator->iterate(updater, fixup);
+}
- /* Store image path name */
- special_objects[OBJ_IMAGE] = allot_alien(false_object,(cell)p->image_path);
+bool factor_vm::read_embedded_image_footer(FILE* file,
+ embedded_image_footer* footer) {
+ safe_fseek(file, -(off_t)sizeof(embedded_image_footer), SEEK_END);
+ safe_fread(footer, (off_t)sizeof(embedded_image_footer), 1, file);
+ return footer->magic == image_magic;
}
-/* Save the current image to disk */
-bool factor_vm::save_image(const vm_char *filename)
-{
- FILE* file;
- image_header h;
-
- file = OPEN_WRITE(filename);
- if(file == NULL)
- {
- std::cout << "Cannot open image file: " << filename << std::endl;
- std::cout << strerror(errno) << std::endl;
- return false;
- }
-
- h.magic = image_magic;
- h.version = image_version;
- h.data_relocation_base = data->tenured->start;
- h.data_size = data->tenured->occupied_space();
- h.code_relocation_base = code->seg->start;
- h.code_size = code->allocator->occupied_space();
-
- h.true_object = true_object;
- h.bignum_zero = bignum_zero;
- h.bignum_pos_one = bignum_pos_one;
- h.bignum_neg_one = bignum_neg_one;
-
- for(cell i = 0; i < special_object_count; i++)
- h.special_objects[i] = (save_special_p(i) ? special_objects[i] : false_object);
-
- bool ok = true;
-
- if(fwrite(&h,sizeof(image_header),1,file) != 1) ok = false;
- if(fwrite((void*)data->tenured->start,h.data_size,1,file) != 1) ok = false;
- if(fwrite(code->allocator->first_block(),h.code_size,1,file) != 1) ok = false;
- if(fclose(file)) ok = false;
-
- if(!ok)
- std::cout << "save-image failed: " << strerror(errno) << std::endl;
-
- return ok;
+char *threadsafe_strerror(int errnum) {
+ char *buf = (char *) malloc(STRERROR_BUFFER_SIZE);
+ if (!buf) {
+ fatal_error("Out of memory in threadsafe_strerror, errno", errnum);
+ }
+ THREADSAFE_STRERROR(errnum, buf, STRERROR_BUFFER_SIZE);
+ return buf;
}
-void factor_vm::primitive_save_image()
-{
- /* do a full GC to push everything into tenured space */
- primitive_compact_gc();
+// Read an image file from disk, only done once during startup
+// This function also initializes the data and code heaps
+void factor_vm::load_image(vm_parameters* p) {
+
+ FILE* file = OPEN_READ(p->image_path);
+ if (file == NULL) {
+ std::cout << "Cannot open image file: " << AS_UTF8(p->image_path) << std::endl;
+ char *msg = threadsafe_strerror(errno);
+ std::cout << "strerror: " << msg << std::endl;
+ free(msg);
+ exit(1);
+ }
+ if (p->embedded_image) {
+ embedded_image_footer footer;
+ if (!read_embedded_image_footer(file, &footer)) {
+ std::cout << "No embedded image" << std::endl;
+ exit(1);
+ }
+ safe_fseek(file, (off_t)footer.image_offset, SEEK_SET);
+ }
+
+ image_header h;
+ if (raw_fread(&h, sizeof(image_header), 1, file) != 1)
+ fatal_error("Cannot read image header", 0);
+
+ if (h.magic != image_magic)
+ fatal_error("Bad image: magic number check failed", h.magic);
+
+ if (h.version != image_version)
+ fatal_error("Bad image: version number check failed", h.version);
+
+ load_data_heap(file, &h, p);
+ load_code_heap(file, &h, p);
+
+ raw_fclose(file);
+
+ // Certain special objects in the image are known to the runtime
+ memcpy(special_objects, h.special_objects, sizeof(special_objects));
+
+ cell data_offset = data->tenured->start - h.data_relocation_base;
+ cell code_offset = code->allocator->start - h.code_relocation_base;
+ fixup_heaps(data_offset, code_offset);
+}
+
+// Save the current image to disk. We don't throw any exceptions here
+// because if the 'then-die' argument is t it is not safe to do
+// so. Instead we signal failure by returning false.
+bool factor_vm::save_image(const vm_char* saving_filename,
+ const vm_char* filename) {
+ image_header h = {};
+
+ h.magic = image_magic;
+ h.version = image_version;
+ h.data_relocation_base = data->tenured->start;
+ h.data_size = data->tenured->occupied_space();
+ h.code_relocation_base = code->allocator->start;
+ h.code_size = code->allocator->occupied_space();
+
+ for (cell i = 0; i < special_object_count; i++)
+ h.special_objects[i] =
+ (save_special_p(i) ? special_objects[i] : false_object);
+
+ FILE* file = OPEN_WRITE(saving_filename);
+ if (file == NULL)
+ return false;
+ if (safe_fwrite(&h, sizeof(image_header), 1, file) != 1)
+ return false;
+ if (h.data_size > 0 &&
+ safe_fwrite((void*)data->tenured->start, h.data_size, 1, file) != 1)
+ return false;
+ if (h.code_size > 0 &&
+ safe_fwrite((void*)code->allocator->start, h.code_size, 1, file) != 1)
+ return false;
+ if (raw_fclose(file) == -1)
+ return false;
+ if (!move_file(saving_filename, filename))
+ return false;
+ return true;
+}
- data_root<byte_array> path(ctx->pop(),this);
- path.untag_check(this);
- save_image((vm_char *)(path.untagged() + 1));
+// Allocates memory
+void factor_vm::primitive_save_image() {
+ // We unbox this before doing anything else. This is the only point
+ // where we might throw an error, so we have to throw an error here since
+ // later steps destroy the current image.
+ bool then_die = to_boolean(ctx->pop());
+ byte_array* path2 = untag_check<byte_array>(ctx->pop());
+ byte_array* path1 = untag_check<byte_array>(ctx->pop());
+
+ // Copy the paths to non-gc memory to avoid them hanging around in
+ // the saved image.
+ vm_char* path1_saved = safe_strdup(path1->data<vm_char>());
+ vm_char* path2_saved = safe_strdup(path2->data<vm_char>());
+
+ if (then_die) {
+ // strip out special_objects data which is set on startup anyway
+ for (cell i = 0; i < special_object_count; i++)
+ if (!save_special_p(i))
+ special_objects[i] = false_object;
+
+ // dont trace objects only reachable from context stacks so we don't
+ // get volatile data saved in the image.
+ active_contexts.clear();
+ code->uninitialized_blocks.clear();
+
+ // I think clearing the callback heap should be fine too.
+ callbacks->allocator->initial_free_list(0);
+ }
+
+ // do a full GC to push everything remaining into tenured space
+ primitive_compact_gc();
+
+ // Save the image
+ bool ret = save_image(path1_saved, path2_saved);
+ if (then_die) {
+ exit(ret ? 0 : 1);
+ }
+ free(path1_saved);
+ free(path2_saved);
+
+ if (!ret) {
+ general_error(ERROR_IO, tag_fixnum(errno), false_object);
+ }
}
-void factor_vm::primitive_save_image_and_exit()
-{
- /* We unbox this before doing anything else. This is the only point
- where we might throw an error, so we have to throw an error here since
- later steps destroy the current image. */
- data_root<byte_array> path(ctx->pop(),this);
- path.untag_check(this);
-
- /* strip out special_objects data which is set on startup anyway */
- for(cell i = 0; i < special_object_count; i++)
- if(!save_special_p(i)) special_objects[i] = false_object;
-
- gc(collect_compact_op,
- 0, /* requested size */
- false /* discard objects only reachable from stacks */);
-
- /* Save the image */
- if(save_image((vm_char *)(path.untagged() + 1)))
- exit(0);
- else
- exit(1);
+bool factor_vm::embedded_image_p() {
+ const vm_char* vm_path = vm_executable_path();
+ FILE* file = OPEN_READ(vm_path);
+ if (!file) {
+ free((vm_char *)vm_path);
+ return false;
+ }
+ embedded_image_footer footer;
+ bool embedded_p = read_embedded_image_footer(file, &footer);
+ fclose(file);
+ free((vm_char *)vm_path);
+ return embedded_p;
}
}