wasm.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /*
  2. * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibCore/ArgsParser.h>
  8. #include <LibCore/File.h>
  9. #include <LibCore/MappedFile.h>
  10. #include <LibCore/MemoryStream.h>
  11. #include <LibLine/Editor.h>
  12. #include <LibMain/Main.h>
  13. #include <LibWasm/AbstractMachine/AbstractMachine.h>
  14. #include <LibWasm/AbstractMachine/BytecodeInterpreter.h>
  15. #include <LibWasm/Printer/Printer.h>
  16. #include <LibWasm/Types.h>
  17. #include <signal.h>
  18. #include <unistd.h>
  19. RefPtr<Line::Editor> g_line_editor;
  20. static OwnPtr<Core::Stream::Stream> g_stdout {};
  21. static OwnPtr<Wasm::Printer> g_printer {};
  22. static bool g_continue { false };
  23. static void (*old_signal)(int);
  24. static Wasm::DebuggerBytecodeInterpreter g_interpreter;
  25. static void sigint_handler(int)
  26. {
  27. if (!g_continue) {
  28. signal(SIGINT, old_signal);
  29. kill(getpid(), SIGINT);
  30. }
  31. g_continue = false;
  32. }
  33. static bool post_interpret_hook(Wasm::Configuration&, Wasm::InstructionPointer& ip, Wasm::Instruction const& instr, Wasm::Interpreter const& interpreter)
  34. {
  35. if (interpreter.did_trap()) {
  36. g_continue = false;
  37. warnln("Trapped when executing ip={}", ip);
  38. g_printer->print(instr);
  39. warnln("Trap reason: {}", interpreter.trap_reason());
  40. const_cast<Wasm::Interpreter&>(interpreter).clear_trap();
  41. }
  42. return true;
  43. }
  44. static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPointer& ip, Wasm::Instruction const& instr)
  45. {
  46. static bool always_print_stack = false;
  47. static bool always_print_instruction = false;
  48. if (always_print_stack)
  49. config.dump_stack();
  50. if (always_print_instruction) {
  51. g_stdout->write(DeprecatedString::formatted("{:0>4} ", ip.value()).bytes()).release_value_but_fixme_should_propagate_errors();
  52. g_printer->print(instr);
  53. }
  54. if (g_continue)
  55. return true;
  56. g_stdout->write(DeprecatedString::formatted("{:0>4} ", ip.value()).bytes()).release_value_but_fixme_should_propagate_errors();
  57. g_printer->print(instr);
  58. DeprecatedString last_command = "";
  59. for (;;) {
  60. auto result = g_line_editor->get_line("> ");
  61. if (result.is_error()) {
  62. return false;
  63. }
  64. auto str = result.release_value();
  65. g_line_editor->add_to_history(str);
  66. if (str.is_empty())
  67. str = last_command;
  68. else
  69. last_command = str;
  70. auto args = str.split_view(' ');
  71. if (args.is_empty())
  72. continue;
  73. auto& cmd = args[0];
  74. if (cmd.is_one_of("h", "help")) {
  75. warnln("Wasm shell commands");
  76. warnln("Toplevel:");
  77. warnln("- [s]tep Run one instruction");
  78. warnln("- next Alias for step");
  79. warnln("- [c]ontinue Execute until a trap or the program exit point");
  80. warnln("- [p]rint <args...> Print various things (see section on print)");
  81. warnln("- call <fn> <args...> Call the function <fn> with the given arguments");
  82. warnln("- set <args...> Set shell option (see section on settings)");
  83. warnln("- unset <args...> Unset shell option (see section on settings)");
  84. warnln("- [h]elp Print this help");
  85. warnln();
  86. warnln("Print:");
  87. warnln("- print [s]tack Print the contents of the stack, including frames and labels");
  88. warnln("- print [[m]em]ory <index> Print the contents of the memory identified by <index>");
  89. warnln("- print [[i]nstr]uction Print the current instruction");
  90. warnln("- print [[f]unc]tion <index> Print the function identified by <index>");
  91. warnln();
  92. warnln("Settings:");
  93. warnln("- set print stack Make the shell print the stack on every instruction executed");
  94. warnln("- set print [instr]uction Make the shell print the instruction that will be executed next");
  95. warnln();
  96. continue;
  97. }
  98. if (cmd.is_one_of("s", "step", "next")) {
  99. return true;
  100. }
  101. if (cmd.is_one_of("p", "print")) {
  102. if (args.size() < 2) {
  103. warnln("Print what?");
  104. continue;
  105. }
  106. auto& what = args[1];
  107. if (what.is_one_of("s", "stack")) {
  108. config.dump_stack();
  109. continue;
  110. }
  111. if (what.is_one_of("m", "mem", "memory")) {
  112. if (args.size() < 3) {
  113. warnln("print what memory?");
  114. continue;
  115. }
  116. auto value = args[2].to_uint<u64>();
  117. if (!value.has_value()) {
  118. warnln("invalid memory index {}", args[2]);
  119. continue;
  120. }
  121. auto mem = config.store().get(Wasm::MemoryAddress(value.value()));
  122. if (!mem) {
  123. warnln("invalid memory index {} (not found)", args[2]);
  124. continue;
  125. }
  126. warnln("{:>32hex-dump}", mem->data().bytes());
  127. continue;
  128. }
  129. if (what.is_one_of("i", "instr", "instruction")) {
  130. g_printer->print(instr);
  131. continue;
  132. }
  133. if (what.is_one_of("f", "func", "function")) {
  134. if (args.size() < 3) {
  135. warnln("print what function?");
  136. continue;
  137. }
  138. auto value = args[2].to_uint<u64>();
  139. if (!value.has_value()) {
  140. warnln("invalid function index {}", args[2]);
  141. continue;
  142. }
  143. auto fn = config.store().get(Wasm::FunctionAddress(value.value()));
  144. if (!fn) {
  145. warnln("invalid function index {} (not found)", args[2]);
  146. continue;
  147. }
  148. if (auto* fn_value = fn->get_pointer<Wasm::HostFunction>()) {
  149. warnln("Host function at {:p}", &fn_value->function());
  150. continue;
  151. }
  152. if (auto* fn_value = fn->get_pointer<Wasm::WasmFunction>()) {
  153. g_printer->print(fn_value->code());
  154. continue;
  155. }
  156. }
  157. }
  158. if (cmd == "call"sv) {
  159. if (args.size() < 2) {
  160. warnln("call what?");
  161. continue;
  162. }
  163. Optional<Wasm::FunctionAddress> address;
  164. auto index = args[1].to_uint<u64>();
  165. if (index.has_value()) {
  166. address = config.frame().module().functions()[index.value()];
  167. } else {
  168. auto& name = args[1];
  169. for (auto& export_ : config.frame().module().exports()) {
  170. if (export_.name() == name) {
  171. if (auto addr = export_.value().get_pointer<Wasm::FunctionAddress>()) {
  172. address = *addr;
  173. break;
  174. }
  175. }
  176. }
  177. }
  178. if (!address.has_value()) {
  179. failed_to_find:;
  180. warnln("Could not find a function {}", args[1]);
  181. continue;
  182. }
  183. auto fn = config.store().get(*address);
  184. if (!fn)
  185. goto failed_to_find;
  186. auto type = fn->visit([&](auto& value) { return value.type(); });
  187. if (type.parameters().size() + 2 != args.size()) {
  188. warnln("Expected {} arguments for call, but found only {}", type.parameters().size(), args.size() - 2);
  189. continue;
  190. }
  191. Vector<u64> values_to_push;
  192. Vector<Wasm::Value> values;
  193. for (size_t index = 2; index < args.size(); ++index)
  194. values_to_push.append(args[index].to_uint().value_or(0));
  195. for (auto& param : type.parameters())
  196. values.append(Wasm::Value { param, values_to_push.take_last() });
  197. Wasm::Result result { Wasm::Trap {} };
  198. {
  199. Wasm::BytecodeInterpreter::CallFrameHandle handle { g_interpreter, config };
  200. result = config.call(g_interpreter, *address, move(values));
  201. }
  202. if (result.is_trap())
  203. warnln("Execution trapped: {}", result.trap().reason);
  204. if (!result.values().is_empty())
  205. warnln("Returned:");
  206. for (auto& value : result.values()) {
  207. g_stdout->write(" -> "sv.bytes()).release_value_but_fixme_should_propagate_errors();
  208. g_printer->print(value);
  209. }
  210. continue;
  211. }
  212. if (cmd.is_one_of("set", "unset")) {
  213. auto value = !cmd.starts_with('u');
  214. if (args.size() < 3) {
  215. warnln("(un)set what (to what)?");
  216. continue;
  217. }
  218. if (args[1] == "print"sv) {
  219. if (args[2] == "stack"sv)
  220. always_print_stack = value;
  221. else if (args[2].is_one_of("instr", "instruction"))
  222. always_print_instruction = value;
  223. else
  224. warnln("Unknown print category '{}'", args[2]);
  225. continue;
  226. }
  227. warnln("Unknown set category '{}'", args[1]);
  228. continue;
  229. }
  230. if (cmd.is_one_of("c", "continue")) {
  231. g_continue = true;
  232. return true;
  233. }
  234. warnln("Command not understood: {}", cmd);
  235. }
  236. }
  237. static Optional<Wasm::Module> parse(StringView filename)
  238. {
  239. auto result = Core::MappedFile::map(filename);
  240. if (result.is_error()) {
  241. warnln("Failed to open {}: {}", filename, result.error());
  242. return {};
  243. }
  244. auto stream = Core::Stream::FixedMemoryStream::construct(ReadonlyBytes { result.value()->data(), result.value()->size() }).release_value_but_fixme_should_propagate_errors();
  245. auto parse_result = Wasm::Module::parse(*stream);
  246. if (parse_result.is_error()) {
  247. warnln("Something went wrong, either the file is invalid, or there's a bug with LibWasm!");
  248. warnln("The parse error was {}", Wasm::parse_error_to_deprecated_string(parse_result.error()));
  249. return {};
  250. }
  251. return parse_result.release_value();
  252. }
  253. static void print_link_error(Wasm::LinkError const& error)
  254. {
  255. for (auto const& missing : error.missing_imports)
  256. warnln("Missing import '{}'", missing);
  257. }
  258. ErrorOr<int> serenity_main(Main::Arguments arguments)
  259. {
  260. StringView filename;
  261. bool print = false;
  262. bool attempt_instantiate = false;
  263. bool debug = false;
  264. bool export_all_imports = false;
  265. bool shell_mode = false;
  266. DeprecatedString exported_function_to_execute;
  267. Vector<u64> values_to_push;
  268. Vector<DeprecatedString> modules_to_link_in;
  269. Core::ArgsParser parser;
  270. parser.add_positional_argument(filename, "File name to parse", "file");
  271. parser.add_option(debug, "Open a debugger", "debug", 'd');
  272. parser.add_option(print, "Print the parsed module", "print", 'p');
  273. parser.add_option(attempt_instantiate, "Attempt to instantiate the module", "instantiate", 'i');
  274. parser.add_option(exported_function_to_execute, "Attempt to execute the named exported function from the module (implies -i)", "execute", 'e', "name");
  275. parser.add_option(export_all_imports, "Export noop functions corresponding to imports", "export-noop", 0);
  276. parser.add_option(shell_mode, "Launch a REPL in the module's context (implies -i)", "shell", 's');
  277. parser.add_option(Core::ArgsParser::Option {
  278. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  279. .help_string = "Extra modules to link with, use to resolve imports",
  280. .long_name = "link",
  281. .short_name = 'l',
  282. .value_name = "file",
  283. .accept_value = [&](char const* str) {
  284. if (auto v = StringView { str, strlen(str) }; !v.is_empty()) {
  285. modules_to_link_in.append(v);
  286. return true;
  287. }
  288. return false;
  289. },
  290. });
  291. parser.add_option(Core::ArgsParser::Option {
  292. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  293. .help_string = "Supply arguments to the function (default=0) (expects u64, casts to required type)",
  294. .long_name = "arg",
  295. .short_name = 0,
  296. .value_name = "u64",
  297. .accept_value = [&](char const* str) -> bool {
  298. if (auto v = StringView { str, strlen(str) }.to_uint<u64>(); v.has_value()) {
  299. values_to_push.append(v.value());
  300. return true;
  301. }
  302. return false;
  303. },
  304. });
  305. parser.parse(arguments);
  306. if (shell_mode) {
  307. debug = true;
  308. attempt_instantiate = true;
  309. }
  310. if (!shell_mode && debug && exported_function_to_execute.is_empty()) {
  311. warnln("Debug what? (pass -e fn)");
  312. return 1;
  313. }
  314. if (debug || shell_mode) {
  315. old_signal = signal(SIGINT, sigint_handler);
  316. }
  317. if (!exported_function_to_execute.is_empty())
  318. attempt_instantiate = true;
  319. auto parse_result = parse(filename);
  320. if (!parse_result.has_value())
  321. return 1;
  322. g_stdout = TRY(Core::Stream::File::standard_output());
  323. g_printer = TRY(try_make<Wasm::Printer>(*g_stdout));
  324. if (print && !attempt_instantiate) {
  325. Wasm::Printer printer(*g_stdout);
  326. printer.print(parse_result.value());
  327. }
  328. if (attempt_instantiate) {
  329. Wasm::AbstractMachine machine;
  330. Core::EventLoop main_loop;
  331. if (debug) {
  332. g_line_editor = Line::Editor::construct();
  333. g_interpreter.pre_interpret_hook = pre_interpret_hook;
  334. g_interpreter.post_interpret_hook = post_interpret_hook;
  335. }
  336. // First, resolve the linked modules
  337. NonnullOwnPtrVector<Wasm::ModuleInstance> linked_instances;
  338. Vector<Wasm::Module> linked_modules;
  339. for (auto& name : modules_to_link_in) {
  340. auto parse_result = parse(name);
  341. if (!parse_result.has_value()) {
  342. warnln("Failed to parse linked module '{}'", name);
  343. return 1;
  344. }
  345. linked_modules.append(parse_result.release_value());
  346. Wasm::Linker linker { linked_modules.last() };
  347. for (auto& instance : linked_instances)
  348. linker.link(instance);
  349. auto link_result = linker.finish();
  350. if (link_result.is_error()) {
  351. warnln("Linking imported module '{}' failed", name);
  352. print_link_error(link_result.error());
  353. return 1;
  354. }
  355. auto instantiation_result = machine.instantiate(linked_modules.last(), link_result.release_value());
  356. if (instantiation_result.is_error()) {
  357. warnln("Instantiation of imported module '{}' failed: {}", name, instantiation_result.error().error);
  358. return 1;
  359. }
  360. linked_instances.append(instantiation_result.release_value());
  361. }
  362. Wasm::Linker linker { parse_result.value() };
  363. for (auto& instance : linked_instances)
  364. linker.link(instance);
  365. if (export_all_imports) {
  366. HashMap<Wasm::Linker::Name, Wasm::ExternValue> exports;
  367. for (auto& entry : linker.unresolved_imports()) {
  368. if (!entry.type.has<Wasm::TypeIndex>())
  369. continue;
  370. auto type = parse_result.value().type(entry.type.get<Wasm::TypeIndex>());
  371. auto address = machine.store().allocate(Wasm::HostFunction(
  372. [name = entry.name, type = type](auto&, auto& arguments) -> Wasm::Result {
  373. StringBuilder argument_builder;
  374. bool first = true;
  375. for (auto& argument : arguments) {
  376. Core::Stream::AllocatingMemoryStream stream;
  377. Wasm::Printer { stream }.print(argument);
  378. if (first)
  379. first = false;
  380. else
  381. argument_builder.append(", "sv);
  382. auto buffer = ByteBuffer::create_uninitialized(stream.used_buffer_size()).release_value_but_fixme_should_propagate_errors();
  383. stream.read_entire_buffer(buffer).release_value_but_fixme_should_propagate_errors();
  384. argument_builder.append(StringView(buffer).trim_whitespace());
  385. }
  386. dbgln("[wasm runtime] Stub function {} was called with the following arguments: {}", name, argument_builder.to_deprecated_string());
  387. Vector<Wasm::Value> result;
  388. result.ensure_capacity(type.results().size());
  389. for (auto& result_type : type.results())
  390. result.append(Wasm::Value { result_type, 0ull });
  391. return Wasm::Result { move(result) };
  392. },
  393. type));
  394. exports.set(entry, *address);
  395. }
  396. linker.link(exports);
  397. }
  398. auto link_result = linker.finish();
  399. if (link_result.is_error()) {
  400. warnln("Linking main module failed");
  401. print_link_error(link_result.error());
  402. return 1;
  403. }
  404. auto result = machine.instantiate(parse_result.value(), link_result.release_value());
  405. if (result.is_error()) {
  406. warnln("Module instantiation failed: {}", result.error().error);
  407. return 1;
  408. }
  409. auto module_instance = result.release_value();
  410. auto launch_repl = [&] {
  411. Wasm::Configuration config { machine.store() };
  412. Wasm::Expression expression { {} };
  413. config.set_frame(Wasm::Frame {
  414. *module_instance,
  415. Vector<Wasm::Value> {},
  416. expression,
  417. 0,
  418. });
  419. Wasm::Instruction instr { Wasm::Instructions::nop };
  420. Wasm::InstructionPointer ip { 0 };
  421. g_continue = false;
  422. pre_interpret_hook(config, ip, instr);
  423. };
  424. auto print_func = [&](auto const& address) {
  425. Wasm::FunctionInstance* fn = machine.store().get(address);
  426. g_stdout->write(DeprecatedString::formatted("- Function with address {}, ptr = {}\n", address.value(), fn).bytes()).release_value_but_fixme_should_propagate_errors();
  427. if (fn) {
  428. g_stdout->write(DeprecatedString::formatted(" wasm function? {}\n", fn->has<Wasm::WasmFunction>()).bytes()).release_value_but_fixme_should_propagate_errors();
  429. fn->visit(
  430. [&](Wasm::WasmFunction const& func) {
  431. Wasm::Printer printer { *g_stdout, 3 };
  432. g_stdout->write(" type:\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
  433. printer.print(func.type());
  434. g_stdout->write(" code:\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
  435. printer.print(func.code());
  436. },
  437. [](Wasm::HostFunction const&) {});
  438. }
  439. };
  440. if (print) {
  441. // Now, let's dump the functions!
  442. for (auto& address : module_instance->functions()) {
  443. print_func(address);
  444. }
  445. }
  446. if (shell_mode) {
  447. launch_repl();
  448. return 0;
  449. }
  450. if (!exported_function_to_execute.is_empty()) {
  451. Optional<Wasm::FunctionAddress> run_address;
  452. Vector<Wasm::Value> values;
  453. for (auto& entry : module_instance->exports()) {
  454. if (entry.name() == exported_function_to_execute) {
  455. if (auto addr = entry.value().get_pointer<Wasm::FunctionAddress>())
  456. run_address = *addr;
  457. }
  458. }
  459. if (!run_address.has_value()) {
  460. warnln("No such exported function, sorry :(");
  461. return 1;
  462. }
  463. auto instance = machine.store().get(*run_address);
  464. VERIFY(instance);
  465. if (instance->has<Wasm::HostFunction>()) {
  466. warnln("Exported function is a host function, cannot run that yet");
  467. return 1;
  468. }
  469. for (auto& param : instance->get<Wasm::WasmFunction>().type().parameters()) {
  470. if (values_to_push.is_empty())
  471. values.append(Wasm::Value { param, 0ull });
  472. else
  473. values.append(Wasm::Value { param, values_to_push.take_last() });
  474. }
  475. if (print) {
  476. outln("Executing ");
  477. print_func(*run_address);
  478. outln();
  479. }
  480. auto result = machine.invoke(g_interpreter, run_address.value(), move(values));
  481. if (debug)
  482. launch_repl();
  483. if (result.is_trap()) {
  484. warnln("Execution trapped: {}", result.trap().reason);
  485. } else {
  486. if (!result.values().is_empty())
  487. warnln("Returned:");
  488. for (auto& value : result.values()) {
  489. g_stdout->write(" -> "sv.bytes()).release_value_but_fixme_should_propagate_errors();
  490. g_printer->print(value);
  491. }
  492. }
  493. }
  494. }
  495. return 0;
  496. }