wasm.cpp 20 KB

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