1 /* Disassembler for RISC-V.
2 Copyright (C) 2019 Red Hat, Inc.
3 This file is part of elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2019.
5
6 This file is free software; you can redistribute it and/or modify
7 it under the terms of either
8
9 * the GNU Lesser General Public License as published by the Free
10 Software Foundation; either version 3 of the License, or (at
11 your option) any later version
12
13 or
14
15 * the GNU General Public License as published by the Free
16 Software Foundation; either version 2 of the License, or (at
17 your option) any later version
18
19 or both in parallel, as here.
20
21 elfutils is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
25
26 You should have received copies of the GNU General Public License and
27 the GNU Lesser General Public License along with this program. If
28 not, see <http://www.gnu.org/licenses/>. */
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <inttypes.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "../libebl/libeblP.h"
43
44 #define MACHINE_ENCODING __LITTLE_ENDIAN
45 #include "memory-access.h"
46
47
48 #define ADD_CHAR(ch) \
49 do { \
50 if (unlikely (bufcnt == bufsize)) \
51 goto enomem; \
52 buf[bufcnt++] = (ch); \
53 } while (0)
54
55 #define ADD_STRING(str) \
56 do { \
57 const char *_str0 = (str); \
58 size_t _len0 = strlen (_str0); \
59 ADD_NSTRING (_str0, _len0); \
60 } while (0)
61
62 #define ADD_NSTRING(str, len) \
63 do { \
64 const char *_str = (str); \
65 size_t _len = (len); \
66 if (unlikely (bufcnt + _len > bufsize)) \
67 goto enomem; \
68 memcpy (buf + bufcnt, _str, _len); \
69 bufcnt += _len; \
70 } while (0)
71
72
73 static const char *regnames[32] =
74 {
75 "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
76 "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
77 "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
78 "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
79 };
80 #define REG(nr) ((char *) regnames[nr])
81 #define REGP(nr) REG (8 + (nr))
82
83
84 static const char *fregnames[32] =
85 {
86 "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7",
87 "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5",
88 "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
89 "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"
90 };
91 #define FREG(nr) ((char *) fregnames[nr])
92 #define FREGP(nr) FREG (8 + (nr))
93
94
95 struct known_csrs
96 {
97 uint16_t nr;
98 const char *name;
99 };
100
compare_csr(const void * a,const void * b)101 static int compare_csr (const void *a, const void *b)
102 {
103 const struct known_csrs *ka = (const struct known_csrs *) a;
104 const struct known_csrs *kb = (const struct known_csrs *) b;
105 if (ka->nr < kb->nr)
106 return -1;
107 return ka->nr == kb->nr ? 0 : 1;
108 }
109
110
111 int
riscv_disasm(Ebl * ebl,const uint8_t ** startp,const uint8_t * end,GElf_Addr addr,const char * fmt,DisasmOutputCB_t outcb,DisasmGetSymCB_t symcb,void * outcbarg,void * symcbarg)112 riscv_disasm (Ebl *ebl,
113 const uint8_t **startp, const uint8_t *end, GElf_Addr addr,
114 const char *fmt, DisasmOutputCB_t outcb,
115 DisasmGetSymCB_t symcb __attribute__((unused)),
116 void *outcbarg, void *symcbarg __attribute__((unused)))
117 {
118 const char *const save_fmt = fmt;
119
120 #define BUFSIZE 512
121 char initbuf[BUFSIZE];
122 size_t bufcnt;
123 size_t bufsize = BUFSIZE;
124 char *buf = initbuf;
125
126 int retval = 0;
127 while (1)
128 {
129 const uint8_t *data = *startp;
130 assert (data <= end);
131 if (data + 2 > end)
132 {
133 if (data != end)
134 retval = -1;
135 break;
136 }
137 uint16_t first = read_2ubyte_unaligned (data);
138
139 // Determine length.
140 size_t length;
141 if ((first & 0x3) != 0x3)
142 length = 2;
143 else if ((first & 0x1f) != 0x1f)
144 length = 4;
145 else if ((first & 0x3f) != 0x3f)
146 length = 6;
147 else if ((first & 0x7f) != 0x7f)
148 length = 8;
149 else
150 {
151 uint16_t nnn = (first >> 12) & 0x7;
152 if (nnn != 0x7)
153 length = 10 + 2 * nnn;
154 else
155 // This is invalid as of the RISC-V spec on 2019-06-21.
156 // The instruction is at least 192 bits in size so use
157 // this minimum size.
158 length = 24;
159 }
160 if (data + length > end)
161 {
162 retval = -1;
163 break;
164 }
165
166 char *mne = NULL;
167 char mnebuf[32];
168 char *op[5] = { NULL, NULL, NULL, NULL, NULL };
169 char immbuf[32];
170 size_t len;
171 char *strp = NULL;
172 char addrbuf[32];
173 bufcnt = 0;
174 int64_t opaddr;
175 if (length == 2)
176 {
177 size_t idx = (first >> 13) * 3 + (first & 0x3);
178 switch (idx)
179 {
180 uint16_t rd;
181 uint16_t rs1;
182 uint16_t rs2;
183
184 case 0:
185 if ((first & 0x1fe0) != 0)
186 {
187 mne = "addi";
188 op[0] = REGP ((first & 0x1c) >> 2);
189 op[1] = REG (2);
190 opaddr = (((first >> 1) & 0x3c0)
191 | ((first >> 7) & 0x30)
192 | ((first >> 2) & 0x8)
193 | ((first >> 4) & 0x4));
194 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64, opaddr);
195 op[2] = addrbuf;
196 }
197 else if (first == 0)
198 mne = "unimp";
199 break;
200 case 1:
201 rs1 = (first >> 7) & 0x1f;
202 int16_t nzimm = ((0 - ((first >> 7) & 0x20))
203 | ((first >> 2) & 0x1f));
204 if (rs1 == 0)
205 mne = nzimm == 0 ? "nop" : "c.nop";
206 else
207 {
208 mne = nzimm == 0 ? "c.addi" : "addi";
209 op[0] = op[1] = REG (rs1);
210 snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, nzimm);
211 op[2] = addrbuf;
212 }
213 break;
214 case 2:
215 rs1 = (first >> 7) & 0x1f;
216 op[0] = op[1] = REG (rs1);
217 opaddr = ((first >> 7) & 0x20) | ((first >> 2) & 0x1f);
218 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
219 op[2] = addrbuf;
220 mne = rs1 == 0 ? "c.slli" : "slli";
221 break;
222 case 3:
223 op[0] = FREGP ((first >> 2) & 0x7);
224 opaddr = ((first << 1) & 0xc0) | ((first >> 7) & 0x38);
225 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)",
226 opaddr, REGP ((first >> 7) & 0x7));
227 op[1] = addrbuf;
228 mne = "fld";
229 break;
230 case 4:
231 if (ebl->class == ELFCLASS32)
232 {
233 mne = "jal";
234 opaddr = (((first << 3) & 0x20) | ((first >> 2) & 0xe)
235 | ((first << 1) & 0x80) | ((first >> 1) | 0x40)
236 | ((first << 2) & 0x400) | (first & 0xb00)
237 | ((first >> 6) & 0x10));
238 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
239 op[0] = addrbuf;
240 }
241 else
242 {
243 int32_t imm = (((UINT32_C (0) - ((first >> 12) & 0x1)) << 5)
244 | ((first >> 2) & 0x1f));
245 uint16_t reg = (first >> 7) & 0x1f;
246 if (reg == 0)
247 {
248 // Reserved
249 len = snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, first);
250 strp = addrbuf;
251 }
252 else
253 {
254 if (imm == 0)
255 mne = "sext.w";
256 else
257 {
258 mne = "addiw";
259 snprintf (addrbuf, sizeof (addrbuf), "%" PRId32, imm);
260 op[2] = addrbuf;
261 }
262 op[0] = op[1] = REG (reg);
263 }
264 }
265 break;
266 case 5:
267 op[0] = FREG ((first >> 7) & 0x1f);
268 opaddr = ((first << 4) & 0x1c0) | ((first >> 7) & 0x20) | ((first >> 2) & 0x18);
269 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", opaddr, REG (2));
270 op[1] = addrbuf;
271 mne = "fld";
272 break;
273 case 6:
274 case 18:
275 mne = idx == 6 ? "lw" : "sw";
276 op[0] = REGP ((first >> 2) & 0x7);
277 opaddr = (((first >> 7) & 0x38) | ((first << 1) & 0x40)
278 | ((first >> 4) & 0x4));
279 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
280 opaddr, REGP ((first >> 7) & 0x7));
281 op[1] = addrbuf;
282 break;
283 case 7:
284 mne = (first & 0xf80) == 0 ? "c.li" : "li";
285 op[0] = REG((first >> 7) & 0x1f);
286 snprintf (addrbuf, sizeof (addrbuf), "%" PRId16,
287 (UINT16_C (0) - ((first >> 7) & 0x20)) | ((first >> 2) & 0x1f));
288 op[1] = addrbuf;
289 break;
290 case 8:
291 rd = ((first >> 7) & 0x1f);
292 if (rd == 0)
293 {
294 len = snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, first);
295 strp = addrbuf;
296 }
297 else
298 {
299 uint16_t uimm = (((first << 4) & 0xc0)
300 | ((first >> 7) & 0x20)
301 | ((first >> 2) & 0x1c));
302 mne = "lw";
303 op[0] = REG (rd);
304 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu16 "(%s)", uimm, REG (2));
305 op[1] = addrbuf;
306 }
307 break;
308 case 9:
309 if (ebl->class == ELFCLASS32)
310 {
311 mne = "flw";
312 op[0] = FREGP ((first >> 2) & 0x7);
313 opaddr = (((first << 1) & 0x40)
314 | ((first >> 7) & 0x38)
315 | ((first >> 4) & 0x4));
316 }
317 else
318 {
319 mne = "ld";
320 op[0] = REGP ((first >> 2) & 0x7);
321 opaddr = ((first >> 7) & 0x38) | ((first << 1) & 0xc0);
322 }
323 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
324 opaddr, REGP ((first >> 7) & 0x7));
325 op[1] = addrbuf;
326 break;
327 case 10:
328 if ((first & 0xf80) == (2 << 7))
329 {
330 mne = "addi";
331 op[0] = op[1] = REG (2);
332 opaddr = (((first >> 2) & 0x10) | ((first << 3) & 0x20)
333 | ((first << 1) & 0x40) | ((first << 4) & 0x180)
334 | ((UINT64_C (0) - ((first >> 12) & 0x1)) << 9));
335 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
336 op[2] = addrbuf;
337 }
338 else
339 {
340 mne = "lui";
341 op[0] = REG((first & 0xf80) >> 7);
342 opaddr = (((UINT64_C (0) - ((first >> 12) & 0x1)) & ~0x1f)
343 | ((first >> 2) & 0x1f));
344 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr & 0xfffff);
345 op[1] = addrbuf;
346 }
347 break;
348 case 11:
349 if (ebl->class == ELFCLASS32)
350 {
351 mne = "flw";
352 op[0] = FREG ((first >> 7) & 0x1f);
353 opaddr = (((first << 4) & 0xc0)
354 | ((first >> 7) & 0x20)
355 | ((first >> 2) & 0x1c));
356 }
357 else
358 {
359 mne = "ld";
360 op[0] = REG ((first >> 7) & 0x1f);
361 opaddr = (((first << 4) & 0x1c0)
362 | ((first >> 7) & 0x20)
363 | ((first >> 2) & 0x18));
364 }
365 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
366 op[1] = addrbuf;
367 break;
368 case 13:
369 if ((first & 0xc00) != 0xc00)
370 {
371 int16_t imm = ((first >> 7) & 0x20) | ((first >> 2) & 0x1f);
372 if ((first & 0xc00) == 0x800)
373 {
374 imm |= 0 - (imm & 0x20);
375 mne = "andi";
376 snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, imm);
377 }
378 else
379 {
380 if (ebl->class != ELFCLASS32 || imm < 32)
381 {
382 mne = (first & 0x400) ? "srai" : "srli";
383 if (imm == 0)
384 {
385 strcpy (stpcpy (mnebuf, "c."), mne);
386 mne = mnebuf;
387 }
388 }
389 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, imm);
390 }
391 op[2] = addrbuf;
392 }
393 else
394 {
395 op[2] = REGP ((first >> 2) & 0x7);
396 static const char *const arithmne[8] =
397 {
398 "sub", "xor", "or", "and", "subw", "addw", NULL, NULL
399 };
400 mne = (char *) arithmne[((first >> 10) & 0x4) | ((first >> 5) & 0x3)];
401 }
402 op[0] = op[1] = REGP ((first >> 7) & 0x7);
403 break;
404 case 14:
405 rs1 = (first >> 7) & 0x1f;
406 rs2 = (first >> 2) & 0x1f;
407 op[0] = REG (rs1);
408 if ((first & 0x1000) == 0)
409 {
410 if (rs2 == 0)
411 {
412 op[1] = NULL;
413 if (rs1 == 1)
414 {
415 mne = "ret";
416 op[0] = NULL;
417 }
418 else
419 mne = "jr";
420 }
421 else
422 {
423 mne = rs1 != 0 ? "mv" : "c.mv";
424 op[1] = REG (rs2);
425 }
426 }
427 else
428 {
429 if (rs2 == 0)
430 {
431 if (rs1 == 0)
432 {
433 mne = "ebreak";
434 op[0] = op[1] = NULL;
435 }
436 else
437 mne = "jalr";
438 }
439 else
440 {
441 mne = rs1 != 0 ? "add" : "c.add";
442 op[2] = REG (rs2);
443 op[1] = op[0];
444 }
445 }
446 break;
447 case 15:
448 op[0] = FREGP ((first >> 2) & 0x7);
449 opaddr = ((first << 1) & 0xc0) | ((first >> 7) & 0x38);
450 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)",
451 opaddr, REGP ((first >> 7) & 0x7));
452 op[1] = addrbuf;
453 mne = "fsd";
454 break;
455 case 16:
456 opaddr = (((UINT64_C (0) - ((first >> 12) & 0x1)) << 11)
457 | ((first << 2) & 0x400)
458 | ((first >> 1) & 0x300)
459 | ((first << 1) & 0x80)
460 | ((first >> 1) & 0x40)
461 | ((first << 3) & 0x20)
462 | ((first >> 7) & 0x10)
463 | ((first >> 2) & 0xe));
464 mne = "j";
465 // TODO translate address
466 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, addr + opaddr);
467 op[0] = addrbuf;
468 break;
469 case 17:
470 op[0] = FREG ((first >> 2) & 0x1f);
471 opaddr = ((first >> 1) & 0x1c0) | ((first >> 7) & 0x38);
472 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", opaddr, REG (2));
473 op[1] = addrbuf;
474 mne = "fsd";
475 break;
476 case 19:
477 case 22:
478 mne = idx == 19 ? "beqz" : "bnez";
479 op[0] = REG (8 + ((first >> 7) & 0x7));
480 opaddr = addr + (((UINT64_C (0) - ((first >> 12) & 0x1)) & ~0xff)
481 | ((first << 1) & 0xc0) | ((first << 3) & 0x20)
482 | ((first >> 7) & 0x18) | ((first >> 2) & 0x6));
483 // TODO translate address
484 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
485 op[1] = addrbuf;
486 break;
487 case 20:
488 op[0] = REG ((first >> 2) & 0x1f);
489 opaddr = ((first >> 1) & 0xc0) | ((first >> 7) & 0x3c);
490 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
491 op[1] = addrbuf;
492 mne = "sw";
493 break;
494 case 21:
495 if (idx == 18 || ebl->class == ELFCLASS32)
496 {
497 mne = "fsw";
498 op[0] = FREGP ((first >> 2) & 0x7);
499 opaddr = (((first >> 7) & 0x38) | ((first << 1) & 0x40)
500 | ((first >> 4) & 0x4));
501 }
502 else
503 {
504 mne = "sd";
505 op[0] = REGP ((first >> 2) & 0x7);
506 opaddr = ((first >> 7) & 0x38) | ((first << 1) & 0xc0);
507 }
508 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
509 opaddr, REGP ((first >> 7) & 0x7));
510 op[1] = addrbuf;
511 break;
512 case 23:
513 if (idx == 18 || ebl->class == ELFCLASS32)
514 {
515 mne = "fsw";
516 op[0] = FREG ((first & 0x7c) >> 2);
517 opaddr = ((first & 0x1e00) >> 7) | ((first & 0x180) >> 1);
518 }
519 else
520 {
521 mne = "sd";
522 op[0] = REG ((first & 0x7c) >> 2);
523 opaddr = ((first & 0x1c00) >> 7) | ((first & 0x380) >> 1);
524 }
525 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
526 op[1] = addrbuf;
527 break;
528 default:
529 break;
530 }
531
532 if (strp == NULL && mne == NULL)
533 {
534 len = snprintf (immbuf, sizeof (immbuf), "0x%04" PRIx16, first);
535 strp = immbuf;
536 }
537 }
538 else if (length == 4)
539 {
540 uint32_t word = read_4ubyte_unaligned (data);
541 size_t idx = (word >> 2) & 0x1f;
542
543 switch (idx)
544 {
545 static const char widthchar[4] = { 's', 'd', '\0', 'q' };
546 static const char intwidthchar[4] = { 'w', 'd', '\0', 'q' };
547 static const char *const rndmode[8] = { "rne", "rtz", "rdn", "rup", "rmm", "???", "???", "dyn" };
548 uint32_t rd;
549 uint32_t rs1;
550 uint32_t rs2;
551 uint32_t rs3;
552 uint32_t func;
553
554 case 0x00:
555 case 0x01:
556 // LOAD and LOAD-FP
557 rd = (word >> 7) & 0x1f;
558 op[0] = idx == 0x00 ? REG (rd) : FREG (rd);
559 opaddr = ((int32_t) word) >> 20;
560 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
561 opaddr, REG ((word >> 15) & 0x1f));
562 op[1] = addrbuf;
563 func = (word >> 12) & 0x7;
564 static const char *const loadmne[8] =
565 {
566 "lb", "lh", "lw", "ld", "lbu", "lhu", "lwu", NULL
567 };
568 static const char *const floadmne[8] =
569 {
570 NULL, NULL, "flw", "fld", "flq", NULL, NULL, NULL
571 };
572 mne = (char *) (idx == 0x00 ? loadmne[func] : floadmne[func]);
573 break;
574 case 0x03:
575 // MISC-MEM
576 rd = (word >> 7) & 0x1f;
577 rs1 = (word >> 15) & 0x1f;
578 func = (word >> 12) & 0x7;
579
580 if (word == 0x8330000f)
581 mne = "fence.tso";
582 else if (word == 0x0000100f)
583 mne = "fence.i";
584 else if (func == 0 && rd == 0 && rs1 == 0 && (word & 0xf0000000) == 0)
585 {
586 static const char *const order[16] =
587 {
588 "unknown", "w", "r", "rw", "o", "ow", "or", "orw",
589 "i", "iw", "ir", "irw", "io", "iow", "ior", "iorw"
590 };
591 uint32_t pred = (word >> 20) & 0xf;
592 uint32_t succ = (word >> 24) & 0xf;
593 if (pred != 0xf || succ != 0xf)
594 {
595 op[0] = (char *) order[succ];
596 op[1] = (char *) order[pred];
597 }
598 mne = "fence";
599 }
600 break;
601 case 0x04:
602 case 0x06:
603 // OP-IMM and OP-IMM32
604 rd = (word >> 7) & 0x1f;
605 op[0] = REG (rd);
606 rs1 = (word >> 15) & 0x1f;
607 op[1] = REG (rs1);
608 opaddr = ((int32_t) word) >> 20;
609 static const char *const opimmmne[8] =
610 {
611 "addi", NULL, "slti", "sltiu", "xori", NULL, "ori", "andi"
612 };
613 func = (word >> 12) & 0x7;
614 mne = (char *) opimmmne[func];
615 if (mne == NULL)
616 {
617 const uint64_t shiftmask = ebl->class == ELFCLASS32 ? 0x1f : 0x3f;
618 if (func == 0x1 && (opaddr & ~shiftmask) == 0)
619 mne = "slli";
620 else if (func == 0x5 && (opaddr & ~shiftmask) == 0)
621 mne = "srli";
622 else if (func == 0x5 && (opaddr & ~shiftmask) == 0x400)
623 mne = "srai";
624 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr & shiftmask);
625 op[2] = addrbuf;
626 }
627 else if (func == 0x0 && (rd != 0 || idx == 0x06) && rs1 == 0 && rd != 0)
628 {
629 mne = "li";
630 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
631 op[1] = addrbuf;
632 }
633 else if (func == 0x00 && opaddr == 0)
634 {
635 if (idx == 0x06)
636 mne ="sext.";
637 else if (rd == 0)
638 {
639 mne = "nop";
640 op[0] = op[1] = NULL;
641 }
642 else
643 mne = "mv";
644 }
645 else if (func == 0x3 && opaddr == 1)
646 mne = "seqz";
647 else if (func == 0x4 && opaddr == -1)
648 {
649 mne = "not";
650 op[2] = NULL;
651 }
652 else
653 {
654 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
655 op[2] = addrbuf;
656
657 if (func == 0x0 && rs1 == 0 && rd != 0)
658 {
659 op[1] = op[2];
660 op[2] = NULL;
661 mne = "li";
662 }
663 }
664 if (mne != NULL && idx == 0x06)
665 {
666 mne = strcpy (mnebuf, mne);
667 strcat (mnebuf, "w");
668 }
669 break;
670 case 0x05:
671 case 0x0d:
672 // LUI and AUIPC
673 mne = idx == 0x05 ? "auipc" : "lui";
674 op[0] = REG ((word >> 7) & 0x1f);
675 opaddr = word >> 12;
676 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
677 op[1] = addrbuf;
678 break;
679 case 0x08:
680 case 0x09:
681 // STORE and STORE-FP
682 rs2 = (word >> 20) & 0x1f;
683 op[0] = idx == 0x08 ? REG (rs2) : FREG (rs2);
684 opaddr = ((((int64_t) ((int32_t) word) >> 20)) & ~0x1f) | ((word >> 7) & 0x1f);
685 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
686 opaddr, REG ((word >> 15) & 0x1f));
687 op[1] = addrbuf;
688 func = (word >> 12) & 0x7;
689 static const char *const storemne[8] =
690 {
691 "sb", "sh", "sw", "sd", NULL, NULL, NULL, NULL
692 };
693 static const char *const fstoremne[8] =
694 {
695 NULL, NULL, "fsw", "fsd", "fsq", NULL, NULL, NULL
696 };
697 mne = (char *) (idx == 0x08 ? storemne[func] : fstoremne[func]);
698 break;
699 case 0x0b:
700 // AMO
701 op[0] = REG ((word >> 7) & 0x1f);
702 rs1 = (word >> 15) & 0x1f;
703 rs2 = (word >> 20) & 0x1f;
704 snprintf (addrbuf, sizeof (addrbuf), "(%s)", REG (rs1));
705 op[2] = addrbuf;
706 size_t width = (word >> 12) & 0x7;
707 func = word >> 27;
708 static const char *const amomne[32] =
709 {
710 "amoadd", "amoswap", "lr", "sc", "amoxor", NULL, NULL, NULL,
711 "amoor", NULL, NULL, NULL, "amoand", NULL, NULL, NULL,
712 "amomin", NULL, NULL, NULL, "amomax", NULL, NULL, NULL,
713 "amominu", NULL, NULL, NULL, "amomaxu", NULL, NULL, NULL
714 };
715 if (amomne[func] != NULL && width >= 2 && width <= 3
716 && (func != 0x02 || rs2 == 0))
717 {
718 if (func == 0x02)
719 {
720 op[1] = op[2];
721 op[2] = NULL;
722 }
723 else
724 op[1] = REG (rs2);
725
726 char *cp = stpcpy (mnebuf, amomne[func]);
727 *cp++ = '.';
728 *cp++ = " wd "[width];
729 assert (cp[-1] != ' ');
730 static const char *const aqrlstr[4] =
731 {
732 "", ".rl", ".aq", ".aqrl"
733 };
734 strcpy (cp, aqrlstr[(word >> 25) & 0x3]);
735 mne = mnebuf;
736 }
737 break;
738 case 0x0c:
739 case 0x0e:
740 // OP and OP-32
741 if ((word & 0xbc000000) == 0)
742 {
743 rs1 = (word >> 15) & 0x1f;
744 rs2 = (word >> 20) & 0x1f;
745 op[0] = REG ((word >> 7) & 0x1f);
746 func = ((word >> 21) & 0x10) | ((word >> 27) & 0x8) | ((word >> 12) & 0x7);
747 static const char *const arithmne2[32] =
748 {
749 "add", "sll", "slt", "sltu", "xor", "srl", "or", "and",
750 "sub", NULL, NULL, NULL, NULL, "sra", NULL, NULL,
751 "mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu",
752 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
753 };
754 static const char *const arithmne3[32] =
755 {
756 "addw", "sllw", NULL, NULL, NULL, "srlw", NULL, NULL,
757 "subw", NULL, NULL, NULL, NULL, "sraw", NULL, NULL,
758 "mulw", NULL, NULL, NULL, "divw", "divuw", "remw", "remuw",
759 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
760 };
761 if (func == 8 && rs1 == 0)
762 {
763 mne = idx == 0x0c ? "neg" : "negw";
764 op[1] = REG (rs2);
765 }
766 else if (idx == 0x0c && rs2 == 0 && func == 2)
767 {
768 op[1] = REG (rs1);
769 mne = "sltz";
770 }
771 else if (idx == 0x0c && rs1 == 0 && (func == 2 || func == 3))
772 {
773 op[1] = REG (rs2);
774 mne = func == 2 ? "sgtz" : "snez";
775 }
776 else
777 {
778 mne = (char *) (idx == 0x0c ? arithmne2[func] : arithmne3[func]);
779 op[1] = REG (rs1);
780 op[2] = REG (rs2);
781 }
782 }
783 break;
784 case 0x10:
785 case 0x11:
786 case 0x12:
787 case 0x13:
788 // MADD, MSUB, NMSUB, NMADD
789 if ((word & 0x06000000) != 0x04000000)
790 {
791 rd = (word >> 7) & 0x1f;
792 rs1 = (word >> 15) & 0x1f;
793 rs2 = (word >> 20) & 0x1f;
794 rs3 = (word >> 27) & 0x1f;
795 uint32_t rm = (word >> 12) & 0x7;
796 width = (word >> 25) & 0x3;
797
798 static const char *const fmamne[4] =
799 {
800 "fmadd.", "fmsub.", "fnmsub.", "fnmadd."
801 };
802 char *cp = stpcpy (mnebuf, fmamne[idx & 0x3]);
803 *cp++ = widthchar[width];
804 *cp = '\0';
805 mne = mnebuf;
806 op[0] = FREG (rd);
807 op[1] = FREG (rs1);
808 op[2] = FREG (rs2);
809 op[3] = FREG (rs3);
810 if (rm != 0x7)
811 op[4] = (char *) rndmode[rm];
812 }
813 break;
814 case 0x14:
815 // OP-FP
816 if ((word & 0x06000000) != 0x04000000)
817 {
818 width = (word >> 25) & 0x3;
819 rd = (word >> 7) & 0x1f;
820 rs1 = (word >> 15) & 0x1f;
821 rs2 = (word >> 20) & 0x1f;
822 func = word >> 27;
823 uint32_t rm = (word >> 12) & 0x7;
824 if (func < 4)
825 {
826 static const char *const fpop[4] =
827 {
828 "fadd", "fsub", "fmul", "fdiv"
829 };
830 char *cp = stpcpy (mnebuf, fpop[func]);
831 *cp++ = '.';
832 *cp++ = widthchar[width];
833 *cp = '\0';
834 mne = mnebuf;
835 op[0] = FREG (rd);
836 op[1] = FREG (rs1);
837 op[2] = FREG (rs2);
838 if (rm != 0x7)
839 op[3] = (char *) rndmode[rm];
840 }
841 else if (func == 0x1c && width != 2 && rs2 == 0 && rm <= 1)
842 {
843 char *cp;
844 if (rm == 0)
845 {
846 cp = stpcpy (mnebuf, "fmv.x.");
847 *cp++ = intwidthchar[width];
848 }
849 else
850 {
851 cp = stpcpy (mnebuf, "fclass.");
852 *cp++ = widthchar[width];
853 }
854 *cp = '\0';
855 mne = mnebuf;
856 op[0] = REG (rd);
857 op[1] = FREG (rs1);
858 }
859 else if (func == 0x1e && width != 2 && rs2 == 0 && rm == 0)
860 {
861 char *cp = stpcpy (mnebuf, "fmv.");
862 *cp++ = intwidthchar[width];
863 strcpy (cp, ".x");
864 mne = mnebuf;
865 op[0] = FREG (rd);
866 op[1] = REG (rs1);
867 }
868 else if (func == 0x14)
869 {
870 uint32_t cmpop = (word >> 12) & 0x7;
871 if (cmpop < 3)
872 {
873 static const char *const mnefpcmp[3] =
874 {
875 "fle", "flt", "feq"
876 };
877 char *cp = stpcpy (mnebuf, mnefpcmp[cmpop]);
878 *cp++ = '.';
879 *cp++ = widthchar[width];
880 *cp = '\0';
881 mne = mnebuf;
882 op[0] = REG (rd);
883 op[1] = FREG (rs1);
884 op[2] = FREG (rs2);
885 }
886 }
887 else if (func == 0x04)
888 {
889 uint32_t cmpop = (word >> 12) & 0x7;
890 if (cmpop < 3)
891 {
892 op[0] = FREG (rd);
893 op[1] = FREG (rs1);
894
895 static const char *const mnefpcmp[3] =
896 {
897 "fsgnj.", "fsgnjn.", "fsgnjx."
898 };
899 static const char *const altsignmne[3] =
900 {
901 "fmv.", "fneg.", "fabs."
902 };
903 char *cp = stpcpy (mnebuf, rs1 == rs2 ? altsignmne[cmpop] : mnefpcmp[cmpop]);
904 *cp++ = widthchar[width];
905 *cp = '\0';
906 mne = mnebuf;
907
908 if (rs1 != rs2)
909 op[2] = FREG (rs2);
910 }
911 }
912 else if (func == 0x08 && width != 2 && rs2 <= 3 && rs2 != 2 && rs2 != width)
913 {
914 op[0] = FREG (rd);
915 op[1] = FREG (rs1);
916 char *cp = stpcpy (mnebuf, "fcvt.");
917 *cp++ = widthchar[width];
918 *cp++ = '.';
919 *cp++ = widthchar[rs2];
920 *cp = '\0';
921 mne = mnebuf;
922 }
923 else if ((func & 0x1d) == 0x18 && width != 2 && rs2 < 4)
924 {
925 char *cp = stpcpy (mnebuf, "fcvt.");
926 if (func == 0x18)
927 {
928 *cp++ = rs2 >= 2 ? 'l' : 'w';
929 if ((rs2 & 1) == 1)
930 *cp++ = 'u';
931 *cp++ = '.';
932 *cp++ = widthchar[width];
933 *cp = '\0';
934 op[0] = REG (rd);
935 op[1] = FREG (rs1);
936 }
937 else
938 {
939 *cp++ = widthchar[width];
940 *cp++ = '.';
941 *cp++ = rs2 >= 2 ? 'l' : 'w';
942 if ((rs2 & 1) == 1)
943 *cp++ = 'u';
944 *cp = '\0';
945 op[0] = FREG (rd);
946 op[1] = REG (rs1);
947 }
948 mne = mnebuf;
949 if (rm != 0x7 && (func == 0x18 || width == 0 || rs2 >= 2))
950 op[2] = (char *) rndmode[rm];
951 }
952 else if (func == 0x0b && rs2 == 0)
953 {
954 op[0] = FREG (rd);
955 op[1] = FREG (rs1);
956 char *cp = stpcpy (mnebuf, "fsqrt.");
957 *cp++ = widthchar[width];
958 *cp = '\0';
959 mne = mnebuf;
960 if (rm != 0x7)
961 op[2] = (char *) rndmode[rm];
962 }
963 else if (func == 0x05 && rm < 2)
964 {
965 op[0] = FREG (rd);
966 op[1] = FREG (rs1);
967 op[2] = FREG (rs2);
968 char *cp = stpcpy (mnebuf, rm == 0 ? "fmin." : "fmax.");
969 *cp++ = widthchar[width];
970 *cp = '\0';
971 mne = mnebuf;
972 }
973 else if (func == 0x14 && rm <= 0x2)
974 {
975 op[0] = REG (rd);
976 op[1] = FREG (rs1);
977 op[2] = FREG (rs2);
978 static const char *const fltcmpmne[3] =
979 {
980 "fle.", "flt.", "feq."
981 };
982 char *cp = stpcpy (mnebuf, fltcmpmne[rm]);
983 *cp++ = widthchar[width];
984 *cp = '\0';
985 mne = mnebuf;
986 }
987 }
988 break;
989 case 0x18:
990 // BRANCH
991 rs1 = (word >> 15) & 0x1f;
992 op[0] = REG (rs1);
993 rs2 = (word >> 20) & 0x1f;
994 op[1] = REG (rs2);
995 opaddr = addr + (((UINT64_C (0) - (word >> 31)) << 12)
996 + ((word << 4) & 0x800)
997 + ((word >> 20) & 0x7e0)
998 + ((word >> 7) & 0x1e));
999 // TODO translate address
1000 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
1001 op[2] = addrbuf;
1002 static const char *const branchmne[8] =
1003 {
1004 "beq", "bne", NULL, NULL, "blt", "bge", "bltu", "bgeu"
1005 };
1006 func = (word >> 12) & 0x7;
1007 mne = (char *) branchmne[func];
1008 if (rs1 == 0 && func == 5)
1009 {
1010 op[0] = op[1];
1011 op[1] = op[2];
1012 op[2] = NULL;
1013 mne = "blez";
1014 }
1015 else if (rs1 == 0 && func == 4)
1016 {
1017 op[0] = op[1];
1018 op[1] = op[2];
1019 op[2] = NULL;
1020 mne = "bgtz";
1021 }
1022 else if (rs2 == 0)
1023 {
1024 if (func == 0 || func == 1 || func == 4 || func == 5)
1025 {
1026 op[1] = op[2];
1027 op[2] = NULL;
1028 strcpy (stpcpy (mnebuf, mne), "z");
1029 mne = mnebuf;
1030 }
1031 }
1032 else if (func == 5 || func == 7)
1033 {
1034 // binutils use these opcodes and the reverse parameter order
1035 char *tmp = op[0];
1036 op[0] = op[1];
1037 op[1] = tmp;
1038 mne = func == 5 ? "ble" : "bleu";
1039 }
1040 break;
1041 case 0x19:
1042 // JALR
1043 if ((word & 0x7000) == 0)
1044 {
1045 rd = (word >> 7) & 0x1f;
1046 rs1 = (word >> 15) & 0x1f;
1047 opaddr = (int32_t) word >> 20;
1048 size_t next = 0;
1049 if (rd > 1)
1050 op[next++] = REG (rd);
1051 if (opaddr == 0)
1052 {
1053 if (rs1 != 0 || next == 0)
1054 op[next] = REG (rs1);
1055 }
1056 else
1057 {
1058 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (rs1));
1059 op[next] = addrbuf;
1060 }
1061 mne = rd == 0 ? "jr" : "jalr";
1062 }
1063 break;
1064 case 0x1b:
1065 // JAL
1066 rd = (word >> 7) & 0x1f;
1067 if (rd != 0)
1068 op[0] = REG (rd);
1069 opaddr = addr + ((UINT64_C (0) - ((word >> 11) & 0x100000))
1070 | (word & 0xff000)
1071 | ((word >> 9) & 0x800)
1072 | ((word >> 20) & 0x7fe));
1073 // TODO translate address
1074 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
1075 op[rd != 0] = addrbuf;
1076 mne = rd == 0 ? "j" : "jal";
1077 break;
1078 case 0x1c:
1079 // SYSTEM
1080 rd = (word >> 7) & 0x1f;
1081 rs1 = (word >> 15) & 0x1f;
1082 if (word == 0x00000073)
1083 mne = "ecall";
1084 else if (word == 0x00100073)
1085 mne = "ebreak";
1086 else if (word == 0x00200073)
1087 mne = "uret";
1088 else if (word == 0x10200073)
1089 mne = "sret";
1090 else if (word == 0x30200073)
1091 mne = "mret";
1092 else if (word == 0x10500073)
1093 mne = "wfi";
1094 else if ((word & 0x3000) == 0x2000 && rs1 == 0)
1095 {
1096 uint32_t csr = word >> 20;
1097 if (/* csr >= 0x000 && */ csr <= 0x007)
1098 {
1099 static const char *const unprivrw[4] =
1100 {
1101 NULL, "frflags", "frrm", "frsr",
1102 };
1103 mne = (char *) unprivrw[csr - 0x000];
1104 }
1105 else if (csr >= 0xc00 && csr <= 0xc03)
1106 {
1107 static const char *const unprivrolow[3] =
1108 {
1109 "rdcycle", "rdtime", "rdinstret"
1110 };
1111 mne = (char *) unprivrolow[csr - 0xc00];
1112 }
1113 op[0] = REG ((word >> 7) & 0x1f);
1114 }
1115 else if ((word & 0x3000) == 0x1000 && rd == 0)
1116 {
1117 uint32_t csr = word >> 20;
1118 if (/* csr >= 0x000 && */ csr <= 0x003)
1119 {
1120 static const char *const unprivrs[4] =
1121 {
1122 NULL, "fsflags", "fsrm", "fssr",
1123 };
1124 static const char *const unprivrsi[4] =
1125 {
1126 NULL, "fsflagsi", "fsrmi", NULL
1127 };
1128 mne = (char *) ((word & 0x4000) == 0 ? unprivrs : unprivrsi)[csr - 0x000];
1129
1130 if ((word & 0x4000) == 0)
1131 op[0] = REG ((word >> 15) & 0x1f);
1132 else
1133 {
1134 snprintf (immbuf, sizeof (immbuf), "%" PRIu32, (word >> 15) & 0x1f);
1135 op[0] = immbuf;
1136 }
1137 }
1138 }
1139 if (mne == NULL && (word & 0x3000) != 0)
1140 {
1141 static const char *const mnecsr[8] =
1142 {
1143 NULL, "csrrw", "csrrs", "csrrc",
1144 NULL, "csrrwi", "csrrsi", "csrrci"
1145 };
1146 static const struct known_csrs known[] =
1147 {
1148 // This list must remain sorted by NR.
1149 { 0x000, "ustatus" },
1150 { 0x001, "fflags" },
1151 { 0x002, "fram" },
1152 { 0x003, "fcsr" },
1153 { 0x004, "uie" },
1154 { 0x005, "utvec" },
1155 { 0x040, "uscratch" },
1156 { 0x041, "uepc" },
1157 { 0x042, "ucause" },
1158 { 0x043, "utval" },
1159 { 0x044, "uip" },
1160 { 0x100, "sstatus" },
1161 { 0x102, "sedeleg" },
1162 { 0x103, "sideleg" },
1163 { 0x104, "sie" },
1164 { 0x105, "stvec" },
1165 { 0x106, "scounteren" },
1166 { 0x140, "sscratch" },
1167 { 0x141, "sepc" },
1168 { 0x142, "scause" },
1169 { 0x143, "stval" },
1170 { 0x144, "sip" },
1171 { 0x180, "satp" },
1172 { 0x200, "vsstatus" },
1173 { 0x204, "vsie" },
1174 { 0x205, "vstvec" },
1175 { 0x240, "vsscratch" },
1176 { 0x241, "vsepc" },
1177 { 0x242, "vscause" },
1178 { 0x243, "vstval" },
1179 { 0x244, "vsip" },
1180 { 0x280, "vsatp" },
1181 { 0x600, "hstatus" },
1182 { 0x602, "hedeleg" },
1183 { 0x603, "hideleg" },
1184 { 0x605, "htimedelta" },
1185 { 0x606, "hcounteren" },
1186 { 0x615, "htimedeltah" },
1187 { 0x680, "hgatp" },
1188 { 0xc00, "cycle" },
1189 { 0xc01, "time" },
1190 { 0xc02, "instret" },
1191 { 0xc03, "hpmcounter3" },
1192 { 0xc04, "hpmcounter4" },
1193 { 0xc05, "hpmcounter5" },
1194 { 0xc06, "hpmcounter6" },
1195 { 0xc07, "hpmcounter7" },
1196 { 0xc08, "hpmcounter8" },
1197 { 0xc09, "hpmcounter9" },
1198 { 0xc0a, "hpmcounter10" },
1199 { 0xc0b, "hpmcounter11" },
1200 { 0xc0c, "hpmcounter12" },
1201 { 0xc0d, "hpmcounter13" },
1202 { 0xc0e, "hpmcounter14" },
1203 { 0xc0f, "hpmcounter15" },
1204 { 0xc10, "hpmcounter16" },
1205 { 0xc11, "hpmcounter17" },
1206 { 0xc12, "hpmcounter18" },
1207 { 0xc13, "hpmcounter19" },
1208 { 0xc14, "hpmcounter20" },
1209 { 0xc15, "hpmcounter21" },
1210 { 0xc16, "hpmcounter22" },
1211 { 0xc17, "hpmcounter23" },
1212 { 0xc18, "hpmcounter24" },
1213 { 0xc19, "hpmcounter25" },
1214 { 0xc1a, "hpmcounter26" },
1215 { 0xc1b, "hpmcounter27" },
1216 { 0xc1c, "hpmcounter28" },
1217 { 0xc1d, "hpmcounter29" },
1218 { 0xc1e, "hpmcounter30" },
1219 { 0xc1f, "hpmcounter31" },
1220 { 0xc80, "cycleh" },
1221 { 0xc81, "timeh" },
1222 { 0xc82, "instreth" },
1223 { 0xc83, "hpmcounter3h" },
1224 { 0xc84, "hpmcounter4h" },
1225 { 0xc85, "hpmcounter5h" },
1226 { 0xc86, "hpmcounter6h" },
1227 { 0xc87, "hpmcounter7h" },
1228 { 0xc88, "hpmcounter8h" },
1229 { 0xc89, "hpmcounter9h" },
1230 { 0xc8a, "hpmcounter10h" },
1231 { 0xc8b, "hpmcounter11h" },
1232 { 0xc8c, "hpmcounter12h" },
1233 { 0xc8d, "hpmcounter13h" },
1234 { 0xc8e, "hpmcounter14h" },
1235 { 0xc8f, "hpmcounter15h" },
1236 { 0xc90, "hpmcounter16h" },
1237 { 0xc91, "hpmcounter17h" },
1238 { 0xc92, "hpmcounter18h" },
1239 { 0xc93, "hpmcounter19h" },
1240 { 0xc94, "hpmcounter20h" },
1241 { 0xc95, "hpmcounter21h" },
1242 { 0xc96, "hpmcounter22h" },
1243 { 0xc97, "hpmcounter23h" },
1244 { 0xc98, "hpmcounter24h" },
1245 { 0xc99, "hpmcounter25h" },
1246 { 0xc9a, "hpmcounter26h" },
1247 { 0xc9b, "hpmcounter27h" },
1248 { 0xc9c, "hpmcounter28h" },
1249 { 0xc9d, "hpmcounter29h" },
1250 { 0xc9e, "hpmcounter30h" },
1251 { 0xc9f, "hpmcounter31h" },
1252 };
1253 uint32_t csr = word >> 20;
1254 uint32_t instr = (word >> 12) & 0x7;
1255 size_t last = 0;
1256 if (rd != 0)
1257 op[last++] = REG (rd);
1258 struct known_csrs key = { csr, NULL };
1259 struct known_csrs *found = bsearch (&key, known,
1260 sizeof (known) / sizeof (known[0]),
1261 sizeof (known[0]),
1262 compare_csr);
1263 if (found)
1264 op[last] = (char *) found->name;
1265 else
1266 {
1267 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx32, csr);
1268 op[last] = addrbuf;
1269 }
1270 ++last;
1271 if ((word & 0x4000) == 0)
1272 op[last] = REG ((word >> 15) & 0x1f);
1273 else
1274 {
1275 snprintf (immbuf, sizeof (immbuf), "%" PRIu32, (word >> 15) & UINT32_C(0x1f));
1276 op[last] = immbuf;
1277 }
1278 if (instr == 1 && rd == 0)
1279 mne = "csrw";
1280 else if (instr == 2 && rd == 0)
1281 mne = "csrs";
1282 else if (instr == 6 && rd == 0)
1283 mne = "csrsi";
1284 else if (instr == 2 && rs1 == 0)
1285 mne = "csrr";
1286 else if (instr == 3 && rd == 0)
1287 mne = "csrc";
1288 else
1289 mne = (char *) mnecsr[instr];
1290 }
1291 break;
1292 default:
1293 break;
1294 }
1295
1296 if (strp == NULL && mne == NULL)
1297 {
1298 len = snprintf (addrbuf, sizeof (addrbuf), "0x%08" PRIx32, word);
1299 strp = addrbuf;
1300 }
1301 }
1302 else
1303 {
1304 // No instruction encodings defined for these sizes yet.
1305 char *cp = stpcpy (mnebuf, "0x");
1306 assert (length % 2 == 0);
1307 for (size_t i = 0; i < length; i += 2)
1308 cp += snprintf (cp, mnebuf + sizeof (mnebuf) - cp, "%04" PRIx16,
1309 read_2ubyte_unaligned (data + i));
1310 strp = mnebuf;
1311 len = cp - mnebuf;
1312 }
1313
1314 if (strp == NULL)
1315 {
1316
1317 if (0)
1318 {
1319 /* Resize the buffer. */
1320 char *oldbuf;
1321 enomem:
1322 oldbuf = buf;
1323 if (buf == initbuf)
1324 buf = malloc (2 * bufsize);
1325 else
1326 buf = realloc (buf, 2 * bufsize);
1327 if (buf == NULL)
1328 {
1329 buf = oldbuf;
1330 retval = ENOMEM;
1331 goto do_ret;
1332 }
1333 bufsize *= 2;
1334
1335 bufcnt = 0;
1336 }
1337
1338 unsigned long string_end_idx = 0;
1339 fmt = save_fmt;
1340 const char *deferred_start = NULL;
1341 size_t deferred_len = 0;
1342 // XXX Can we get this from color.c?
1343 static const char color_off[] = "\e[0m";
1344 while (*fmt != '\0')
1345 {
1346 if (*fmt != '%')
1347 {
1348 char ch = *fmt++;
1349 if (ch == '\\')
1350 {
1351 switch ((ch = *fmt++))
1352 {
1353 case '0' ... '7':
1354 {
1355 int val = ch - '0';
1356 ch = *fmt;
1357 if (ch >= '0' && ch <= '7')
1358 {
1359 val *= 8;
1360 val += ch - '0';
1361 ch = *++fmt;
1362 if (ch >= '0' && ch <= '7' && val < 32)
1363 {
1364 val *= 8;
1365 val += ch - '0';
1366 ++fmt;
1367 }
1368 }
1369 ch = val;
1370 }
1371 break;
1372
1373 case 'n':
1374 ch = '\n';
1375 break;
1376
1377 case 't':
1378 ch = '\t';
1379 break;
1380
1381 default:
1382 retval = EINVAL;
1383 goto do_ret;
1384 }
1385 }
1386 else if (ch == '\e' && *fmt == '[')
1387 {
1388 deferred_start = fmt - 1;
1389 do
1390 ++fmt;
1391 while (*fmt != 'm' && *fmt != '\0');
1392
1393 if (*fmt == 'm')
1394 {
1395 deferred_len = ++fmt - deferred_start;
1396 continue;
1397 }
1398
1399 fmt = deferred_start + 1;
1400 deferred_start = NULL;
1401 }
1402 ADD_CHAR (ch);
1403 continue;
1404 }
1405 ++fmt;
1406
1407 int width = 0;
1408 while (isdigit (*fmt))
1409 width = width * 10 + (*fmt++ - '0');
1410
1411 int prec = 0;
1412 if (*fmt == '.')
1413 while (isdigit (*++fmt))
1414 prec = prec * 10 + (*fmt - '0');
1415
1416 size_t start_idx = bufcnt;
1417 size_t non_printing = 0;
1418 switch (*fmt++)
1419 {
1420 case 'm':
1421 if (deferred_start != NULL)
1422 {
1423 ADD_NSTRING (deferred_start, deferred_len);
1424 non_printing += deferred_len;
1425 }
1426
1427 ADD_STRING (mne);
1428
1429 if (deferred_start != NULL)
1430 {
1431 ADD_STRING (color_off);
1432 non_printing += strlen (color_off);
1433 }
1434
1435 string_end_idx = bufcnt;
1436 break;
1437
1438 case 'o':
1439 if (op[prec - 1] != NULL)
1440 {
1441 if (deferred_start != NULL)
1442 {
1443 ADD_NSTRING (deferred_start, deferred_len);
1444 non_printing += deferred_len;
1445 }
1446
1447 ADD_STRING (op[prec - 1]);
1448
1449 if (deferred_start != NULL)
1450 {
1451 ADD_STRING (color_off);
1452 non_printing += strlen (color_off);
1453 }
1454
1455 string_end_idx = bufcnt;
1456 }
1457 else
1458 bufcnt = string_end_idx;
1459 break;
1460
1461 case 'e':
1462 string_end_idx = bufcnt;
1463 break;
1464
1465 case 'a':
1466 /* Pad to requested column. */
1467 while (bufcnt - non_printing < (size_t) width)
1468 ADD_CHAR (' ');
1469 width = 0;
1470 break;
1471
1472 case 'l':
1473 // TODO
1474 break;
1475
1476 default:
1477 abort();
1478 }
1479
1480 /* Pad according to the specified width. */
1481 while (bufcnt - non_printing < start_idx + width)
1482 ADD_CHAR (' ');
1483 }
1484
1485 strp = buf;
1486 len = bufcnt;
1487 }
1488
1489 addr += length;
1490 *startp = data + length;
1491 retval = outcb (strp, len, outcbarg);
1492 if (retval != 0)
1493 break;
1494 }
1495
1496 do_ret:
1497 if (buf != initbuf)
1498 free (buf);
1499
1500 return retval;
1501 }
1502