Line data Source code
1 : /* Cam Mannett 2020
2 : *
3 : * See LICENSE file
4 : */
5 :
6 : #include "malbolge/c_interface.hpp"
7 : #include "malbolge/version.hpp"
8 : #include "malbolge/loader.hpp"
9 : #include "malbolge/virtual_cpu.hpp"
10 :
11 : #ifdef EMSCRIPTEN
12 : #include "malbolge/c_interface_wasm.hpp"
13 : #include <emscripten.h>
14 : #endif
15 :
16 : #include <unordered_map>
17 : #include <sstream>
18 :
19 : using namespace malbolge;
20 : using namespace std::string_literals;
21 :
22 : namespace
23 : {
24 : static_assert(static_cast<int>(MALBOLGE_VCPU_NUM_STATES) ==
25 : static_cast<int>(virtual_cpu::execution_state::NUM_STATES),
26 : "malbolge_vcpu_execution_state and virtual_cpu::execution_state mismatch");
27 : static_assert(static_cast<int>(MALBOLGE_VCPU_REGISTER_MAX) ==
28 : static_cast<int>(virtual_cpu::vcpu_register::NUM_REGISTERS),
29 : "malbolge_vcpu_register and virtual_cpu::vcpu_register mismatch");
30 :
31 : class vcpu_signal_manager
32 : {
33 : public:
34 : using callback_address = void*;
35 :
36 : enum class signal_type {
37 : STATE,
38 : OUTPUT,
39 : BREAKPOINT,
40 : NUM_TYPES
41 : };
42 :
43 24 : void connect(malbolge_virtual_cpu vcpu,
44 : signal_type signal,
45 : callback_address cb_address) noexcept
46 : {
47 24 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
48 :
49 24 : switch (signal) {
50 8 : case signal_type::STATE:
51 : {
52 62 : auto state_cb = [vcpu, cb_address](auto state, auto eptr) {
53 31 : auto cb = reinterpret_cast<malbolge_vcpu_state_callback>(cb_address);
54 31 : const auto c_state = static_cast<malbolge_vcpu_execution_state>(state);
55 31 : auto err = MALBOLGE_ERR_SUCCESS;
56 :
57 31 : if (eptr) {
58 : try {
59 2 : std::rethrow_exception(eptr);
60 1 : } catch (system_exception& e) {
61 0 : log::print(log::ERROR, e.what());
62 0 : err = static_cast<malbolge_result>(e.code().value());
63 1 : } catch (std::exception& e) {
64 1 : log::print(log::ERROR, e.what());
65 1 : err = MALBOLGE_ERR_EXECUTION_FAIL;
66 0 : } catch (...) {
67 0 : log::print(log::ERROR, "Unknown exception");
68 0 : err = MALBOLGE_ERR_UNKNOWN;
69 : }
70 : }
71 :
72 31 : cb(vcpu, c_state, err);
73 31 : };
74 :
75 8 : auto& conn = state_[vcpu][cb_address];
76 8 : conn = vcpu_ptr->register_for_state_signal(std::move(state_cb));
77 8 : break;
78 : }
79 8 : case signal_type::OUTPUT:
80 : {
81 64 : auto output_cb = [vcpu, cb_address](auto c) {
82 64 : auto cb = reinterpret_cast<malbolge_vcpu_output_callback>(cb_address);
83 64 : cb(vcpu, c);
84 72 : };
85 8 : auto& conn = output_[vcpu][cb_address];
86 8 : conn = vcpu_ptr->register_for_output_signal(std::move(output_cb));
87 8 : break;
88 : }
89 8 : case signal_type::BREAKPOINT:
90 : {
91 10 : auto bp_cb = [vcpu, cb_address](auto address) {
92 5 : auto cb = reinterpret_cast<malbolge_vcpu_breakpoint_hit_callback>(cb_address);
93 5 : cb(vcpu, static_cast<unsigned int>(address));
94 13 : };
95 8 : auto& conn = bp_[vcpu][cb_address];
96 8 : conn = vcpu_ptr->register_for_breakpoint_hit_signal(std::move(bp_cb));
97 8 : break;
98 : }
99 0 : default:
100 : // Will never get here due to the strong enum typing and the
101 : // static_assert check below
102 0 : break;
103 : }
104 24 : }
105 :
106 4 : void disconnect(malbolge_virtual_cpu vcpu,
107 : signal_type signal,
108 : callback_address cb_address) noexcept
109 : {
110 4 : auto simple_remover = [&](auto& map) {
111 4 : auto vcpu_it = map.find(vcpu);
112 4 : if (vcpu_it == map.end()) {
113 0 : return;
114 : }
115 :
116 4 : auto it = vcpu_it->second.find(cb_address);
117 4 : if (it == vcpu_it->second.end()) {
118 0 : return;
119 : }
120 :
121 4 : it->second.disconnect();
122 4 : vcpu_it->second.erase(it);
123 :
124 4 : if (vcpu_it->second.empty()) {
125 4 : map.erase(vcpu_it);
126 : }
127 4 : };
128 :
129 4 : switch (signal) {
130 1 : case signal_type::STATE:
131 1 : simple_remover(state_);
132 1 : break;
133 2 : case signal_type::OUTPUT:
134 2 : simple_remover(output_);
135 2 : break;
136 1 : case signal_type::BREAKPOINT:
137 1 : simple_remover(bp_);
138 1 : break;
139 0 : default:
140 : // Will never get here due to the strong enum typing and the
141 : // static_assert check below
142 0 : break;
143 : }
144 4 : }
145 :
146 : private:
147 : static_assert(static_cast<int>(signal_type::NUM_TYPES) == 3,
148 : "Number of C API signal types has changed");
149 :
150 : template <typename Connection>
151 : using address_map = std::unordered_map<malbolge_virtual_cpu,
152 : std::unordered_map<callback_address,
153 : Connection>>;
154 :
155 : address_map<virtual_cpu::state_signal_type::connection> state_;
156 : address_map<virtual_cpu::output_signal_type::connection> output_;
157 : address_map<virtual_cpu::breakpoint_hit_signal_type::connection> bp_;
158 : };
159 :
160 : vcpu_signal_manager signal_manager_;
161 : }
162 :
163 4 : unsigned int malbolge_log_level()
164 : {
165 4 : return log::ERROR - log::log_level();
166 : }
167 :
168 5 : int malbolge_set_log_level(unsigned int level)
169 : {
170 5 : if (level >= log::NUM_LOG_LEVELS) {
171 1 : log::print(log::ERROR, "Log level is too high");
172 1 : return MALBOLGE_ERR_INVALID_LOG_LEVEL;
173 : }
174 :
175 4 : log::set_log_level(static_cast<log::level>(log::NUM_LOG_LEVELS - 1 - level));
176 4 : return MALBOLGE_ERR_SUCCESS;
177 : }
178 :
179 1 : const char *malbolge_version()
180 : {
181 1 : return version_string;
182 : }
183 :
184 5 : int malbolge_is_likely_normalised_source(const char *buffer,
185 : unsigned long size)
186 : {
187 5 : if (!buffer) [[unlikely]] {
188 1 : log::print(log::ERROR, "NULL program source pointer");
189 1 : return MALBOLGE_ERR_NULL_ARG;
190 : }
191 :
192 4 : return is_likely_normalised_source(buffer, buffer + size);
193 : }
194 :
195 7 : int malbolge_normalise_source(char *buffer,
196 : unsigned long size,
197 : unsigned long *new_size,
198 : unsigned int *fail_line,
199 : unsigned int *fail_column)
200 : {
201 7 : if (!buffer) [[unlikely]] {
202 2 : log::print(log::ERROR, "NULL program source pointer");
203 2 : return MALBOLGE_ERR_NULL_ARG;
204 : }
205 5 : if (!new_size) [[unlikely]] {
206 1 : log::print(log::ERROR, "NULL normalised program size pointer");
207 1 : return MALBOLGE_ERR_NULL_ARG;
208 : }
209 :
210 : try {
211 4 : auto it = normalise_source(buffer, buffer + size);
212 2 : *new_size = std::distance(buffer, it);
213 :
214 2 : if (*new_size < size) {
215 1 : buffer[*new_size] = '\n';
216 : }
217 :
218 2 : return MALBOLGE_ERR_SUCCESS;
219 2 : } catch (parse_exception& e) {
220 2 : log::print(log::ERROR, e.what());
221 :
222 2 : if (e.has_location()) {
223 2 : if (fail_line) {
224 1 : *fail_line = e.location()->line;
225 : }
226 2 : if (fail_column) {
227 1 : *fail_column = e.location()->column;
228 : }
229 : }
230 2 : return MALBOLGE_ERR_PARSE_FAIL;
231 0 : } catch (...) {
232 0 : log::print(log::ERROR, "Unknown exception");
233 : }
234 :
235 0 : return MALBOLGE_ERR_UNKNOWN;
236 : }
237 :
238 4 : int malbolge_denormalise_source(char *buffer,
239 : unsigned long size,
240 : unsigned int *fail_column)
241 : {
242 4 : if (!buffer) [[unlikely]] {
243 1 : log::print(log::ERROR, "NULL program source pointer");
244 1 : return MALBOLGE_ERR_NULL_ARG;
245 : }
246 :
247 : try {
248 3 : denormalise_source(buffer, buffer + size);
249 1 : return MALBOLGE_ERR_SUCCESS;
250 2 : } catch (parse_exception& e) {
251 2 : log::print(log::ERROR, e.what());
252 :
253 2 : if (e.has_location()) {
254 2 : if (fail_column) {
255 1 : *fail_column = e.location()->column;
256 : }
257 : }
258 2 : return MALBOLGE_ERR_PARSE_FAIL;
259 0 : } catch (...) {
260 0 : log::print(log::ERROR, "Unknown exception");
261 : }
262 :
263 0 : return MALBOLGE_ERR_UNKNOWN;
264 : }
265 :
266 15 : malbolge_virtual_memory malbolge_load_program(char *buffer,
267 : unsigned long size,
268 : malbolge_load_normalised_mode mode,
269 : unsigned int *fail_line,
270 : unsigned int *fail_column)
271 : {
272 15 : if (!buffer) [[unlikely]] {
273 1 : log::print(log::ERROR, "NULL program source pointer");
274 1 : return nullptr;
275 : }
276 :
277 : try {
278 : auto vmem = load(buffer,
279 : buffer + size,
280 14 : static_cast<load_normalised_mode>(mode));
281 10 : return new virtual_memory(std::move(vmem));
282 8 : } catch (parse_exception& e) {
283 4 : log::print(log::ERROR, e.what());
284 :
285 4 : if (e.has_location()) {
286 1 : if (fail_line) {
287 1 : *fail_line = e.location()->line;
288 : }
289 1 : if (fail_column) {
290 1 : *fail_column = e.location()->column;
291 : }
292 : }
293 0 : } catch (std::exception& e) {
294 0 : log::print(log::ERROR, e.what());
295 0 : } catch (...) {
296 0 : log::print(log::ERROR, "Unknown exception");
297 : }
298 :
299 4 : return nullptr;
300 : }
301 :
302 8 : void malbolge_free_virtual_memory(malbolge_virtual_memory vmem)
303 : {
304 8 : delete static_cast<virtual_memory*>(vmem);
305 8 : }
306 :
307 9 : malbolge_virtual_cpu malbolge_create_vcpu(malbolge_virtual_memory vmem)
308 : {
309 9 : if (!vmem) [[unlikely]] {
310 1 : log::print(log::ERROR, "NULL virtual memory pointer");
311 1 : return nullptr;
312 : }
313 :
314 8 : auto vcpu = new virtual_cpu{std::move(*static_cast<virtual_memory*>(vmem))};
315 8 : malbolge_free_virtual_memory(vmem);
316 :
317 8 : return vcpu;
318 : }
319 :
320 2 : void malbolge_free_vcpu(malbolge_virtual_cpu vcpu)
321 : {
322 2 : delete static_cast<virtual_cpu*>(vcpu);
323 2 : }
324 :
325 9 : int malbolge_vcpu_attach_callbacks(malbolge_virtual_cpu vcpu,
326 : malbolge_vcpu_state_callback state_cb,
327 : malbolge_vcpu_output_callback output_cb,
328 : malbolge_vcpu_breakpoint_hit_callback bp_cb)
329 : {
330 9 : if (!vcpu) [[unlikely]] {
331 1 : log::print(log::ERROR, "NULL virtual CPU pointer");
332 1 : return MALBOLGE_ERR_NULL_ARG;
333 : }
334 :
335 8 : if (state_cb) {
336 8 : signal_manager_.connect(vcpu,
337 : vcpu_signal_manager::signal_type::STATE,
338 : reinterpret_cast<void*>(state_cb));
339 : }
340 8 : if (output_cb) {
341 8 : signal_manager_.connect(vcpu,
342 : vcpu_signal_manager::signal_type::OUTPUT,
343 : reinterpret_cast<void*>(output_cb));
344 : }
345 8 : if (bp_cb) {
346 8 : signal_manager_.connect(vcpu,
347 : vcpu_signal_manager::signal_type::BREAKPOINT,
348 : reinterpret_cast<void*>(bp_cb));
349 : }
350 :
351 8 : return MALBOLGE_ERR_SUCCESS;
352 : }
353 :
354 3 : int malbolge_vcpu_detach_callbacks(malbolge_virtual_cpu vcpu,
355 : malbolge_vcpu_state_callback state_cb,
356 : malbolge_vcpu_output_callback output_cb,
357 : malbolge_vcpu_breakpoint_hit_callback bp_cb)
358 : {
359 3 : if (!vcpu) [[unlikely]] {
360 1 : log::print(log::ERROR, "NULL virtual CPU pointer");
361 1 : return MALBOLGE_ERR_NULL_ARG;
362 : }
363 :
364 2 : if (state_cb) {
365 1 : signal_manager_.disconnect(vcpu,
366 : vcpu_signal_manager::signal_type::STATE,
367 : reinterpret_cast<void*>(state_cb));
368 : }
369 2 : if (output_cb) {
370 2 : signal_manager_.disconnect(vcpu,
371 : vcpu_signal_manager::signal_type::OUTPUT,
372 : reinterpret_cast<void*>(output_cb));
373 : }
374 2 : if (bp_cb) {
375 1 : signal_manager_.disconnect(vcpu,
376 : vcpu_signal_manager::signal_type::BREAKPOINT,
377 : reinterpret_cast<void*>(bp_cb));
378 : }
379 :
380 2 : return MALBOLGE_ERR_SUCCESS;
381 : }
382 :
383 16 : int malbolge_vcpu_run(malbolge_virtual_cpu vcpu)
384 : {
385 16 : if (!vcpu) [[unlikely]] {
386 1 : log::print(log::ERROR, "NULL virtual CPU pointer");
387 1 : return MALBOLGE_ERR_NULL_ARG;
388 : }
389 :
390 :
391 15 : auto err = static_cast<int>(MALBOLGE_ERR_UNKNOWN);
392 : try {
393 15 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
394 15 : vcpu_ptr->run();
395 14 : return MALBOLGE_ERR_SUCCESS;
396 1 : } catch (std::exception& e) {
397 1 : log::print(log::ERROR, e.what());
398 1 : err = MALBOLGE_ERR_EXECUTION_FAIL;
399 0 : } catch (...) {
400 0 : log::print(log::ERROR, "Unknown exception");
401 : }
402 :
403 1 : return err;
404 : }
405 :
406 4 : int malbolge_vcpu_pause(malbolge_virtual_cpu vcpu)
407 : {
408 4 : if (!vcpu) [[unlikely]] {
409 1 : log::print(log::ERROR, "NULL virtual CPU pointer");
410 1 : return MALBOLGE_ERR_NULL_ARG;
411 : }
412 :
413 3 : auto err = static_cast<int>(MALBOLGE_ERR_UNKNOWN);
414 : try {
415 3 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
416 3 : vcpu_ptr->pause();
417 2 : return MALBOLGE_ERR_SUCCESS;
418 1 : } catch (std::exception& e) {
419 1 : log::print(log::ERROR, e.what());
420 1 : err = MALBOLGE_ERR_EXECUTION_FAIL;
421 0 : } catch (...) {
422 0 : log::print(log::ERROR, "Unknown exception");
423 : }
424 :
425 1 : return err;
426 : }
427 :
428 4 : int malbolge_vcpu_step(malbolge_virtual_cpu vcpu)
429 : {
430 4 : if (!vcpu) [[unlikely]] {
431 1 : log::print(log::ERROR, "NULL virtual CPU pointer");
432 1 : return MALBOLGE_ERR_NULL_ARG;
433 : }
434 :
435 3 : auto err = static_cast<int>(MALBOLGE_ERR_UNKNOWN);
436 : try {
437 3 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
438 3 : vcpu_ptr->step();
439 2 : return MALBOLGE_ERR_SUCCESS;
440 1 : } catch (std::exception& e) {
441 1 : log::print(log::ERROR, e.what());
442 1 : err = MALBOLGE_ERR_EXECUTION_FAIL;
443 0 : } catch (...) {
444 0 : log::print(log::ERROR, "Unknown exception");
445 : }
446 :
447 1 : return err;
448 : }
449 :
450 6 : int malbolge_vcpu_add_input(malbolge_virtual_cpu vcpu,
451 : const char* buffer,
452 : unsigned int size)
453 : {
454 6 : if (!vcpu) [[unlikely]] {
455 2 : log::print(log::ERROR, "NULL virtual CPU pointer");
456 2 : return MALBOLGE_ERR_NULL_ARG;
457 : }
458 4 : if (!buffer) [[unlikely]] {
459 1 : log::print(log::ERROR, "NULL buffer pointer");
460 1 : return MALBOLGE_ERR_NULL_ARG;
461 : }
462 :
463 : try {
464 3 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
465 3 : vcpu_ptr->add_input({buffer, size});
466 3 : return MALBOLGE_ERR_SUCCESS;
467 0 : } catch (std::exception& e) {
468 0 : log::print(log::ERROR, e.what());
469 0 : } catch (...) {
470 0 : log::print(log::ERROR, "Unknown exception");
471 : }
472 :
473 0 : return MALBOLGE_ERR_UNKNOWN;
474 : }
475 :
476 6 : int malbolge_vcpu_add_breakpoint(malbolge_virtual_cpu vcpu,
477 : unsigned int address,
478 : unsigned int ignore_count)
479 : {
480 6 : if (!vcpu) [[unlikely]] {
481 1 : log::print(log::ERROR, "NULL virtual CPU pointer");
482 1 : return MALBOLGE_ERR_NULL_ARG;
483 : }
484 :
485 : try {
486 5 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
487 5 : vcpu_ptr->add_breakpoint(address, ignore_count);
488 5 : return MALBOLGE_ERR_SUCCESS;
489 0 : } catch (std::exception& e) {
490 0 : log::print(log::ERROR, e.what());
491 0 : } catch (...) {
492 0 : log::print(log::ERROR, "Unknown exception");
493 : }
494 :
495 0 : return MALBOLGE_ERR_UNKNOWN;
496 : }
497 :
498 2 : int malbolge_vcpu_remove_breakpoint(malbolge_virtual_cpu vcpu,
499 : unsigned int address)
500 : {
501 2 : if (!vcpu) [[unlikely]] {
502 1 : log::print(log::ERROR, "NULL virtual CPU pointer");
503 1 : return MALBOLGE_ERR_NULL_ARG;
504 : }
505 :
506 : try {
507 1 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
508 1 : vcpu_ptr->remove_breakpoint(address);
509 1 : return MALBOLGE_ERR_SUCCESS;
510 0 : } catch (std::exception& e) {
511 0 : log::print(log::ERROR, e.what());
512 0 : } catch (...) {
513 0 : log::print(log::ERROR, "Unknown exception");
514 : }
515 :
516 0 : return MALBOLGE_ERR_UNKNOWN;
517 : }
518 :
519 8 : int malbolge_vcpu_address_value(malbolge_virtual_cpu vcpu,
520 : unsigned int address,
521 : malbolge_vcpu_address_value_callback cb)
522 : {
523 8 : if (!vcpu) [[unlikely]] {
524 2 : log::print(log::ERROR, "NULL virtual CPU pointer");
525 2 : return MALBOLGE_ERR_NULL_ARG;
526 : }
527 6 : if (!cb) [[unlikely]] {
528 1 : log::print(log::ERROR, "NULL callback");
529 1 : return MALBOLGE_ERR_NULL_ARG;
530 : }
531 :
532 : try {
533 5 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
534 :
535 10 : auto wrapped_cb = [cb, vcpu](auto address, auto value) {
536 5 : cb(vcpu,
537 : static_cast<unsigned int>(address),
538 : static_cast<unsigned int>(value));
539 10 : };
540 5 : vcpu_ptr->address_value(address, std::move(wrapped_cb));
541 5 : return MALBOLGE_ERR_SUCCESS;
542 0 : } catch (std::exception& e) {
543 0 : log::print(log::ERROR, e.what());
544 0 : } catch (...) {
545 0 : log::print(log::ERROR, "Unknown exception");
546 : }
547 :
548 0 : return MALBOLGE_ERR_UNKNOWN;
549 : }
550 :
551 13 : int malbolge_vcpu_register_value(malbolge_virtual_cpu vcpu,
552 : enum malbolge_vcpu_register reg,
553 : malbolge_vcpu_register_value_callback cb)
554 : {
555 13 : if (!vcpu) [[unlikely]] {
556 2 : log::print(log::ERROR, "NULL virtual CPU pointer");
557 2 : return MALBOLGE_ERR_NULL_ARG;
558 : }
559 11 : if (!cb) [[unlikely]] {
560 1 : log::print(log::ERROR, "NULL callback");
561 1 : return MALBOLGE_ERR_NULL_ARG;
562 : }
563 :
564 : try {
565 10 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
566 :
567 18 : auto wrapped_cb = [cb, vcpu](auto reg, auto address, auto value) {
568 24 : cb(vcpu,
569 : static_cast<malbolge_vcpu_register>(reg),
570 15 : address ? static_cast<unsigned int>(*address) : 0,
571 : static_cast<unsigned int>(value));
572 19 : };
573 10 : vcpu_ptr->register_value(static_cast<virtual_cpu::vcpu_register>(reg),
574 20 : std::move(wrapped_cb));
575 10 : return MALBOLGE_ERR_SUCCESS;
576 0 : } catch (std::exception& e) {
577 0 : log::print(log::ERROR, e.what());
578 0 : } catch (...) {
579 0 : log::print(log::ERROR, "Unknown exception");
580 : }
581 :
582 0 : return MALBOLGE_ERR_UNKNOWN;
583 : }
584 :
585 : #ifdef EMSCRIPTEN
586 : EM_JS(void, malbolge_state_cb, (int state, int err_code, malbolge_virtual_cpu ptr),
587 : {
588 : postMessage({
589 : cmd: "malbolgevCPUState",
590 : state: state,
591 : errorCode: err_code,
592 : vcpu: ptr
593 : });
594 : })
595 :
596 : EM_JS(void, malbolge_breakpoint_cb, (int address, malbolge_virtual_cpu ptr),
597 : {
598 : postMessage({
599 : cmd: "malbolgeBreakpoint",
600 : address: address,
601 : vcpu: ptr
602 : });
603 : })
604 :
605 : EM_JS(void, malbolge_output_cb, (const char* text, malbolge_virtual_cpu ptr),
606 : {
607 : postMessage({
608 : cmd: "malbolgeOutput",
609 : data: UTF8ToString(text),
610 : vcpu: ptr
611 : });
612 : })
613 :
614 : int malbolge_vcpu_run_wasm(malbolge_virtual_cpu vcpu)
615 : {
616 : constexpr static auto max_buf_size = std::size_t{10};
617 :
618 : if (!vcpu) [[unlikely]] {
619 : log::print(log::ERROR, "NULL virtual CPU pointer");
620 : return MALBOLGE_ERR_NULL_ARG;
621 : }
622 :
623 : auto vcpu_ptr = static_cast<virtual_cpu*>(vcpu);
624 :
625 : // As the signals are called from the same thread, we do not need any
626 : // locking around shared state
627 : auto output_buf = std::make_shared<std::string>();
628 : output_buf->reserve(max_buf_size);
629 :
630 : vcpu_ptr->register_for_output_signal([vcpu, buf = output_buf](auto c) {
631 : buf->push_back(c);
632 : if (buf->size() >= max_buf_size) {
633 : malbolge_output_cb(buf->data(), vcpu);
634 : buf->clear();
635 : }
636 : });
637 : vcpu_ptr->register_for_state_signal([vcpu, buf = std::move(output_buf)]
638 : (auto state, auto eptr) {
639 : // Flush the output buffer on a vCPU state change
640 : if (!buf->empty()) {
641 : malbolge_output_cb(buf->data(), vcpu);
642 : buf->clear();
643 : }
644 :
645 : const auto c_state = static_cast<malbolge_vcpu_execution_state>(state);
646 : auto err = MALBOLGE_ERR_SUCCESS;
647 :
648 : if (eptr) {
649 : try {
650 : std::rethrow_exception(eptr);
651 : } catch (system_exception& e) {
652 : log::print(log::ERROR, e.what());
653 : err = static_cast<malbolge_result>(e.code().value());
654 : } catch (std::exception& e) {
655 : log::print(log::ERROR, e.what());
656 : err = MALBOLGE_ERR_EXECUTION_FAIL;
657 : } catch (...) {
658 : log::print(log::ERROR, "Unknown exception");
659 : err = MALBOLGE_ERR_UNKNOWN;
660 : }
661 : }
662 :
663 : malbolge_state_cb(c_state, err, vcpu);
664 : });
665 : vcpu_ptr->register_for_breakpoint_hit_signal([vcpu](auto address) {
666 : malbolge_breakpoint_cb(static_cast<int>(address), vcpu);
667 : });
668 :
669 : auto err = static_cast<int>(MALBOLGE_ERR_UNKNOWN);
670 : try {
671 : vcpu_ptr->run();
672 : return MALBOLGE_ERR_SUCCESS;
673 : } catch (std::exception& e) {
674 : log::print(log::ERROR, e.what());
675 : err = MALBOLGE_ERR_EXECUTION_FAIL;
676 : } catch (...) {
677 : log::print(log::ERROR, "Unknown exception");
678 : }
679 :
680 : return err;
681 : }
682 : #endif
|