wasm32-dis.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. /* Opcode printing code for the WebAssembly target
  2. Copyright (C) 2017 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. /* Type names for blocks and signatures. */
  27. #define BLOCK_TYPE_NONE 0x40
  28. #define BLOCK_TYPE_I32 0x7f
  29. #define BLOCK_TYPE_I64 0x7e
  30. #define BLOCK_TYPE_F32 0x7d
  31. #define BLOCK_TYPE_F64 0x7c
  32. enum wasm_class
  33. {
  34. wasm_typed,
  35. wasm_special,
  36. wasm_break,
  37. wasm_break_if,
  38. wasm_break_table,
  39. wasm_return,
  40. wasm_call,
  41. wasm_call_import,
  42. wasm_call_indirect,
  43. wasm_get_local,
  44. wasm_set_local,
  45. wasm_tee_local,
  46. wasm_drop,
  47. wasm_constant_i32,
  48. wasm_constant_i64,
  49. wasm_constant_f32,
  50. wasm_constant_f64,
  51. wasm_unary,
  52. wasm_binary,
  53. wasm_conv,
  54. wasm_load,
  55. wasm_store,
  56. wasm_select,
  57. wasm_relational,
  58. wasm_eqz,
  59. wasm_current_memory,
  60. wasm_grow_memory,
  61. wasm_signature
  62. };
  63. struct wasm32_private_data
  64. {
  65. bfd_boolean print_registers;
  66. bfd_boolean print_well_known_globals;
  67. /* Limit valid symbols to those with a given prefix. */
  68. const char *section_prefix;
  69. };
  70. typedef struct
  71. {
  72. const char *name;
  73. const char *description;
  74. } wasm32_options_t;
  75. static const wasm32_options_t options[] =
  76. {
  77. { "registers", N_("Disassemble \"register\" names") },
  78. { "globals", N_("Name well-known globals") },
  79. };
  80. #define WASM_OPCODE(opcode, name, intype, outtype, clas, signedness) \
  81. { name, wasm_ ## clas, opcode },
  82. struct wasm32_opcode_s
  83. {
  84. const char *name;
  85. enum wasm_class clas;
  86. unsigned char opcode;
  87. } wasm32_opcodes[] =
  88. {
  89. #include "opcode/wasm.h"
  90. { NULL, 0, 0 }
  91. };
  92. /* Parse the disassembler options in OPTS and initialize INFO. */
  93. static void
  94. parse_wasm32_disassembler_options (struct disassemble_info *info,
  95. const char *opts)
  96. {
  97. struct wasm32_private_data *private = info->private_data;
  98. while (opts != NULL)
  99. {
  100. if (CONST_STRNEQ (opts, "registers"))
  101. private->print_registers = TRUE;
  102. else if (CONST_STRNEQ (opts, "globals"))
  103. private->print_well_known_globals = TRUE;
  104. opts = strchr (opts, ',');
  105. if (opts)
  106. opts++;
  107. }
  108. }
  109. /* Check whether SYM is valid. Special-case absolute symbols, which
  110. are unhelpful to print, and arguments to a "call" insn, which we
  111. want to be in a section matching a given prefix. */
  112. static bfd_boolean
  113. wasm32_symbol_is_valid (asymbol *sym,
  114. struct disassemble_info *info)
  115. {
  116. struct wasm32_private_data *private_data = info->private_data;
  117. if (sym == NULL)
  118. return FALSE;
  119. if (strcmp(sym->section->name, "*ABS*") == 0)
  120. return FALSE;
  121. if (private_data && private_data->section_prefix != NULL
  122. && strncmp (sym->section->name, private_data->section_prefix,
  123. strlen (private_data->section_prefix)))
  124. return FALSE;
  125. return TRUE;
  126. }
  127. /* Initialize the disassembler structures for INFO. */
  128. void
  129. disassemble_init_wasm32 (struct disassemble_info *info)
  130. {
  131. if (info->private_data == NULL)
  132. {
  133. static struct wasm32_private_data private;
  134. private.print_registers = FALSE;
  135. private.print_well_known_globals = FALSE;
  136. private.section_prefix = NULL;
  137. info->private_data = &private;
  138. }
  139. if (info->disassembler_options)
  140. {
  141. parse_wasm32_disassembler_options (info, info->disassembler_options);
  142. info->disassembler_options = NULL;
  143. }
  144. info->symbol_is_valid = wasm32_symbol_is_valid;
  145. }
  146. /* Read an LEB128-encoded integer from INFO at address PC, reading one
  147. byte at a time. Set ERROR_RETURN if no complete integer could be
  148. read, LENGTH_RETURN to the number oof bytes read (including bytes
  149. in incomplete numbers). SIGN means interpret the number as
  150. SLEB128. Unfortunately, this is a duplicate of wasm-module.c's
  151. wasm_read_leb128 (). */
  152. static uint64_t
  153. wasm_read_leb128 (bfd_vma pc,
  154. struct disassemble_info * info,
  155. bfd_boolean * error_return,
  156. unsigned int * length_return,
  157. bfd_boolean sign)
  158. {
  159. uint64_t result = 0;
  160. unsigned int num_read = 0;
  161. unsigned int shift = 0;
  162. unsigned char byte = 0;
  163. bfd_boolean success = FALSE;
  164. while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
  165. {
  166. num_read++;
  167. result |= ((bfd_vma) (byte & 0x7f)) << shift;
  168. shift += 7;
  169. if ((byte & 0x80) == 0)
  170. {
  171. success = TRUE;
  172. break;
  173. }
  174. }
  175. if (length_return != NULL)
  176. *length_return = num_read;
  177. if (error_return != NULL)
  178. *error_return = ! success;
  179. if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
  180. result |= -((uint64_t) 1 << shift);
  181. return result;
  182. }
  183. /* Read a 32-bit IEEE float from PC using INFO, convert it to a host
  184. double, and store it at VALUE. */
  185. static int
  186. read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
  187. {
  188. bfd_byte buf[4];
  189. if (info->read_memory_func (pc, buf, sizeof (buf), info))
  190. return -1;
  191. floatformat_to_double (&floatformat_ieee_single_little, buf,
  192. value);
  193. return sizeof (buf);
  194. }
  195. /* Read a 64-bit IEEE float from PC using INFO, convert it to a host
  196. double, and store it at VALUE. */
  197. static int
  198. read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
  199. {
  200. bfd_byte buf[8];
  201. if (info->read_memory_func (pc, buf, sizeof (buf), info))
  202. return -1;
  203. floatformat_to_double (&floatformat_ieee_double_little, buf,
  204. value);
  205. return sizeof (buf);
  206. }
  207. /* Main disassembly routine. Disassemble insn at PC using INFO. */
  208. int
  209. print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
  210. {
  211. unsigned char opcode;
  212. struct wasm32_opcode_s *op;
  213. bfd_byte buffer[16];
  214. void *stream = info->stream;
  215. fprintf_ftype prin = info->fprintf_func;
  216. struct wasm32_private_data *private_data = info->private_data;
  217. long long constant = 0;
  218. double fconstant = 0.0;
  219. long flags = 0;
  220. long offset = 0;
  221. long depth = 0;
  222. long index = 0;
  223. long target_count = 0;
  224. long block_type = 0;
  225. int len = 1;
  226. int ret = 0;
  227. unsigned int bytes_read = 0;
  228. int i;
  229. const char *locals[] =
  230. {
  231. "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
  232. "$rp", "$fp", "$sp",
  233. "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
  234. "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
  235. "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
  236. };
  237. int nlocals = ARRAY_SIZE (locals);
  238. const char *globals[] =
  239. {
  240. "$got", "$plt", "$gpo"
  241. };
  242. int nglobals = ARRAY_SIZE (globals);
  243. bfd_boolean error = FALSE;
  244. if (info->read_memory_func (pc, buffer, 1, info))
  245. return -1;
  246. opcode = buffer[0];
  247. for (op = wasm32_opcodes; op->name; op++)
  248. if (op->opcode == opcode)
  249. break;
  250. if (!op->name)
  251. {
  252. prin (stream, "\t.byte 0x%02x\n", buffer[0]);
  253. return 1;
  254. }
  255. else
  256. {
  257. len = 1;
  258. prin (stream, "\t");
  259. prin (stream, "%s", op->name);
  260. if (op->clas == wasm_typed)
  261. {
  262. block_type = wasm_read_leb128
  263. (pc + len, info, &error, &bytes_read, FALSE);
  264. if (error)
  265. return -1;
  266. len += bytes_read;
  267. switch (block_type)
  268. {
  269. case BLOCK_TYPE_NONE:
  270. prin (stream, "[]");
  271. break;
  272. case BLOCK_TYPE_I32:
  273. prin (stream, "[i]");
  274. break;
  275. case BLOCK_TYPE_I64:
  276. prin (stream, "[l]");
  277. break;
  278. case BLOCK_TYPE_F32:
  279. prin (stream, "[f]");
  280. break;
  281. case BLOCK_TYPE_F64:
  282. prin (stream, "[d]");
  283. break;
  284. }
  285. }
  286. switch (op->clas)
  287. {
  288. case wasm_special:
  289. case wasm_eqz:
  290. case wasm_binary:
  291. case wasm_unary:
  292. case wasm_conv:
  293. case wasm_relational:
  294. case wasm_drop:
  295. case wasm_signature:
  296. case wasm_call_import:
  297. case wasm_typed:
  298. case wasm_select:
  299. break;
  300. case wasm_break_table:
  301. target_count = wasm_read_leb128
  302. (pc + len, info, &error, &bytes_read, FALSE);
  303. if (error)
  304. return -1;
  305. len += bytes_read;
  306. prin (stream, " %ld", target_count);
  307. for (i = 0; i < target_count + 1; i++)
  308. {
  309. long target = 0;
  310. target = wasm_read_leb128
  311. (pc + len, info, &error, &bytes_read, FALSE);
  312. if (error)
  313. return -1;
  314. len += bytes_read;
  315. prin (stream, " %ld", target);
  316. }
  317. break;
  318. case wasm_break:
  319. case wasm_break_if:
  320. depth = wasm_read_leb128
  321. (pc + len, info, &error, &bytes_read, FALSE);
  322. if (error)
  323. return -1;
  324. len += bytes_read;
  325. prin (stream, " %ld", depth);
  326. break;
  327. case wasm_return:
  328. break;
  329. case wasm_constant_i32:
  330. case wasm_constant_i64:
  331. constant = wasm_read_leb128
  332. (pc + len, info, &error, &bytes_read, TRUE);
  333. if (error)
  334. return -1;
  335. len += bytes_read;
  336. prin (stream, " %lld", constant);
  337. break;
  338. case wasm_constant_f32:
  339. /* This appears to be the best we can do, even though we're
  340. using host doubles for WebAssembly floats. */
  341. ret = read_f32 (&fconstant, pc + len, info);
  342. if (ret < 0)
  343. return -1;
  344. len += ret;
  345. prin (stream, " %.9g", fconstant);
  346. break;
  347. case wasm_constant_f64:
  348. ret = read_f64 (&fconstant, pc + len, info);
  349. if (ret < 0)
  350. return -1;
  351. len += ret;
  352. prin (stream, " %.17g", fconstant);
  353. break;
  354. case wasm_call:
  355. index = wasm_read_leb128
  356. (pc + len, info, &error, &bytes_read, FALSE);
  357. if (error)
  358. return -1;
  359. len += bytes_read;
  360. prin (stream, " ");
  361. private_data->section_prefix = ".space.function_index";
  362. (*info->print_address_func) ((bfd_vma) index, info);
  363. private_data->section_prefix = NULL;
  364. break;
  365. case wasm_call_indirect:
  366. constant = wasm_read_leb128
  367. (pc + len, info, &error, &bytes_read, FALSE);
  368. if (error)
  369. return -1;
  370. len += bytes_read;
  371. prin (stream, " %lld", constant);
  372. constant = wasm_read_leb128
  373. (pc + len, info, &error, &bytes_read, FALSE);
  374. if (error)
  375. return -1;
  376. len += bytes_read;
  377. prin (stream, " %lld", constant);
  378. break;
  379. case wasm_get_local:
  380. case wasm_set_local:
  381. case wasm_tee_local:
  382. constant = wasm_read_leb128
  383. (pc + len, info, &error, &bytes_read, FALSE);
  384. if (error)
  385. return -1;
  386. len += bytes_read;
  387. prin (stream, " %lld", constant);
  388. if (strcmp (op->name + 4, "local") == 0)
  389. {
  390. if (private_data->print_registers
  391. && constant >= 0 && constant < nlocals)
  392. prin (stream, " <%s>", locals[constant]);
  393. }
  394. else
  395. {
  396. if (private_data->print_well_known_globals
  397. && constant >= 0 && constant < nglobals)
  398. prin (stream, " <%s>", globals[constant]);
  399. }
  400. break;
  401. case wasm_grow_memory:
  402. case wasm_current_memory:
  403. constant = wasm_read_leb128
  404. (pc + len, info, &error, &bytes_read, FALSE);
  405. if (error)
  406. return -1;
  407. len += bytes_read;
  408. prin (stream, " %lld", constant);
  409. break;
  410. case wasm_load:
  411. case wasm_store:
  412. flags = wasm_read_leb128
  413. (pc + len, info, &error, &bytes_read, FALSE);
  414. if (error)
  415. return -1;
  416. len += bytes_read;
  417. offset = wasm_read_leb128
  418. (pc + len, info, &error, &bytes_read, FALSE);
  419. if (error)
  420. return -1;
  421. len += bytes_read;
  422. prin (stream, " a=%ld %ld", flags, offset);
  423. }
  424. }
  425. return len;
  426. }
  427. /* Print valid disassembler options to STREAM. */
  428. void
  429. print_wasm32_disassembler_options (FILE *stream)
  430. {
  431. unsigned int i, max_len = 0;
  432. fprintf (stream, _("\
  433. The following WebAssembly-specific disassembler options are supported for use\n\
  434. with the -M switch:\n"));
  435. for (i = 0; i < ARRAY_SIZE (options); i++)
  436. {
  437. unsigned int len = strlen (options[i].name);
  438. if (max_len < len)
  439. max_len = len;
  440. }
  441. for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
  442. fprintf (stream, " %s%*c %s\n",
  443. options[i].name,
  444. (int)(max_len - strlen (options[i].name)), ' ',
  445. _(options[i].description));
  446. }