wasm.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  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/Interpreter.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 void print_buffer(ReadonlyBytes buffer, int split)
  22. {
  23. for (size_t i = 0; i < buffer.size(); ++i) {
  24. if (split > 0) {
  25. if (i % split == 0 && i) {
  26. printf(" ");
  27. for (size_t j = i - split; j < i; ++j) {
  28. auto ch = buffer[j];
  29. printf("%c", ch >= 32 && ch <= 127 ? ch : '.'); // silly hack
  30. }
  31. puts("");
  32. }
  33. }
  34. printf("%02x ", buffer[i]);
  35. }
  36. puts("");
  37. }
  38. static void sigint_handler(int)
  39. {
  40. if (!g_continue) {
  41. signal(SIGINT, old_signal);
  42. kill(getpid(), SIGINT);
  43. }
  44. g_continue = false;
  45. }
  46. static bool post_interpret_hook(Wasm::Configuration&, Wasm::InstructionPointer&, const Wasm::Instruction&, const Wasm::Interpreter& interpreter)
  47. {
  48. if (interpreter.did_trap()) {
  49. g_continue = false;
  50. const_cast<Wasm::Interpreter&>(interpreter).clear_trap();
  51. }
  52. return true;
  53. }
  54. static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPointer& ip, const Wasm::Instruction& instr)
  55. {
  56. static bool always_print_stack = false;
  57. static bool always_print_instruction = false;
  58. if (always_print_stack)
  59. config.dump_stack();
  60. if (always_print_instruction) {
  61. g_stdout.write(String::formatted("{:0>4} ", ip.value()).bytes());
  62. g_printer.print(instr);
  63. }
  64. if (g_continue)
  65. return true;
  66. g_stdout.write(String::formatted("{:0>4} ", ip.value()).bytes());
  67. g_printer.print(instr);
  68. String last_command = "";
  69. for (;;) {
  70. auto result = g_line_editor->get_line("> ");
  71. if (result.is_error()) {
  72. return false;
  73. }
  74. auto str = result.release_value();
  75. g_line_editor->add_to_history(str);
  76. if (str.is_empty())
  77. str = last_command;
  78. else
  79. last_command = str;
  80. auto args = str.split_view(' ');
  81. if (args.is_empty())
  82. continue;
  83. auto& cmd = args[0];
  84. if (cmd.is_one_of("s", "step", "next")) {
  85. return true;
  86. }
  87. if (cmd.is_one_of("p", "print")) {
  88. if (args.size() < 2) {
  89. warnln("Print what?");
  90. continue;
  91. }
  92. auto& what = args[1];
  93. if (what.is_one_of("s", "stack")) {
  94. config.dump_stack();
  95. continue;
  96. }
  97. if (what.is_one_of("m", "mem", "memory")) {
  98. if (args.size() < 3) {
  99. warnln("print what memory?");
  100. continue;
  101. }
  102. auto value = args[2].to_uint<u64>();
  103. if (!value.has_value()) {
  104. warnln("invalid memory index {}", args[2]);
  105. continue;
  106. }
  107. auto mem = config.store().get(Wasm::MemoryAddress(value.value()));
  108. if (!mem) {
  109. warnln("invalid memory index {} (not found)", args[2]);
  110. continue;
  111. }
  112. print_buffer(mem->data(), 32);
  113. continue;
  114. }
  115. if (what.is_one_of("i", "instr", "instruction")) {
  116. g_printer.print(instr);
  117. continue;
  118. }
  119. if (what.is_one_of("f", "func", "function")) {
  120. if (args.size() < 3) {
  121. warnln("print what function?");
  122. continue;
  123. }
  124. auto value = args[2].to_uint<u64>();
  125. if (!value.has_value()) {
  126. warnln("invalid function index {}", args[2]);
  127. continue;
  128. }
  129. auto fn = config.store().get(Wasm::FunctionAddress(value.value()));
  130. if (!fn) {
  131. warnln("invalid function index {} (not found)", args[2]);
  132. continue;
  133. }
  134. if (auto* fn_value = fn->get_pointer<Wasm::HostFunction>()) {
  135. warnln("Host function at {:p}", &fn_value->function());
  136. continue;
  137. }
  138. if (auto* fn_value = fn->get_pointer<Wasm::WasmFunction>()) {
  139. g_printer.print(fn_value->code());
  140. continue;
  141. }
  142. }
  143. }
  144. if (cmd == "call"sv) {
  145. if (args.size() < 2) {
  146. warnln("call what?");
  147. continue;
  148. }
  149. Optional<Wasm::FunctionAddress> address;
  150. auto index = args[1].to_uint<u64>();
  151. if (index.has_value()) {
  152. address = config.frame()->module().functions()[index.value()];
  153. } else {
  154. auto& name = args[1];
  155. for (auto& export_ : config.frame()->module().exports()) {
  156. if (export_.name() == name) {
  157. if (auto addr = export_.value().get_pointer<Wasm::FunctionAddress>()) {
  158. address = *addr;
  159. break;
  160. }
  161. }
  162. }
  163. }
  164. if (!address.has_value()) {
  165. failed_to_find:;
  166. warnln("Could not find a function {}", args[1]);
  167. continue;
  168. }
  169. auto fn = config.store().get(*address);
  170. if (!fn)
  171. goto failed_to_find;
  172. auto type = fn->visit([&](auto& value) { return value.type(); });
  173. if (type.parameters().size() + 2 != args.size()) {
  174. warnln("Expected {} arguments for call, but found only {}", type.parameters().size(), args.size() - 2);
  175. continue;
  176. }
  177. Vector<u64> values_to_push;
  178. Vector<Wasm::Value> values;
  179. for (size_t index = 2; index < args.size(); ++index)
  180. values_to_push.append(args[index].to_uint().value_or(0));
  181. for (auto& param : type.parameters())
  182. values.append(Wasm::Value { param, values_to_push.take_last() });
  183. auto result = config.call(*address, move(values));
  184. if (result.is_trap())
  185. warnln("Execution trapped!");
  186. if (!result.values().is_empty())
  187. warnln("Returned:");
  188. for (auto& value : result.values()) {
  189. auto str = value.value().visit(
  190. [&](const auto& value) {
  191. if constexpr (requires { value.value(); })
  192. return String::formatted(" -> addr{} ", value.value());
  193. else
  194. return String::formatted(" -> {} ", value);
  195. });
  196. g_stdout.write(str.bytes());
  197. g_printer.print(value.type());
  198. }
  199. continue;
  200. }
  201. if (cmd.is_one_of("set", "unset")) {
  202. auto value = !cmd.starts_with('u');
  203. if (args.size() < 3) {
  204. warnln("(un)set what (to what)?");
  205. continue;
  206. }
  207. if (args[1] == "print"sv) {
  208. if (args[2] == "stack"sv)
  209. always_print_stack = value;
  210. else if (args[2].is_one_of("instr", "instruction"))
  211. always_print_instruction = value;
  212. else
  213. warnln("Unknown print category '{}'", args[2]);
  214. continue;
  215. }
  216. warnln("Unknown set category '{}'", args[1]);
  217. continue;
  218. }
  219. if (cmd.is_one_of("c", "continue")) {
  220. g_continue = true;
  221. return true;
  222. }
  223. warnln("Command not understood: {}", cmd);
  224. }
  225. }
  226. static Optional<Wasm::Module> parse(const StringView& filename)
  227. {
  228. auto result = Core::File::open(filename, Core::OpenMode::ReadOnly);
  229. if (result.is_error()) {
  230. warnln("Failed to open {}: {}", filename, result.error());
  231. return {};
  232. }
  233. auto stream = Core::InputFileStream(result.release_value());
  234. auto parse_result = Wasm::Module::parse(stream);
  235. if (parse_result.is_error()) {
  236. warnln("Something went wrong, either the file is invalid, or there's a bug with LibWasm!");
  237. warnln("The parse error was {}", Wasm::parse_error_to_string(parse_result.error()));
  238. return {};
  239. }
  240. return parse_result.release_value();
  241. }
  242. static void print_link_error(const Wasm::LinkError& error)
  243. {
  244. for (const auto& missing : error.missing_imports)
  245. warnln("Missing import '{}'", missing);
  246. }
  247. int main(int argc, char* argv[])
  248. {
  249. const char* filename = nullptr;
  250. bool print = false;
  251. bool attempt_instantiate = false;
  252. bool debug = false;
  253. String exported_function_to_execute;
  254. Vector<u64> values_to_push;
  255. Vector<String> modules_to_link_in;
  256. Core::ArgsParser parser;
  257. parser.add_positional_argument(filename, "File name to parse", "file");
  258. parser.add_option(debug, "Open a debugger", "debug", 'd');
  259. parser.add_option(print, "Print the parsed module", "print", 'p');
  260. parser.add_option(attempt_instantiate, "Attempt to instantiate the module", "instantiate", 'i');
  261. parser.add_option(exported_function_to_execute, "Attempt to execute the named exported function from the module (implies -i)", "execute", 'e', "name");
  262. parser.add_option(Core::ArgsParser::Option {
  263. .requires_argument = true,
  264. .help_string = "Extra modules to link with, use to resolve imports",
  265. .long_name = "link",
  266. .short_name = 'l',
  267. .value_name = "file",
  268. .accept_value = [&](const char* str) {
  269. if (auto v = StringView { str }; !v.is_empty()) {
  270. modules_to_link_in.append(v);
  271. return true;
  272. }
  273. return false;
  274. },
  275. });
  276. parser.add_option(Core::ArgsParser::Option {
  277. .requires_argument = true,
  278. .help_string = "Supply arguments to the function (default=0) (expects u64, casts to required type)",
  279. .long_name = "arg",
  280. .short_name = 0,
  281. .value_name = "u64",
  282. .accept_value = [&](const char* str) -> bool {
  283. if (auto v = StringView { str }.to_uint<u64>(); v.has_value()) {
  284. values_to_push.append(v.value());
  285. return true;
  286. }
  287. return false;
  288. },
  289. });
  290. parser.parse(argc, argv);
  291. if (debug && exported_function_to_execute.is_empty()) {
  292. warnln("Debug what? (pass -e fn)");
  293. return 1;
  294. }
  295. if (debug) {
  296. old_signal = signal(SIGINT, sigint_handler);
  297. }
  298. if (!exported_function_to_execute.is_empty())
  299. attempt_instantiate = true;
  300. auto parse_result = parse(filename);
  301. if (print && !attempt_instantiate) {
  302. auto out_stream = Core::OutputFileStream::standard_output();
  303. Wasm::Printer printer(out_stream);
  304. printer.print(parse_result.value());
  305. }
  306. if (attempt_instantiate) {
  307. Wasm::AbstractMachine machine;
  308. Core::EventLoop main_loop;
  309. if (debug) {
  310. g_line_editor = Line::Editor::construct();
  311. machine.pre_interpret_hook = pre_interpret_hook;
  312. machine.post_interpret_hook = post_interpret_hook;
  313. }
  314. // First, resolve the linked modules
  315. NonnullOwnPtrVector<Wasm::ModuleInstance> linked_instances;
  316. Vector<Wasm::Module> linked_modules;
  317. for (auto& name : modules_to_link_in) {
  318. auto parse_result = parse(name);
  319. if (!parse_result.has_value()) {
  320. warnln("Failed to parse linked module '{}'", name);
  321. return 1;
  322. }
  323. linked_modules.append(parse_result.release_value());
  324. Wasm::Linker linker { linked_modules.last() };
  325. for (auto& instance : linked_instances)
  326. linker.link(instance);
  327. auto link_result = linker.finish();
  328. if (link_result.is_error()) {
  329. warnln("Linking imported module '{}' failed", name);
  330. print_link_error(link_result.error());
  331. return 1;
  332. }
  333. auto instantiation_result = machine.instantiate(linked_modules.last(), link_result.release_value());
  334. if (instantiation_result.is_error()) {
  335. warnln("Instantiation of imported module '{}' failed: {}", name, instantiation_result.error().error);
  336. return 1;
  337. }
  338. linked_instances.append(instantiation_result.release_value());
  339. }
  340. Wasm::Linker linker { parse_result.value() };
  341. for (auto& instance : linked_instances)
  342. linker.link(instance);
  343. auto link_result = linker.finish();
  344. if (link_result.is_error()) {
  345. warnln("Linking main module failed");
  346. print_link_error(link_result.error());
  347. return 1;
  348. }
  349. auto result = machine.instantiate(parse_result.value(), link_result.release_value());
  350. if (result.is_error()) {
  351. warnln("Module instantiation failed: {}", result.error().error);
  352. return 1;
  353. }
  354. auto module_instance = result.release_value();
  355. auto stream = Core::OutputFileStream::standard_output();
  356. auto print_func = [&](const auto& address) {
  357. Wasm::FunctionInstance* fn = machine.store().get(address);
  358. stream.write(String::formatted("- Function with address {}, ptr = {}\n", address.value(), fn).bytes());
  359. if (fn) {
  360. stream.write(String::formatted(" wasm function? {}\n", fn->has<Wasm::WasmFunction>()).bytes());
  361. fn->visit(
  362. [&](const Wasm::WasmFunction& func) {
  363. Wasm::Printer printer { stream, 3 };
  364. stream.write(" type:\n"sv.bytes());
  365. printer.print(func.type());
  366. stream.write(" code:\n"sv.bytes());
  367. printer.print(func.code());
  368. },
  369. [](const Wasm::HostFunction&) {});
  370. }
  371. };
  372. if (print) {
  373. // Now, let's dump the functions!
  374. for (auto& address : module_instance->functions()) {
  375. print_func(address);
  376. }
  377. }
  378. if (!exported_function_to_execute.is_empty()) {
  379. Optional<Wasm::FunctionAddress> run_address;
  380. Vector<Wasm::Value> values;
  381. for (auto& entry : module_instance->exports()) {
  382. if (entry.name() == exported_function_to_execute) {
  383. if (auto addr = entry.value().get_pointer<Wasm::FunctionAddress>())
  384. run_address = *addr;
  385. }
  386. }
  387. if (!run_address.has_value()) {
  388. warnln("No such exported function, sorry :(");
  389. return 1;
  390. }
  391. auto instance = machine.store().get(*run_address);
  392. VERIFY(instance);
  393. if (instance->has<Wasm::HostFunction>()) {
  394. warnln("Exported function is a host function, cannot run that yet");
  395. return 1;
  396. }
  397. for (auto& param : instance->get<Wasm::WasmFunction>().type().parameters()) {
  398. if (values_to_push.is_empty())
  399. values.append(Wasm::Value { param, 0ull });
  400. else
  401. values.append(Wasm::Value { param, values_to_push.take_last() });
  402. }
  403. if (print) {
  404. outln("Executing ");
  405. print_func(*run_address);
  406. outln();
  407. }
  408. auto result = machine.invoke(run_address.value(), move(values));
  409. if (debug) {
  410. Wasm::Configuration config { machine.store() };
  411. auto frame = make<Wasm::Frame>(
  412. *module_instance,
  413. Vector<Wasm::Value> {},
  414. instance->get<Wasm::WasmFunction>().code().body(),
  415. 1);
  416. config.set_frame(move(frame));
  417. const Wasm::Instruction instr { Wasm::Instructions::nop };
  418. Wasm::InstructionPointer ip { 0 };
  419. g_continue = false;
  420. pre_interpret_hook(config, ip, instr);
  421. }
  422. if (result.is_trap())
  423. warnln("Execution trapped!");
  424. if (!result.values().is_empty())
  425. warnln("Returned:");
  426. for (auto& value : result.values()) {
  427. value.value().visit(
  428. [&](const auto& value) {
  429. if constexpr (requires { value.value(); })
  430. out(" -> addr{} ", value.value());
  431. else
  432. out(" -> {} ", value);
  433. });
  434. Wasm::Printer printer { stream };
  435. printer.print(value.type());
  436. }
  437. }
  438. }
  439. return 0;
  440. }