wasm32-dis.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. /* Opcode printing code for the WebAssembly target
  2. Copyright (C) 2017-2022 Free Software Foundation, Inc.
  3. This file is part of libopcodes.
  4. This library is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. It is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  11. License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
  15. MA 02110-1301, USA. */
  16. #include "sysdep.h"
  17. #include "disassemble.h"
  18. #include "opintl.h"
  19. #include "safe-ctype.h"
  20. #include "floatformat.h"
  21. #include "libiberty.h"
  22. #include "elf-bfd.h"
  23. #include "elf/internal.h"
  24. #include "elf/wasm32.h"
  25. #include <stdint.h>
  26. #include <limits.h>
  27. #ifndef CHAR_BIT
  28. #define CHAR_BIT 8
  29. #endif
  30. /* Type names for blocks and signatures. */
  31. #define BLOCK_TYPE_NONE 0x40
  32. #define BLOCK_TYPE_I32 0x7f
  33. #define BLOCK_TYPE_I64 0x7e
  34. #define BLOCK_TYPE_F32 0x7d
  35. #define BLOCK_TYPE_F64 0x7c
  36. enum wasm_class
  37. {
  38. wasm_typed,
  39. wasm_special,
  40. wasm_break,
  41. wasm_break_if,
  42. wasm_break_table,
  43. wasm_return,
  44. wasm_call,
  45. wasm_call_import,
  46. wasm_call_indirect,
  47. wasm_get_local,
  48. wasm_set_local,
  49. wasm_tee_local,
  50. wasm_drop,
  51. wasm_constant_i32,
  52. wasm_constant_i64,
  53. wasm_constant_f32,
  54. wasm_constant_f64,
  55. wasm_unary,
  56. wasm_binary,
  57. wasm_conv,
  58. wasm_load,
  59. wasm_store,
  60. wasm_select,
  61. wasm_relational,
  62. wasm_eqz,
  63. wasm_current_memory,
  64. wasm_grow_memory,
  65. wasm_signature
  66. };
  67. struct wasm32_private_data
  68. {
  69. bool print_registers;
  70. bool print_well_known_globals;
  71. /* Limit valid symbols to those with a given prefix. */
  72. const char *section_prefix;
  73. };
  74. typedef struct
  75. {
  76. const char *name;
  77. const char *description;
  78. } wasm32_options_t;
  79. static const wasm32_options_t options[] =
  80. {
  81. { "registers", N_("Disassemble \"register\" names") },
  82. { "globals", N_("Name well-known globals") },
  83. };
  84. #define WASM_OPCODE(opcode, name, intype, outtype, clas, signedness) \
  85. { name, wasm_ ## clas, opcode },
  86. struct wasm32_opcode_s
  87. {
  88. const char *name;
  89. enum wasm_class clas;
  90. unsigned char opcode;
  91. } wasm32_opcodes[] =
  92. {
  93. #include "opcode/wasm.h"
  94. { NULL, 0, 0 }
  95. };
  96. /* Parse the disassembler options in OPTS and initialize INFO. */
  97. static void
  98. parse_wasm32_disassembler_options (struct disassemble_info *info,
  99. const char *opts)
  100. {
  101. struct wasm32_private_data *private = info->private_data;
  102. while (opts != NULL)
  103. {
  104. if (startswith (opts, "registers"))
  105. private->print_registers = true;
  106. else if (startswith (opts, "globals"))
  107. private->print_well_known_globals = true;
  108. opts = strchr (opts, ',');
  109. if (opts)
  110. opts++;
  111. }
  112. }
  113. /* Check whether SYM is valid. Special-case absolute symbols, which
  114. are unhelpful to print, and arguments to a "call" insn, which we
  115. want to be in a section matching a given prefix. */
  116. static bool
  117. wasm32_symbol_is_valid (asymbol *sym,
  118. struct disassemble_info *info)
  119. {
  120. struct wasm32_private_data *private_data = info->private_data;
  121. if (sym == NULL)
  122. return false;
  123. if (strcmp(sym->section->name, "*ABS*") == 0)
  124. return false;
  125. if (private_data && private_data->section_prefix != NULL
  126. && strncmp (sym->section->name, private_data->section_prefix,
  127. strlen (private_data->section_prefix)))
  128. return false;
  129. return true;
  130. }
  131. /* Initialize the disassembler structures for INFO. */
  132. void
  133. disassemble_init_wasm32 (struct disassemble_info *info)
  134. {
  135. if (info->private_data == NULL)
  136. {
  137. static struct wasm32_private_data private;
  138. private.print_registers = false;
  139. private.print_well_known_globals = false;
  140. private.section_prefix = NULL;
  141. info->private_data = &private;
  142. }
  143. if (info->disassembler_options)
  144. {
  145. parse_wasm32_disassembler_options (info, info->disassembler_options);
  146. info->disassembler_options = NULL;
  147. }
  148. info->symbol_is_valid = wasm32_symbol_is_valid;
  149. }
  150. /* Read an LEB128-encoded integer from INFO at address PC, reading one
  151. byte at a time. Set ERROR_RETURN if no complete integer could be
  152. read, LENGTH_RETURN to the number oof bytes read (including bytes
  153. in incomplete numbers). SIGN means interpret the number as
  154. SLEB128. Unfortunately, this is a duplicate of wasm-module.c's
  155. wasm_read_leb128 (). */
  156. static uint64_t
  157. wasm_read_leb128 (bfd_vma pc,
  158. struct disassemble_info *info,
  159. bool *error_return,
  160. unsigned int *length_return,
  161. bool sign)
  162. {
  163. uint64_t result = 0;
  164. unsigned int num_read = 0;
  165. unsigned int shift = 0;
  166. unsigned char byte = 0;
  167. unsigned char lost, mask;
  168. int status = 1;
  169. while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
  170. {
  171. num_read++;
  172. if (shift < CHAR_BIT * sizeof (result))
  173. {
  174. result |= ((uint64_t) (byte & 0x7f)) << shift;
  175. /* These bits overflowed. */
  176. lost = byte ^ (result >> shift);
  177. /* And this is the mask of possible overflow bits. */
  178. mask = 0x7f ^ ((uint64_t) 0x7f << shift >> shift);
  179. shift += 7;
  180. }
  181. else
  182. {
  183. lost = byte;
  184. mask = 0x7f;
  185. }
  186. if ((lost & mask) != (sign && (int64_t) result < 0 ? mask : 0))
  187. status |= 2;
  188. if ((byte & 0x80) == 0)
  189. {
  190. status &= ~1;
  191. if (sign && shift < CHAR_BIT * sizeof (result) && (byte & 0x40))
  192. result |= -((uint64_t) 1 << shift);
  193. break;
  194. }
  195. }
  196. if (length_return != NULL)
  197. *length_return = num_read;
  198. if (error_return != NULL)
  199. *error_return = status != 0;
  200. return result;
  201. }
  202. /* Read a 32-bit IEEE float from PC using INFO, convert it to a host
  203. double, and store it at VALUE. */
  204. static int
  205. read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
  206. {
  207. bfd_byte buf[4];
  208. if (info->read_memory_func (pc, buf, sizeof (buf), info))
  209. return -1;
  210. floatformat_to_double (&floatformat_ieee_single_little, buf,
  211. value);
  212. return sizeof (buf);
  213. }
  214. /* Read a 64-bit IEEE float from PC using INFO, convert it to a host
  215. double, and store it at VALUE. */
  216. static int
  217. read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
  218. {
  219. bfd_byte buf[8];
  220. if (info->read_memory_func (pc, buf, sizeof (buf), info))
  221. return -1;
  222. floatformat_to_double (&floatformat_ieee_double_little, buf,
  223. value);
  224. return sizeof (buf);
  225. }
  226. /* Main disassembly routine. Disassemble insn at PC using INFO. */
  227. int
  228. print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
  229. {
  230. unsigned char opcode;
  231. struct wasm32_opcode_s *op;
  232. bfd_byte buffer[16];
  233. void *stream = info->stream;
  234. fprintf_ftype prin = info->fprintf_func;
  235. struct wasm32_private_data *private_data = info->private_data;
  236. uint64_t val;
  237. int len;
  238. unsigned int bytes_read;
  239. bool error;
  240. if (info->read_memory_func (pc, buffer, 1, info))
  241. return -1;
  242. opcode = buffer[0];
  243. for (op = wasm32_opcodes; op->name; op++)
  244. if (op->opcode == opcode)
  245. break;
  246. if (!op->name)
  247. {
  248. prin (stream, "\t.byte 0x%02x\n", buffer[0]);
  249. return 1;
  250. }
  251. len = 1;
  252. prin (stream, "\t");
  253. prin (stream, "%s", op->name);
  254. if (op->clas == wasm_typed)
  255. {
  256. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, false);
  257. if (error)
  258. return -1;
  259. len += bytes_read;
  260. switch (val)
  261. {
  262. case BLOCK_TYPE_NONE:
  263. prin (stream, "[]");
  264. break;
  265. case BLOCK_TYPE_I32:
  266. prin (stream, "[i]");
  267. break;
  268. case BLOCK_TYPE_I64:
  269. prin (stream, "[l]");
  270. break;
  271. case BLOCK_TYPE_F32:
  272. prin (stream, "[f]");
  273. break;
  274. case BLOCK_TYPE_F64:
  275. prin (stream, "[d]");
  276. break;
  277. default:
  278. return -1;
  279. }
  280. }
  281. switch (op->clas)
  282. {
  283. case wasm_special:
  284. case wasm_eqz:
  285. case wasm_binary:
  286. case wasm_unary:
  287. case wasm_conv:
  288. case wasm_relational:
  289. case wasm_drop:
  290. case wasm_signature:
  291. case wasm_call_import:
  292. case wasm_typed:
  293. case wasm_select:
  294. break;
  295. case wasm_break_table:
  296. {
  297. uint32_t target_count, i;
  298. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  299. false);
  300. target_count = val;
  301. if (error || target_count != val || target_count == (uint32_t) -1)
  302. return -1;
  303. len += bytes_read;
  304. prin (stream, " %u", target_count);
  305. for (i = 0; i < target_count + 1; i++)
  306. {
  307. uint32_t target;
  308. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  309. false);
  310. target = val;
  311. if (error || target != val)
  312. return -1;
  313. len += bytes_read;
  314. prin (stream, " %u", target);
  315. }
  316. }
  317. break;
  318. case wasm_break:
  319. case wasm_break_if:
  320. {
  321. uint32_t depth;
  322. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  323. false);
  324. depth = val;
  325. if (error || depth != val)
  326. return -1;
  327. len += bytes_read;
  328. prin (stream, " %u", depth);
  329. }
  330. break;
  331. case wasm_return:
  332. break;
  333. case wasm_constant_i32:
  334. case wasm_constant_i64:
  335. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, true);
  336. if (error)
  337. return -1;
  338. len += bytes_read;
  339. prin (stream, " %" PRId64, val);
  340. break;
  341. case wasm_constant_f32:
  342. {
  343. double fconstant;
  344. int ret;
  345. /* This appears to be the best we can do, even though we're
  346. using host doubles for WebAssembly floats. */
  347. ret = read_f32 (&fconstant, pc + len, info);
  348. if (ret < 0)
  349. return -1;
  350. len += ret;
  351. prin (stream, " %.9g", fconstant);
  352. }
  353. break;
  354. case wasm_constant_f64:
  355. {
  356. double fconstant;
  357. int ret;
  358. ret = read_f64 (&fconstant, pc + len, info);
  359. if (ret < 0)
  360. return -1;
  361. len += ret;
  362. prin (stream, " %.17g", fconstant);
  363. }
  364. break;
  365. case wasm_call:
  366. {
  367. uint32_t function_index;
  368. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  369. false);
  370. function_index = val;
  371. if (error || function_index != val)
  372. return -1;
  373. len += bytes_read;
  374. prin (stream, " ");
  375. private_data->section_prefix = ".space.function_index";
  376. (*info->print_address_func) ((bfd_vma) function_index, info);
  377. private_data->section_prefix = NULL;
  378. }
  379. break;
  380. case wasm_call_indirect:
  381. {
  382. uint32_t type_index, xtra_index;
  383. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  384. false);
  385. type_index = val;
  386. if (error || type_index != val)
  387. return -1;
  388. len += bytes_read;
  389. prin (stream, " %u", type_index);
  390. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  391. false);
  392. xtra_index = val;
  393. if (error || xtra_index != val)
  394. return -1;
  395. len += bytes_read;
  396. prin (stream, " %u", xtra_index);
  397. }
  398. break;
  399. case wasm_get_local:
  400. case wasm_set_local:
  401. case wasm_tee_local:
  402. {
  403. uint32_t local_index;
  404. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  405. false);
  406. local_index = val;
  407. if (error || local_index != val)
  408. return -1;
  409. len += bytes_read;
  410. prin (stream, " %u", local_index);
  411. if (strcmp (op->name + 4, "local") == 0)
  412. {
  413. static const char *locals[] =
  414. {
  415. "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
  416. "$rp", "$fp", "$sp",
  417. "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
  418. "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
  419. "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
  420. };
  421. if (private_data->print_registers
  422. && local_index < ARRAY_SIZE (locals))
  423. prin (stream, " <%s>", locals[local_index]);
  424. }
  425. else
  426. {
  427. static const char *globals[] =
  428. {
  429. "$got", "$plt", "$gpo"
  430. };
  431. if (private_data->print_well_known_globals
  432. && local_index < ARRAY_SIZE (globals))
  433. prin (stream, " <%s>", globals[local_index]);
  434. }
  435. }
  436. break;
  437. case wasm_grow_memory:
  438. case wasm_current_memory:
  439. {
  440. uint32_t reserved_size;
  441. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  442. false);
  443. reserved_size = val;
  444. if (error || reserved_size != val)
  445. return -1;
  446. len += bytes_read;
  447. prin (stream, " %u", reserved_size);
  448. }
  449. break;
  450. case wasm_load:
  451. case wasm_store:
  452. {
  453. uint32_t flags, offset;
  454. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  455. false);
  456. flags = val;
  457. if (error || flags != val)
  458. return -1;
  459. len += bytes_read;
  460. val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
  461. false);
  462. offset = val;
  463. if (error || offset != val)
  464. return -1;
  465. len += bytes_read;
  466. prin (stream, " a=%u %u", flags, offset);
  467. }
  468. break;
  469. }
  470. return len;
  471. }
  472. /* Print valid disassembler options to STREAM. */
  473. void
  474. print_wasm32_disassembler_options (FILE *stream)
  475. {
  476. unsigned int i, max_len = 0;
  477. fprintf (stream, _("\
  478. The following WebAssembly-specific disassembler options are supported for use\n\
  479. with the -M switch:\n"));
  480. for (i = 0; i < ARRAY_SIZE (options); i++)
  481. {
  482. unsigned int len = strlen (options[i].name);
  483. if (max_len < len)
  484. max_len = len;
  485. }
  486. for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
  487. fprintf (stream, " %s%*c %s\n",
  488. options[i].name,
  489. (int)(max_len - strlen (options[i].name)), ' ',
  490. _(options[i].description));
  491. }