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
21using namespace disvm;
22using namespace disvm::runtime;
23using namespace disvm::format;
24
25namespace
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
697export_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
705export_function_t::~export_function_t()
706{
707 if (name != nullptr)
708 name->release();
709}
710
711import_function_t::import_function_t(import_function_t &&other)
712 : sig{ other.sig }
713 , name{ std::move(other.name) }
714{
715}
716
717import_function_t::~import_function_t()
718{
719 if (name != nullptr)
720 name->release();
721}
722
723import_vm_module_t::import_vm_module_t(import_vm_module_t &&other)
724 : functions{ std::move(other.functions) }
725{
726}
727
728exception_t::exception_t(exception_t &&other)
729 : pc{ other.pc }
730 , name{ std::move(other.name) }
731{
732}
733
734exception_t::~exception_t()
735{
736 if (name != nullptr)
737 name->release();
738}
739
740handler_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
750vm_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
759std::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
773std::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}