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