History

Initial public commit for Dis VM

arr committed 10 years ago

Commit f95902eNo parent (first commit)40 files · +9358 −0Browse files

Showing the changes to src/vm/module_reader.cpp (1 of 40 files). Show all

Unified · Split

src/vm/module_reader.cpp+800−0
@@ -0,0 +1,800 @@
1+//
2+// Dis VM
3+// File: loader.cpp
4+// Author: arr
5+//
6+
7+#include <cassert>
8+#include <limits>
9+#include <memory>
10+#include <iostream>
11+#include <fstream>
12+#include <array>
13+#include <tuple>
14+#include <disvm.h>
15+#include <exceptions.h>
16+#include <vm_memory.h>
17+#include <utils.h>
18+#include <module_reader.h>
19+#include "buffered_reader.h"
20+
21+using namespace disvm;
22+using namespace disvm::runtime;
23+using namespace disvm::format;
24+
25+namespace
26+{
27+ // constants used in module processing
28+ struct module_constants
29+ {
30+ static const auto max_type_pointer_count = operand_t{ 128 * 1024 };
31+ static const auto no_error_handle_type_desc = operand_t{ -1 };
32+ static const auto no_entry_pc = vm_pc_t{ -1 };
33+ static const auto vm_module_type_desc_number = std::size_t{ 0 };
34+ static const auto max_module_name_bytes = std::size_t{ 128 }; // Including null
35+ static const auto array_address_stack_size = std::size_t{ 4 };
36+ };
37+
38+ // enumeration for types in the data section
39+ enum class datum_type_t
40+ {
41+ value_bit8 = 1,
42+ value_bit32 = 2,
43+ utf_string = 3,
44+ value_real64 = 4,
45+ array = 5,
46+ set_array = 6,
47+ restore_load_address = 7,
48+ value_bit64 = 8
49+ };
50+
51+ // Read the next operand as defined in the Dis VM specification.
52+ std::tuple<bool, operand_t> read_next_operand(util::buffered_reader_t &reader)
53+ {
54+ auto result = operand_t{ 0 };
55+
56+ // Converting stack allocation to buffer since max size of operand is a machine word
57+ byte_t buffer[sizeof(result)];
58+
59+ if (!reader.get_next_bytes(1, buffer))
60+ return std::make_tuple(false, 0);
61+
62+ switch (buffer[0] & 0xc0)
63+ {
64+ case 0x00:
65+ // 1 byte operand
66+ result = buffer[0];
67+ break;
68+
69+ case 0x40:
70+ // 1 byte operand - Preserve sign
71+ result = buffer[0] | ~0x7f;
72+ break;
73+
74+ case 0x80:
75+ // 2 byte operand
76+ if (!reader.get_next_bytes(1, buffer + 1))
77+ return std::make_tuple(false, 0);
78+
79+ // Preserve sign
80+ if ((buffer[0] & 0x20) != 0)
81+ buffer[0] |= ~0x3f;
82+ else
83+ buffer[0] &= 0x3f;
84+
85+ result = (buffer[0] << 8);
86+ result |= buffer[1];
87+ break;
88+
89+ case 0xc0:
90+ // 4 byte operand
91+ if (!reader.get_next_bytes(3, buffer + 1))
92+ return std::make_tuple(false, 0);
93+
94+ // Preserve sign
95+ if ((buffer[0] & 0x20) != 0)
96+ buffer[0] |= ~0x3f;
97+ else
98+ buffer[0] &= 0x3f;
99+
100+ result = (buffer[0] << 24);
101+ result |= (buffer[1] << 16);
102+ result |= (buffer[2] << 8);
103+ result |= buffer[3];
104+ break;
105+
106+ default:
107+ assert(false && "Should not be possible");
108+ }
109+
110+ return std::make_tuple(true, result);
111+ }
112+
113+ // Read the next word as defined in the Dis VM specification.
114+ std::tuple<bool, word_t> read_next_word(util::buffered_reader_t &reader)
115+ {
116+ auto result = word_t{};
117+
118+ // Converting stack allocation to buffer since max size of operand is a machine word
119+ byte_t buffer[sizeof(result)];
120+
121+ if (!reader.get_next_bytes(sizeof(result), buffer))
122+ return std::make_tuple(false, 0);
123+
124+ result = (buffer[0] << 24);
125+ result |= (buffer[1] << 16);
126+ result |= (buffer[2] << 8);
127+ result |= buffer[3];
128+
129+ return std::make_tuple(true, result);
130+ }
131+
132+ real_t convert_to_real(const operand_t f, const operand_t s)
133+ {
134+ auto u64 = static_cast<uint64_t>(f);
135+ u64 = (u64 << 32) | (static_cast<uint64_t>(s) & 0xffffffff);
136+
137+ auto r = real_t{};
138+ std::memcpy(&r, &u64, sizeof(u64));
139+
140+ static_assert(sizeof(u64) == sizeof(r), "Real should be 64-bits");
141+ return r;
142+ }
143+
144+ bool read_next_string(util::buffered_reader_t &reader, std::vector<uint8_t> &char_buffer)
145+ {
146+ assert(char_buffer.size() == 0);
147+ char_buffer.reserve(1024);
148+
149+ auto success = bool{};
150+ auto value = uint8_t{};
151+ std::tie(success, value) = reader.get_next_byte();
152+ while (success && value != 0)
153+ {
154+ char_buffer.push_back(value);
155+ std::tie(success, value) = reader.get_next_byte();
156+ }
157+
158+ return success;
159+ }
160+
161+ byte_t *read_8byte_segments(util::buffered_reader_t &reader, datum_type_t type, uint32_t segment_count, byte_t *dest)
162+ {
163+ assert(type == datum_type_t::value_real64 || type == datum_type_t::value_bit64);
164+
165+ auto success = bool{};
166+ auto first = word_t{};
167+ auto second = word_t{};
168+ for (auto idx = uint32_t{ 0 }; idx < segment_count; ++idx)
169+ {
170+ std::tie(success, first) = read_next_word(reader);
171+ if (!success) throw module_reader_exception{ "Failed to read first word" };
172+
173+ std::tie(success, second) = read_next_word(reader);
174+ if (!success) throw module_reader_exception{ "Failed to read second word" };
175+
176+ if (type == datum_type_t::value_real64)
177+ {
178+ auto r = convert_to_real(first, second);
179+ *reinterpret_cast<real_t *>(dest) = r;
180+ dest += sizeof(r);
181+ }
182+ else
183+ {
184+ auto b = static_cast<big_t>(first);
185+ b = (b << 32) | (static_cast<big_t>(second) & 0xffffffff);
186+ *reinterpret_cast<big_t *>(dest) = b;
187+ dest += sizeof(b);
188+ }
189+ }
190+
191+ return dest;
192+ }
193+
194+ std::tuple<address_mode_middle_t, address_mode_t, address_mode_t> convert_to_address_mode(uint8_t instr_addr_mode)
195+ {
196+ // Taken from Dis VM specification
197+ // bit 7 6 5 4 3 2 1 0
198+ // m1 m0 s2 s1 s0 d2 d1 d0
199+
200+ static const auto mid_mask = uint8_t{ 0xc0 };
201+ static const auto src_mask = uint8_t{ 0x38 };
202+ static const auto dest_mask = uint8_t{ 0x07 };
203+ assert((mid_mask | src_mask | dest_mask) == std::numeric_limits<uint8_t>::max());
204+
205+ const auto mid = static_cast<address_mode_middle_t>((instr_addr_mode & mid_mask) >> 6);
206+ const auto src = static_cast<address_mode_t>((instr_addr_mode & src_mask) >> 3);
207+ const auto dest = static_cast<address_mode_t>(instr_addr_mode & dest_mask);
208+
209+ return std::make_tuple(mid, src, dest);
210+ }
211+
212+ // See header format definition in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html)
213+ void read_header(util::buffered_reader_t &reader, vm_module_t &modobj)
214+ {
215+ auto success = bool{};
216+ auto &header = modobj.header;
217+
218+ std::tie(success, header.magic_number) = read_next_operand(reader);
219+ if (!success)
220+ throw module_reader_exception{ "Failed to read magic number" };
221+
222+ assert(header.magic_number == magic_number_constants::xmagic || header.magic_number == magic_number_constants::smagic);
223+
224+ // If the object file is signed, read in the signature.
225+ if (header.magic_number == magic_number_constants::smagic)
226+ {
227+ std::tie(success, header.Signature.length) = read_next_operand(reader);
228+ if (!success) throw module_reader_exception{ "Failed to read signature length" };
229+
230+ header.Signature.signature.reset(new byte_t[header.Signature.length]);
231+ const auto bytesRead = reader.get_next_bytes(header.Signature.length, header.Signature.signature.get());
232+ if (bytesRead != header.Signature.length) throw module_reader_exception{ "Failed to read full signature" };
233+ }
234+ else
235+ {
236+ header.Signature.length = word_t{ 0 };
237+ }
238+
239+ auto runtime_flags = operand_t{};
240+ std::tie(success, runtime_flags) = read_next_operand(reader);
241+ if (!success) throw module_reader_exception{ "Failed to read runtime flag" };
242+ header.runtime_flag = static_cast<runtime_flags_t>(runtime_flags);
243+
244+ std::tie(success, header.stack_extent) = read_next_operand(reader);
245+ if (!success) throw module_reader_exception{ "Failed to read stack extent" };
246+
247+ std::tie(success, header.code_size) = read_next_operand(reader);
248+ if (!success) throw module_reader_exception{ "Failed to read code size" };
249+
250+ std::tie(success, header.data_size) = read_next_operand(reader);
251+ if (!success) throw module_reader_exception{ "Failed to read data size" };
252+
253+ std::tie(success, header.type_size) = read_next_operand(reader);
254+ if (!success) throw module_reader_exception{ "Failed to read type size" };
255+
256+ std::tie(success, header.export_size) = read_next_operand(reader);
257+ if (!success) throw module_reader_exception{ "Failed to read export size" };
258+
259+ // [SPEC] If a module does not have an entry program counter it is set to '-1'.
260+ std::tie(success, header.entry_pc) = read_next_operand(reader);
261+ if (!success) throw module_reader_exception{ "Failed to entry pc" };
262+
263+ // [SPEC] If a module does not have an entry program counter the entry type index is set to '-1'.
264+ std::tie(success, header.entry_type) = read_next_operand(reader);
265+ if (!success) throw module_reader_exception{ "Failed to read entry type" };
266+ }
267+
268+ // See code section layout in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html)
269+ void read_code_section(util::buffered_reader_t &reader, vm_module_t &modobj)
270+ {
271+ auto success = bool{};
272+ const auto instruction_count = modobj.header.code_size;
273+
274+ // Allocate memory for the instructions
275+ modobj.code_section.resize(instruction_count);
276+
277+ for (auto c = word_t{ 0 }; c < instruction_count; ++c)
278+ {
279+ // Read in a single instruction
280+ byte_t op_and_addrmode[2] = {};
281+ const auto bytesRead = reader.get_next_bytes(sizeof(op_and_addrmode), op_and_addrmode);
282+ if (bytesRead != sizeof(op_and_addrmode)) throw module_reader_exception{ "Failed to read op code and address mode" };
283+
284+ auto vm_instr = vm_instruction_t{};
285+ auto &instr = vm_instr.op;
286+
287+ instr.opcode = static_cast<opcode_t>(op_and_addrmode[0]);
288+ assert(opcode_t::first_opcode <= instr.opcode && instr.opcode <= opcode_t::last_opcode);
289+
290+ std::tie(instr.middle.mode, instr.source.mode, instr.destination.mode) = convert_to_address_mode(op_and_addrmode[1]);
291+
292+ if (instr.middle.mode != address_mode_middle_t::none)
293+ {
294+ std::tie(success, instr.middle.register1) = read_next_operand(reader);
295+ if (!success) throw module_reader_exception{ "Failed to read middle register" };
296+ }
297+
298+ if (instr.source.mode != address_mode_t::none)
299+ {
300+ std::tie(success, instr.source.register1) = read_next_operand(reader);
301+ if (!success) throw module_reader_exception{ "Failed to read source register 1" };
302+
303+ if (instr.source.mode == address_mode_t::offset_double_indirect_fp ||
304+ instr.source.mode == address_mode_t::offset_double_indirect_mp)
305+ {
306+ std::tie(success, instr.source.register2) = read_next_operand(reader);
307+ if (!success) throw module_reader_exception{ "Failed to read source register 2" };
308+
309+ // Double indirect offsets need to be < 2^16-1.
310+ assert(instr.source.register1 <= std::numeric_limits<uint16_t>::max());
311+ assert(instr.source.register2 <= std::numeric_limits<uint16_t>::max());
312+ }
313+ }
314+
315+ if (instr.destination.mode != address_mode_t::none)
316+ {
317+ std::tie(success, instr.destination.register1) = read_next_operand(reader);
318+ if (!success) throw module_reader_exception{ "Failed to read destination register 1" };
319+
320+ if (instr.destination.mode == address_mode_t::offset_double_indirect_fp ||
321+ instr.destination.mode == address_mode_t::offset_double_indirect_mp)
322+ {
323+ std::tie(success, instr.destination.register2) = read_next_operand(reader);
324+ if (!success) throw module_reader_exception{ "Failed to destination register 2" };
325+
326+ // Double indirect offsets need to be < 2^16-1.
327+ assert(instr.destination.register1 <= std::numeric_limits<uint16_t>::max());
328+ assert(instr.destination.register2 <= std::numeric_limits<uint16_t>::max());
329+ }
330+ }
331+
332+ modobj.code_section[c] = std::move(vm_instr);
333+ }
334+
335+ if (modobj.header.entry_pc != module_constants::no_entry_pc && static_cast<std::size_t>(modobj.header.entry_pc) >= modobj.code_section.size())
336+ throw module_reader_exception{ "Invalid initial program counter value for code section" };
337+ }
338+
339+ // See type section layout in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html)
340+ void read_type_section(util::buffered_reader_t &reader, vm_module_t &modobj)
341+ {
342+ auto success = bool{};
343+ const auto type_count = modobj.header.type_size;
344+
345+ // Allocate memory for the types
346+ modobj.type_section.resize(type_count);
347+
348+ for (auto c = word_t{ 0 }; c < type_count; ++c)
349+ {
350+ // Read in single type
351+ operand_t desc_number;
352+ std::tie(success, desc_number) = read_next_operand(reader);
353+ if (!success) throw module_reader_exception{ "Failed to read type descriptor number" };
354+
355+ operand_t size;
356+ std::tie(success, size) = read_next_operand(reader);
357+ if (!success) throw module_reader_exception{ "Failed to read type size" };
358+
359+ operand_t map_in_bytes;
360+ std::tie(success, map_in_bytes) = read_next_operand(reader);
361+ if (!success) throw module_reader_exception{ "Failed to read type pointer count" };
362+ if (map_in_bytes > module_constants::max_type_pointer_count) throw module_reader_exception{ "Invalid Limbo type" };
363+
364+ auto pointer_map = std::vector<uint8_t>(map_in_bytes);
365+ if (map_in_bytes != 0)
366+ {
367+ const auto bytesRead = reader.get_next_bytes(map_in_bytes, pointer_map.data());
368+ if (bytesRead != map_in_bytes) throw module_reader_exception{ "Failed to read type pointer map" };
369+ }
370+
371+ modobj.type_section[desc_number] = std::move(type_descriptor_t::create(size, pointer_map));
372+ }
373+ }
374+
375+ // See data section layout in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html)
376+ void read_data_section(util::buffered_reader_t &reader, vm_module_t &modobj)
377+ {
378+ if (modobj.header.data_size != 0)
379+ {
380+ auto vm_module_type = modobj.type_section[module_constants::vm_module_type_desc_number];
381+ if (vm_module_type->size_in_bytes != modobj.header.data_size) throw module_reader_exception{ "Invalid type desc for MP" };
382+
383+ // The Dis VM spec does not guarantee the MP segment (i.e. data segment) of the loaded
384+ // module will be initialized to zero, but it is the C/C++ approach so defer to that.
385+ modobj.original_mp.reset(vm_alloc_t::allocate(vm_module_type, vm_alloc_t::zero_memory));
386+ }
387+
388+ auto array_stack = std::array<byte_t *, module_constants::array_address_stack_size>{};
389+ auto array_stack_pointer = std::size_t{ 0 };
390+ auto success = bool{};
391+ auto code = byte_t{};
392+ auto base = reinterpret_cast<byte_t *>(modobj.original_mp->allocation());
393+ for (;;)
394+ {
395+ auto bytesRead = reader.get_next_bytes(sizeof(code), &code);
396+ if (bytesRead != sizeof(code)) throw module_reader_exception{ "Failed to read data item code" };
397+ if (code == 0)
398+ break;
399+
400+ const auto type = static_cast<datum_type_t>((code & 0xf0) >> 4);
401+ auto count = static_cast<uint32_t>(code & 0x0f);
402+ if (count == 0)
403+ {
404+ std::tie(success, count) = read_next_operand(reader);
405+ if (!success) throw module_reader_exception{ "Failed to read long item datum count" };
406+ }
407+
408+ auto byte_offset = operand_t{};
409+ std::tie(success, byte_offset) = read_next_operand(reader);
410+ if (!success) throw module_reader_exception{ "Failed to read offset" };
411+
412+ // Compute the next data destination.
413+ auto data_dest = base + byte_offset;
414+
415+ switch (type)
416+ {
417+ case datum_type_t::value_bit8:
418+ {
419+ auto byte = uint8_t{};
420+ for (auto idx = uint32_t{ 0 }; idx < count; ++idx)
421+ {
422+ std::tie(success, byte) = reader.get_next_byte();
423+ if (!success) throw module_reader_exception{ "Failed to read 1 byte" };
424+
425+ *reinterpret_cast<byte_t *>(data_dest) = byte;
426+ data_dest += sizeof(byte);
427+ }
428+ break;
429+ }
430+ case datum_type_t::value_bit32:
431+ {
432+ auto word = word_t{};
433+ for (auto idx = uint32_t{ 0 }; idx < count; ++idx)
434+ {
435+ word = word_t{ 0 };
436+ std::tie(success, word) = read_next_word(reader);
437+ if (!success) throw module_reader_exception{ "Failed to read word" };
438+
439+ *reinterpret_cast<word_t *>(data_dest) = word;
440+ data_dest += sizeof(word);
441+ }
442+ break;
443+ }
444+ case datum_type_t::utf_string:
445+ {
446+ auto str_buffer = std::vector<uint8_t>(count);
447+ auto bytesRead = reader.get_next_bytes(count, str_buffer.data());
448+ if (bytesRead != count)
449+ throw module_reader_exception{ "Failed to read string data" };
450+
451+ auto new_string = new vm_string_t(str_buffer.size(), str_buffer.data());
452+ *reinterpret_cast<pointer_t *>(data_dest) = new_string->allocation();
453+ break;
454+ }
455+ case datum_type_t::value_real64:
456+ data_dest = read_8byte_segments(reader, datum_type_t::value_real64, count, data_dest);
457+ break;
458+ case datum_type_t::array:
459+ {
460+ auto element_type = word_t{};
461+ std::tie(success, element_type) = read_next_word(reader);
462+ if (!success) throw module_reader_exception{ "Failed to read word" };
463+
464+ if (element_type < 0 || modobj.header.type_size < element_type)
465+ if (!success) throw module_reader_exception{ "Invalid array element type" };
466+
467+ auto element_count = word_t{};
468+ std::tie(success, element_count) = read_next_word(reader);
469+ if (!success) throw module_reader_exception{ "Failed to read word" };
470+
471+ auto new_array = new vm_array_t(modobj.type_section[element_type], element_count);
472+ *reinterpret_cast<pointer_t *>(data_dest) = new_array->allocation();
473+ break;
474+ }
475+ case datum_type_t::set_array:
476+ {
477+ if (data_dest == nullptr) throw module_reader_exception{ "Invalid data type processing" };
478+ auto arr_maybe = vm_alloc_t::from_allocation(*reinterpret_cast<pointer_t *>(data_dest));
479+ assert(arr_maybe != nullptr);
480+
481+ if (arr_maybe->alloc_type != vm_array_t::type_desc())
482+ throw module_reader_exception{ "Data index not an array type" };
483+
484+ auto arr = dynamic_cast<vm_array_t *>(arr_maybe);
485+
486+ auto array_index = word_t{};
487+ std::tie(success, array_index) = read_next_word(reader);
488+ if (!success) throw module_reader_exception{ "Failed to read word" };
489+
490+ if (array_index < 0 || arr->get_length() <= array_index || array_stack_pointer >= module_constants::array_address_stack_size)
491+ throw module_reader_exception{ "Invalid array stack pointer" };
492+
493+ array_stack[array_stack_pointer++] = base;
494+ base = reinterpret_cast<byte_t *>(arr->at(0)) + (array_index * arr->alloc_type->size_in_bytes);
495+
496+ break;
497+ }
498+ case datum_type_t::restore_load_address:
499+ if (array_stack_pointer == 0) throw module_reader_exception{ "Invalid array stack pointer" };
500+
501+ base = array_stack[--array_stack_pointer];
502+ assert(base != nullptr);
503+
504+#ifndef NDEBUG
505+ array_stack[array_stack_pointer] = nullptr;
506+#endif
507+
508+ break;
509+ case datum_type_t::value_bit64:
510+ data_dest = read_8byte_segments(reader, datum_type_t::value_bit64, count, data_dest);
511+ break;
512+ default:
513+ throw module_reader_exception{ "Unknown item data type" };
514+ }
515+ }
516+ }
517+
518+ // See module name in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html)
519+ void read_module_name(util::buffered_reader_t &reader, vm_module_t &modobj)
520+ {
521+ auto string_buffer = std::vector<uint8_t>{};
522+ if (!read_next_string(reader, string_buffer))
523+ throw module_reader_exception{ "Failed to read module name" };
524+
525+ modobj.module_name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data());
526+ assert(modobj.module_name->get_length() <= module_constants::max_module_name_bytes);
527+ }
528+
529+ // See link section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html)
530+ void read_link_section(util::buffered_reader_t &reader, vm_module_t &modobj)
531+ {
532+ auto success = bool{};
533+ const auto export_count = modobj.header.export_size;
534+
535+ // Allocate memory for the export entries
536+ modobj.export_section.reserve(export_count);
537+
538+ for (auto c = word_t{ 0 }; c < export_count; ++c)
539+ {
540+ // Read in single export item
541+ auto item = export_function_t{};
542+
543+ std::tie(success, item.pc) = read_next_operand(reader);
544+ if (!success) throw module_reader_exception{ "Failed to read linkage pc" };
545+
546+ std::tie(success, item.frame_type) = read_next_operand(reader);
547+ if (!success) throw module_reader_exception{ "Failed to read linkage frame type index" };
548+
549+ std::tie(success, item.sig) = read_next_word(reader);
550+ if (!success) throw module_reader_exception{ "Failed to read linkage signature" };
551+
552+ auto string_buffer = std::vector<uint8_t>{};
553+ if (!read_next_string(reader, string_buffer))
554+ throw module_reader_exception{ "Failed to read linkage name" };
555+
556+ item.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data());
557+
558+ const auto sig = item.sig;
559+ modobj.export_section.emplace(sig, std::move(item));
560+ }
561+ }
562+
563+ // See import section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html)
564+ // [SPEC] The specification does not seem to match what is observed in an actual module.
565+ // The function is modeled after the disassembler (dis.b and libinterp/load.c) instead.
566+ void read_import_section(util::buffered_reader_t &reader, vm_module_t &modobj)
567+ {
568+ auto success = bool{};
569+ auto module_import_count = operand_t{};
570+ std::tie(success, module_import_count) = read_next_operand(reader);
571+ if (!success) throw module_reader_exception{ "Failed to read module import count" };
572+
573+ // Allocate memory for module imports
574+ modobj.import_section.reserve(module_import_count);
575+
576+ for (auto c = operand_t{ 0 }; c < module_import_count; ++c)
577+ {
578+ // Read in single module import
579+ import_vm_module_t module{};
580+
581+ auto func_import_count = operand_t{};
582+ std::tie(success, func_import_count) = read_next_operand(reader);
583+ if (!success) throw module_reader_exception{ "Failed to read function import count" };
584+
585+ // Allocate memory for function imports
586+ module.functions.reserve(func_import_count);
587+
588+ for (auto k = operand_t{ 0 }; k < func_import_count; ++k)
589+ {
590+ // Read in single function import
591+ import_function_t func{};
592+
593+ std::tie(success, func.sig) = read_next_word(reader);
594+ if (!success) throw module_reader_exception{ "Failed to read import function signature" };
595+
596+ auto string_buffer = std::vector<uint8_t>{};
597+ if (!read_next_string(reader, string_buffer))
598+ throw module_reader_exception{ "Failed to read import function name" };
599+
600+ func.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data());
601+
602+ module.functions.push_back(std::move(func));
603+ }
604+
605+ modobj.import_section.push_back(std::move(module));
606+ }
607+
608+ byte_t value;
609+ std::tie(success, value) = reader.get_next_byte();
610+ if (!success || value != 0)
611+ throw module_reader_exception{ "Failed to read final byte from import section" };
612+ }
613+
614+ // See handler section in Dis VM specification (http://www.vitanuova.com/inferno/man/6/dis.html)
615+ void read_handler_section(util::buffered_reader_t &reader, vm_module_t &modobj)
616+ {
617+ auto success = bool{};
618+ auto handler_count = operand_t{};
619+ std::tie(success, handler_count) = read_next_operand(reader);
620+
621+ // Allocate memory for handlers
622+ modobj.handler_section.reserve(handler_count);
623+
624+ for (auto c = operand_t{ 0 }; c < handler_count; ++c)
625+ {
626+ // Read in single handler
627+ handler_t handler{};
628+
629+ std::tie(success, handler.exception_offset) = read_next_operand(reader);
630+ if (!success) throw module_reader_exception{ "Failed to read exception offset" };
631+
632+ std::tie(success, handler.begin_pc) = read_next_operand(reader);
633+ if (!success) throw module_reader_exception{ "Failed to read handler begin program counter" };
634+
635+ std::tie(success, handler.end_pc) = read_next_operand(reader);
636+ if (!success) throw module_reader_exception{ "Failed to read handler end program counter" };
637+
638+ auto type_desc_number = operand_t{};
639+ std::tie(success, type_desc_number) = read_next_operand(reader);
640+ if (!success) throw module_reader_exception{ "Failed to read handler type description ID" };
641+
642+ if (type_desc_number != module_constants::no_error_handle_type_desc)
643+ {
644+ assert(0 <= type_desc_number && type_desc_number < static_cast<operand_t>(modobj.type_section.size()));
645+ handler.type_desc = modobj.type_section[type_desc_number];
646+ assert(handler.type_desc != nullptr);
647+ }
648+
649+ auto handler_cases = operand_t{};
650+ std::tie(success, handler_cases) = read_next_operand(reader);
651+
652+ // [SPEC] The operand read above contains two pieces of data.
653+ // The higher 16-bits represent the number of exception cases that catch exception types.
654+ // The lower 16-bits represent the total number of handled cases.
655+ // In the limbo language strings and exceptions can be caught in an exception handler.
656+ // Based on Inferno implementation libinterp/load.c.
657+ handler.exception_type_count = word_t{ handler_cases >> 16 };
658+ const auto total_count = (handler_cases & 0xffff);
659+
660+ // Allocate memory for exceptions plus the wild card.
661+ handler.exception_table.reserve(total_count + 1);
662+
663+ for (auto k = operand_t{ 0 }; k < total_count; ++k)
664+ {
665+ // Read in single exception
666+ exception_t except{};
667+
668+ auto string_buffer = std::vector<uint8_t>{};
669+ if (!read_next_string(reader, string_buffer))
670+ throw module_reader_exception{ "Failed to read exception name" };
671+
672+ except.name = std::make_unique<vm_string_t>(string_buffer.size(), string_buffer.data());
673+
674+ std::tie(success, except.pc) = read_next_operand(reader);
675+ if (!success) throw module_reader_exception{ "Failed to read exception pc" };
676+
677+ handler.exception_table.push_back(std::move(except));
678+ }
679+
680+ // Read in wildcard exception
681+ exception_t wildcard_exception{};
682+ std::tie(success, wildcard_exception.pc) = read_next_operand(reader);
683+ if (!success) throw module_reader_exception{ "Failed to read wildcard exception pc" };
684+
685+ handler.exception_table.push_back(std::move(wildcard_exception));
686+
687+ modobj.handler_section.push_back(std::move(handler));
688+ }
689+
690+ byte_t value;
691+ std::tie(success, value) = reader.get_next_byte();
692+ if (!success || value != 0)
693+ throw module_reader_exception{ "Failed to read final byte from handler section" };
694+ }
695+}
696+
697+export_function_t::export_function_t(export_function_t &&other)
698+ : pc{ other.pc }
699+ , frame_type{ other.frame_type }
700+ , sig{ other.sig }
701+ , name{ std::move(other.name) }
702+{
703+}
704+
705+export_function_t::~export_function_t()
706+{
707+ if (name != nullptr)
708+ name->release();
709+}
710+
711+import_function_t::import_function_t(import_function_t &&other)
712+ : sig{ other.sig }
713+ , name{ std::move(other.name) }
714+{
715+}
716+
717+import_function_t::~import_function_t()
718+{
719+ if (name != nullptr)
720+ name->release();
721+}
722+
723+import_vm_module_t::import_vm_module_t(import_vm_module_t &&other)
724+ : functions{ std::move(other.functions) }
725+{
726+}
727+
728+exception_t::exception_t(exception_t &&other)
729+ : pc{ other.pc }
730+ , name{ std::move(other.name) }
731+{
732+}
733+
734+exception_t::~exception_t()
735+{
736+ if (name != nullptr)
737+ name->release();
738+}
739+
740+handler_t::handler_t(handler_t &&other)
741+ : exception_offset{ other.exception_offset }
742+ , begin_pc{ other.begin_pc }
743+ , end_pc{ other.end_pc }
744+ , type_desc{ std::move(other.type_desc) }
745+ , exception_table{ std::move(other.exception_table) }
746+ , exception_type_count{ other.exception_type_count }
747+{
748+}
749+
750+vm_module_t::~vm_module_t()
751+{
752+ if (module_name != nullptr)
753+ module_name->release();
754+
755+ if (original_mp != nullptr)
756+ original_mp->release();
757+}
758+
759+std::unique_ptr<vm_module_t> disvm::read_module(const char *path)
760+{
761+ assert(path != nullptr);
762+
763+ auto module_file = std::ifstream{ path, std::ifstream::binary };
764+ if (!module_file.is_open())
765+ {
766+ assert(false && "Failed to open module");
767+ throw vm_user_exception{ "Failed to open module" };
768+ }
769+
770+ return read_module(module_file);
771+}
772+
773+std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data)
774+{
775+ util::buffered_reader_t reader{ data };
776+
777+ auto modobj = std::make_unique<vm_module_t>();
778+
779+ read_header(reader, *modobj);
780+
781+ if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_import_deprecated))
782+ throw module_reader_exception{ "Obsolete module" };
783+
784+ read_code_section(reader, *modobj);
785+ read_type_section(reader, *modobj);
786+ read_data_section(reader, *modobj);
787+ read_module_name(reader, *modobj);
788+ read_link_section(reader, *modobj);
789+
790+ if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_import))
791+ read_import_section(reader, *modobj);
792+
793+ if (util::has_flag(modobj->header.runtime_flag, runtime_flags_t::has_handler))
794+ read_handler_section(reader, *modobj);
795+
796+ // [TODO] Native code generation - runtime_flags_t::must_compile
797+ assert(!util::has_flag(modobj->header.runtime_flag, runtime_flags_t::must_compile));
798+
799+ return modobj;
800+}