| 1 | /* -*-c-*- |
| 2 | * |
| 3 | * Register dumping and other diagnostic tools for assembler code |
| 4 | * |
| 5 | * (c) 2016 Straylight/Edgeware |
| 6 | */ |
| 7 | |
| 8 | /*----- Licensing notice --------------------------------------------------* |
| 9 | * |
| 10 | * This file is part of Catacomb. |
| 11 | * |
| 12 | * Catacomb is free software; you can redistribute it and/or modify |
| 13 | * it under the terms of the GNU Library General Public License as |
| 14 | * published by the Free Software Foundation; either version 2 of the |
| 15 | * License, or (at your option) any later version. |
| 16 | * |
| 17 | * Catacomb is distributed in the hope that it will be useful, |
| 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 20 | * GNU Library General Public License for more details. |
| 21 | * |
| 22 | * You should have received a copy of the GNU Library General Public |
| 23 | * License along with Catacomb; if not, write to the Free |
| 24 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
| 25 | * MA 02111-1307, USA. |
| 26 | */ |
| 27 | |
| 28 | /*----- Header files ------------------------------------------------------*/ |
| 29 | |
| 30 | #include "config.h" |
| 31 | |
| 32 | #include <assert.h> |
| 33 | #include <limits.h> |
| 34 | #include <stdio.h> |
| 35 | #include <string.h> |
| 36 | |
| 37 | #include <mLib/bits.h> |
| 38 | #include <mLib/macros.h> |
| 39 | |
| 40 | #include "dispatch.h" |
| 41 | #include "regdump.h" |
| 42 | |
| 43 | /*----- Low-level printing ------------------------------------------------*/ |
| 44 | |
| 45 | /* Currently these are good for all of our targets. */ |
| 46 | #define STEP_8 1 |
| 47 | #define TY_HEX_8 uint8 |
| 48 | #define P_HEX_8 "0x%02x" |
| 49 | #define TY_UNSGN_8 uint8 |
| 50 | #define P_UNSGN_8 "%3u" |
| 51 | #define PV_CHR_8 " `%c'" |
| 52 | #define PV_HEX_8 " %02x" |
| 53 | #define PV_UNSGN_8 "%4u" |
| 54 | |
| 55 | #define STEP_16 2 |
| 56 | #define TY_HEX_16 uint16 |
| 57 | #define P_HEX_16 "0x%04x" |
| 58 | #define TY_UNSGN_16 uint16 |
| 59 | #define P_UNSGN_16 "%5u" |
| 60 | #define TY_SGN_16 int16 |
| 61 | #define P_SGN_16 "%6d" |
| 62 | #define PV_HEX_16 " 0x%04x" |
| 63 | #define PV_UNSGN_16 "%9u" |
| 64 | #define PV_SGN_16 "%9d" |
| 65 | |
| 66 | #define STEP_32 4 |
| 67 | #define TY_HEX_32 uint32 |
| 68 | #define P_HEX_32 "0x%08x" |
| 69 | #define TY_UNSGN_32 uint32 |
| 70 | #define P_UNSGN_32 "%10u" |
| 71 | #define TY_SGN_32 int32 |
| 72 | #define P_SGN_32 "%11d" |
| 73 | #define TY_FLT_32 float |
| 74 | #define P_FLT_32 "%15.9g" |
| 75 | #define PV_HEX_32 " 0x%08x" |
| 76 | #define PV_UNSGN_32 "%19u" |
| 77 | #define PV_SGN_32 "%19d" |
| 78 | #define PV_FLT_32 "%19.9g" |
| 79 | |
| 80 | #if ULONG_MAX >> 31 > 0xffffffff |
| 81 | # define PL64 "l" |
| 82 | #else |
| 83 | # define PL64 "ll" |
| 84 | #endif |
| 85 | #define STEP_64 8 |
| 86 | #define TY_HEX_64 uint64 |
| 87 | #define P_HEX_64 "0x%016"PL64"x" |
| 88 | #define TY_UNSGN_64 uint64 |
| 89 | #define P_UNSGN_64 "%20"PL64"u" |
| 90 | #define TY_SGN_64 int64 |
| 91 | #define P_SGN_64 "%20"PL64"d" |
| 92 | #define TY_FLT_64 double |
| 93 | #define P_FLT_64 "%24.17g" |
| 94 | #define PV_HEX_64 " 0x%016"PL64"x" |
| 95 | #define PV_UNSGN_64 "%39"PL64"u" |
| 96 | #define PV_SGN_64 "%39"PL64"d" |
| 97 | #define PV_FLT_64 "%39.17g" |
| 98 | |
| 99 | #if CPUFAM_X86 |
| 100 | # define STEP_80 12 |
| 101 | #endif |
| 102 | #if CPUFAM_AMD64 |
| 103 | # define STEP_80 16 |
| 104 | #endif |
| 105 | #define TY_FLT_80 long double |
| 106 | #define P_FLT_80 "%29.21Lg" |
| 107 | #define PV_FLT_80 P_FLT_80 |
| 108 | |
| 109 | #if CPUFAM_X86 || CPUFAM_AMD64 |
| 110 | # define ARCH_FORMATS(_) \ |
| 111 | _(80, FLT) |
| 112 | #endif |
| 113 | #ifndef ARCH_FORMATS |
| 114 | # define ARCH_FORMATS(_) |
| 115 | #endif |
| 116 | |
| 117 | #define FORMATS(_) \ |
| 118 | ARCH_FORMATS(_) \ |
| 119 | _(64, HEX) _(64, FLT) _(64, UNSGN) _(64, SGN) \ |
| 120 | _(32, HEX) _(32, FLT) _(32, UNSGN) _(32, SGN) \ |
| 121 | _(16, HEX) _(16, UNSGN) _(16, SGN) \ |
| 122 | _(8, HEX) _(8, CHR) _(8, UNSGN) |
| 123 | |
| 124 | struct fmtinfo { |
| 125 | const unsigned char *p; |
| 126 | unsigned wd, f; |
| 127 | #define FMTF_VECTOR 1u |
| 128 | }; |
| 129 | |
| 130 | #define FMTFUNC_STD(w, fmt) \ |
| 131 | static void dump_##fmt##_##w(struct fmtinfo *fmt) \ |
| 132 | { \ |
| 133 | TY_##fmt##_##w x = *(const TY_##fmt##_##w *)fmt->p; \ |
| 134 | \ |
| 135 | if (fmt->f&FMTF_VECTOR) printf(PV_##fmt##_##w, x); \ |
| 136 | else printf(P_##fmt##_##w, x); \ |
| 137 | fmt->p += STEP_##w; fmt->wd += 8*STEP_##w; \ |
| 138 | } |
| 139 | |
| 140 | #define FMTFUNC_HEX(w) FMTFUNC_STD(w, HEX) |
| 141 | #define FMTFUNC_UNSGN(w) FMTFUNC_STD(w, UNSGN) |
| 142 | #define FMTFUNC_SGN(w) FMTFUNC_STD(w, SGN) |
| 143 | #define FMTFUNC_FLT(w) FMTFUNC_STD(w, FLT) |
| 144 | #define FMTFUNC_CHR(w) |
| 145 | |
| 146 | static void dump_CHR_8(struct fmtinfo *fmt) |
| 147 | { |
| 148 | unsigned char x = *(const unsigned char *)fmt->p; |
| 149 | |
| 150 | if (x < 32 || x > 126) printf("\\x%02x", x); |
| 151 | else printf(" `%c'", x); |
| 152 | fmt->p += 1; fmt->wd += 8; |
| 153 | } |
| 154 | |
| 155 | #define FMTFUNC(w, fmt) FMTFUNC_##fmt(w) |
| 156 | FORMATS(FMTFUNC) |
| 157 | #undef FMTFUNC |
| 158 | |
| 159 | static const struct fmttab { |
| 160 | uint32 mask; |
| 161 | void (*fmt)(struct fmtinfo *); |
| 162 | } fmttab[] = { |
| 163 | #define FMTTAB(wd, fmt) { REGF_##fmt | REGF_##wd, dump_##fmt##_##wd }, |
| 164 | FORMATS(FMTTAB) |
| 165 | #undef FMTTAB |
| 166 | { 0, 0 } |
| 167 | }; |
| 168 | |
| 169 | /*----- Common subroutines ------------------------------------------------*/ |
| 170 | |
| 171 | /* --- @regwd@ --- * |
| 172 | * |
| 173 | * Arguments: @uint32 f@ = format control word; see @REGF_...@ |
| 174 | * |
| 175 | * Returns: The actual width of the operand, in bits. |
| 176 | * |
| 177 | * Use: If the operand is a vector (the @REGF_WDMASK@ field is |
| 178 | * nonzero) then return the width it denotes; otherwise, return |
| 179 | * the largest width implied by the @REGF_TYMASK@ field. |
| 180 | */ |
| 181 | |
| 182 | static unsigned regwd(uint32 f) |
| 183 | { |
| 184 | unsigned wd = 1 << ((f®F_WDMASK) >> REGF_WDSHIFT); |
| 185 | |
| 186 | if (wd > 1) return (wd); |
| 187 | else if (f®F_80) return (80); |
| 188 | else if (f®F_64) return (64); |
| 189 | else if (f®F_32) return (32); |
| 190 | else if (f®F_16) return (16); |
| 191 | else if (f®F_8) return (8); |
| 192 | else { assert(0); return (1); } |
| 193 | } |
| 194 | |
| 195 | /* --- @regname@ --- * |
| 196 | * |
| 197 | * Arguments: @char *buf = pointer to output buffer@ |
| 198 | * @uint32 f@ = format control word; see @REGF_...@ |
| 199 | * |
| 200 | * Returns: Pointer to name string. |
| 201 | * |
| 202 | * Use: Return a pointer to the name of the register implied by @f@, |
| 203 | * or null if there is no register. Systematic register names |
| 204 | * can be built in the provided buffer. |
| 205 | */ |
| 206 | |
| 207 | static const char *regname(char *buf, uint32 f) |
| 208 | { |
| 209 | unsigned wd = regwd(f); |
| 210 | unsigned src = f®F_SRCMASK; |
| 211 | unsigned ix = (f®F_IXMASK) >> REGF_IXSHIFT; |
| 212 | char *p = buf; |
| 213 | |
| 214 | switch (src) { |
| 215 | |
| 216 | case REGSRC_ABS: |
| 217 | return (0); |
| 218 | |
| 219 | #if CPUFAM_X86 || CPUFAM_AMD64 |
| 220 | case REGSRC_GP: |
| 221 | if (ix == REGIX_FLAGS) { |
| 222 | if (wd == 64) *p++ = 'r'; |
| 223 | else if (wd == 32) *p++ = 'e'; |
| 224 | else if (wd != 16) assert(0); |
| 225 | p += sprintf(p, "flags"); |
| 226 | #if CPUFAM_AMD64 |
| 227 | } else if (REGIX_R8 <= ix && ix <= REGIX_R15) { |
| 228 | p += sprintf(p, "r%u", ix - REGIX_R8 + 8); |
| 229 | switch (wd) { |
| 230 | case 64: break; |
| 231 | case 32: *p++ = 'd'; break; |
| 232 | case 16: *p++ = 'w'; break; |
| 233 | case 8: *p++ = 'l'; break; |
| 234 | default: assert(0); |
| 235 | } |
| 236 | # endif |
| 237 | } else { |
| 238 | if (wd == 64) *p++ = 'r'; |
| 239 | else if (wd == 32) *p++ = 'e'; |
| 240 | switch (ix) { |
| 241 | case REGIX_IP: *p++ = 'i'; *p++ = 'p'; goto longreg; |
| 242 | case REGIX_AX: *p++ = 'a'; goto shortreg; |
| 243 | case REGIX_BX: *p++ = 'b'; goto shortreg; |
| 244 | case REGIX_CX: *p++ = 'c'; goto shortreg; |
| 245 | case REGIX_DX: *p++ = 'd'; goto shortreg; |
| 246 | case REGIX_SI: *p++ = 's'; *p++ = 'i'; goto longreg; |
| 247 | case REGIX_DI: *p++ = 'd'; *p++ = 'i'; goto longreg; |
| 248 | case REGIX_BP: *p++ = 'b'; *p++ = 'p'; goto longreg; |
| 249 | case REGIX_SP: *p++ = 's'; *p++ = 'p'; goto longreg; |
| 250 | default: assert(0); |
| 251 | } |
| 252 | if (0) { |
| 253 | shortreg: |
| 254 | switch (wd) { |
| 255 | case 64: |
| 256 | case 32: |
| 257 | case 16: *p++ = 'x'; break; |
| 258 | case 8: *p++ = 'l'; break; |
| 259 | default: assert(0); |
| 260 | } |
| 261 | } else { |
| 262 | longreg: |
| 263 | switch (wd) { |
| 264 | case 64: |
| 265 | case 32: |
| 266 | case 16: break; |
| 267 | case 8: *p++ = 'l'; break; |
| 268 | default: assert(0); |
| 269 | } |
| 270 | } |
| 271 | } |
| 272 | *p++ = 0; |
| 273 | return (buf); |
| 274 | |
| 275 | case REGSRC_SEG: |
| 276 | assert(wd == 16); |
| 277 | switch (ix) { |
| 278 | case REGIX_CS: sprintf(buf, "cs"); break; |
| 279 | case REGIX_DS: sprintf(buf, "ds"); break; |
| 280 | case REGIX_SS: sprintf(buf, "ss"); break; |
| 281 | case REGIX_ES: sprintf(buf, "es"); break; |
| 282 | case REGIX_FS: sprintf(buf, "fs"); break; |
| 283 | case REGIX_GS: sprintf(buf, "gs"); break; |
| 284 | default: assert(0); |
| 285 | } |
| 286 | return (buf); |
| 287 | |
| 288 | case REGSRC_STMMX: |
| 289 | if (ix == REGIX_FPFLAGS) return (0); |
| 290 | if (f®F_80) sprintf(buf, "st(%u)", ix); |
| 291 | else sprintf(buf, "mm%u", ix); |
| 292 | return (buf); |
| 293 | |
| 294 | case REGSRC_SIMD: |
| 295 | if (ix == REGIX_FPFLAGS) return (0); |
| 296 | switch (wd) { |
| 297 | case 32: case 64: case 128: sprintf(buf, "xmm%u", ix); break; |
| 298 | case 256: sprintf(buf, "ymm%u", ix); break; |
| 299 | default: assert(0); |
| 300 | } |
| 301 | return (buf); |
| 302 | #endif |
| 303 | |
| 304 | #if CPUFAM_ARMEL |
| 305 | case REGSRC_GP: |
| 306 | if (ix == REGIX_CPSR) sprintf(buf, "cpsr"); |
| 307 | else if (ix == 15) sprintf(buf, "pc"); |
| 308 | else sprintf(buf, "r%u", ix); |
| 309 | return (buf); |
| 310 | case REGSRC_FP: |
| 311 | if (ix == REGIX_FPSCR) sprintf(buf, "fpscr"); |
| 312 | else { |
| 313 | switch (wd) { |
| 314 | case 32: *p++ = 's'; break; |
| 315 | case 64: *p++ = 'd'; break; |
| 316 | case 128: *p++ = 'q'; break; |
| 317 | default: assert(0); |
| 318 | } |
| 319 | p += sprintf(p, "%u", ix); |
| 320 | *p++ = 0; |
| 321 | } |
| 322 | return (buf); |
| 323 | #endif |
| 324 | |
| 325 | #if CPUFAM_ARM64 |
| 326 | case REGSRC_GP: |
| 327 | if (ix == REGIX_PC) sprintf(buf, "pc"); |
| 328 | else if (ix == REGIX_NZCV) sprintf(buf, "nzcv"); |
| 329 | else if (ix == 31 && wd == 64) sprintf(buf, "sp"); |
| 330 | else { |
| 331 | switch (wd) { |
| 332 | case 32: *p++ = 'w'; break; |
| 333 | case 64: *p++ = 'x'; break; |
| 334 | default: assert(0); |
| 335 | } |
| 336 | p += sprintf(p, "%u", ix); |
| 337 | *p++ = 0; |
| 338 | } |
| 339 | return (buf); |
| 340 | case REGSRC_FP: |
| 341 | if (ix == REGIX_FPFLAGS) sprintf(buf, "fpflags"); |
| 342 | else { |
| 343 | if (f®F_WDMASK) |
| 344 | *p++ = 'v'; |
| 345 | else switch (wd) { |
| 346 | case 8: *p++ = 'b'; break; |
| 347 | case 16: *p++ = 'h'; break; |
| 348 | case 32: *p++ = 's'; break; |
| 349 | case 64: *p++ = 'd'; break; |
| 350 | default: assert(0); |
| 351 | } |
| 352 | p += sprintf(p, "%u", ix); |
| 353 | *p++ = 0; |
| 354 | } |
| 355 | return (buf); |
| 356 | #endif |
| 357 | |
| 358 | default: |
| 359 | assert(0); |
| 360 | return ("???"); |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | /*----- x86 and AMD64 -----------------------------------------------------*/ |
| 365 | |
| 366 | #if CPUFAM_X86 || CPUFAM_AMD64 |
| 367 | |
| 368 | #if CPUFAM_X86 |
| 369 | # define P_HEX_GP "0x%08x" |
| 370 | # define GP(gp) (gp).u32 |
| 371 | #endif |
| 372 | #if CPUFAM_AMD64 |
| 373 | # define P_HEX_GP "0x%016"PL64"x" |
| 374 | # define GP(gp) (gp).u64 |
| 375 | #endif |
| 376 | |
| 377 | void regdump_init(void) { ; } |
| 378 | |
| 379 | static void dump_flags(const char *lbl, const char *reg, gpreg f) |
| 380 | { |
| 381 | printf(";; "); |
| 382 | if (lbl) printf("%s: ", lbl); |
| 383 | if (reg) printf("%s = ", reg); |
| 384 | printf(""P_HEX_GP"\n", GP(f)); |
| 385 | printf(";;\t\tstatus: %ccf %cpf %caf %czf %csf %cdf %cof\n", |
| 386 | (GP(f) >> 0)&1u ? '+' : '-', |
| 387 | (GP(f) >> 2)&1u ? '+' : '-', |
| 388 | (GP(f) >> 4)&1u ? '+' : '-', |
| 389 | (GP(f) >> 6)&1u ? '+' : '-', |
| 390 | (GP(f) >> 7)&1u ? '+' : '-', |
| 391 | (GP(f) >> 10)&1u ? '+' : '-', |
| 392 | (GP(f) >> 11)&1u ? '+' : '-'); |
| 393 | printf(";;\t\tsystem: %ctf %cif iopl=%d %cnt " |
| 394 | "%crf %cvm %cac %cvif %cvip %cid\n", |
| 395 | (GP(f) >> 8)&1u ? '+' : '-', |
| 396 | (GP(f) >> 9)&1u ? '+' : '-', |
| 397 | (int)((GP(f) >> 12)&1u), |
| 398 | (GP(f) >> 14)&1u ? '+' : '-', |
| 399 | (GP(f) >> 16)&1u ? '+' : '-', |
| 400 | (GP(f) >> 17)&1u ? '+' : '-', |
| 401 | (GP(f) >> 18)&1u ? '+' : '-', |
| 402 | (GP(f) >> 19)&1u ? '+' : '-', |
| 403 | (GP(f) >> 20)&1u ? '+' : '-', |
| 404 | (GP(f) >> 21)&1u ? '+' : '-'); |
| 405 | } |
| 406 | |
| 407 | static const char |
| 408 | *pcmap[] = { "sgl", "???", "dbl", "ext" }, |
| 409 | *rcmap[] = { "nr", "-∞", "+∞", "0" }; |
| 410 | |
| 411 | static void dump_fpflags(const char *lbl, const struct fxsave *fx) |
| 412 | { |
| 413 | unsigned top = (fx->fsw >> 11)&7u; |
| 414 | unsigned tag = fx->ftw; |
| 415 | int skip = lbl ? strlen(lbl) + 2 : 0; |
| 416 | |
| 417 | printf(";; "); |
| 418 | if (lbl) printf("%s: ", lbl); |
| 419 | |
| 420 | printf(" fcw = 0x%04x: " |
| 421 | "%cim %cdm %czm %com %cum %cpm pc=%s rc=%s %cx\n", |
| 422 | fx->fcw, |
| 423 | (fx->fcw >> 0)&1u ? '+' : '-', |
| 424 | (fx->fcw >> 1)&1u ? '+' : '-', |
| 425 | (fx->fcw >> 2)&1u ? '+' : '-', |
| 426 | (fx->fcw >> 3)&1u ? '+' : '-', |
| 427 | (fx->fcw >> 4)&1u ? '+' : '-', |
| 428 | (fx->fcw >> 5)&1u ? '+' : '-', |
| 429 | pcmap[(fx->fcw >> 8)&3u], |
| 430 | rcmap[(fx->fcw >> 10)&3u], |
| 431 | (fx->fcw >> 12)&1u ? '+' : '-'); |
| 432 | printf(";; %*s fsw = 0x%04x: " |
| 433 | "%cie %cde %cze %coe %cue %cpe %csf %ces %cc0 %cc1 %cc2 %cc3 " |
| 434 | "top=%d %cb\n", |
| 435 | skip, "", |
| 436 | fx->fsw, |
| 437 | (fx->fsw >> 0)&1u ? '+' : '-', |
| 438 | (fx->fsw >> 1)&1u ? '+' : '-', |
| 439 | (fx->fsw >> 2)&1u ? '+' : '-', |
| 440 | (fx->fsw >> 3)&1u ? '+' : '-', |
| 441 | (fx->fsw >> 4)&1u ? '+' : '-', |
| 442 | (fx->fsw >> 5)&1u ? '+' : '-', |
| 443 | (fx->fsw >> 6)&1u ? '+' : '-', |
| 444 | (fx->fsw >> 7)&1u ? '+' : '-', |
| 445 | (fx->fsw >> 8)&1u ? '+' : '-', |
| 446 | (fx->fsw >> 9)&1u ? '+' : '-', |
| 447 | (fx->fsw >> 10)&1u ? '+' : '-', |
| 448 | (fx->fsw >> 14)&1u ? '+' : '-', |
| 449 | top, |
| 450 | (fx->fsw >> 15)&1u ? '+' : '-'); |
| 451 | printf(";; %*s ftw = 0x%02x\n", skip, "", tag); |
| 452 | } |
| 453 | |
| 454 | static void dump_mxflags(const char *lbl, const struct fxsave *fx) |
| 455 | { |
| 456 | printf(";; "); |
| 457 | if (lbl) printf("%s: ", lbl); |
| 458 | |
| 459 | printf(" mxcsr = 0x%08x\n" |
| 460 | ";;\t\tmask = %cim %cdm %czm %com %cum %cpm\n" |
| 461 | ";;\t\t exc = %cie %cde %cze %coe %cue %cpe\n" |
| 462 | ";;\t\tmisc = %cdaz %cftz rc=%s\n", |
| 463 | fx->mxcsr, |
| 464 | (fx->mxcsr >> 7)&1u ? '+' : '-', |
| 465 | (fx->mxcsr >> 8)&1u ? '+' : '-', |
| 466 | (fx->mxcsr >> 9)&1u ? '+' : '-', |
| 467 | (fx->mxcsr >> 10)&1u ? '+' : '-', |
| 468 | (fx->mxcsr >> 11)&1u ? '+' : '-', |
| 469 | (fx->mxcsr >> 12)&1u ? '+' : '-', |
| 470 | (fx->mxcsr >> 0)&1u ? '+' : '-', |
| 471 | (fx->mxcsr >> 1)&1u ? '+' : '-', |
| 472 | (fx->mxcsr >> 2)&1u ? '+' : '-', |
| 473 | (fx->mxcsr >> 3)&1u ? '+' : '-', |
| 474 | (fx->mxcsr >> 4)&1u ? '+' : '-', |
| 475 | (fx->mxcsr >> 5)&1u ? '+' : '-', |
| 476 | (fx->mxcsr >> 6)&1u ? '+' : '-', |
| 477 | (fx->mxcsr >> 15)&1u ? '+' : '-', |
| 478 | rcmap[(fx->mxcsr >> 13)&3u]); |
| 479 | } |
| 480 | |
| 481 | #if CPUFAM_X86 |
| 482 | # define REGF_GPWD REGF_32 |
| 483 | #endif |
| 484 | #if CPUFAM_AMD64 |
| 485 | # define REGF_GPWD REGF_64 |
| 486 | #endif |
| 487 | |
| 488 | void regdump_gp(const struct regmap *map) |
| 489 | { |
| 490 | unsigned i; |
| 491 | |
| 492 | printf(";; General-purpose registers:\n"); |
| 493 | for (i = REGIX_AX; i < REGIX_GPLIM; i++) |
| 494 | regdump(map, 0, |
| 495 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_GPWD | REGSRC_GP | i); |
| 496 | regdump(map, 0, REGF_HEX | REGF_GPWD | REGSRC_GP | REGIX_IP); |
| 497 | |
| 498 | printf(";; Segment registers:\n"); |
| 499 | for (i = 0; i < REGIX_SEGLIM; i++) |
| 500 | regdump(map, 0, REGF_HEX | REGF_16 | REGSRC_SEG | i); |
| 501 | |
| 502 | printf(";; Flags:\n"); |
| 503 | regdump(map, 0, REGSRC_GP | REGF_GPWD | REGIX_FLAGS); |
| 504 | } |
| 505 | |
| 506 | void regdump_fp(const struct regmap *map) |
| 507 | { |
| 508 | unsigned top = (map->fx->fsw >> 11)&7u; |
| 509 | unsigned tag = map->fx->ftw; |
| 510 | unsigned i; |
| 511 | |
| 512 | printf(";; Floating-point/MMX registers:\n"); |
| 513 | if (!top && tag == 0xff) |
| 514 | for (i = 0; i < 8; i++) |
| 515 | regdump(map, 0, |
| 516 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_CHR | |
| 517 | REGF_32 | REGF_16 | REGF_8 | |
| 518 | REGSRC_STMMX | i | (6 << REGF_WDSHIFT)); |
| 519 | if (tag) |
| 520 | for (i = 0; i < 8; i++) |
| 521 | regdump(map, 0, REGF_FLT | REGF_80 | REGSRC_STMMX | i); |
| 522 | |
| 523 | printf(";; Floating-point state:\n"); |
| 524 | dump_fpflags(0, map->fx); |
| 525 | } |
| 526 | |
| 527 | void regdump_simd(const struct regmap *map) |
| 528 | { |
| 529 | unsigned f = REGF_HEX | REGF_FLT | REGF_UNSGN | REGF_SGN | REGF_CHR | |
| 530 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | |
| 531 | REGSRC_SIMD; |
| 532 | unsigned i; |
| 533 | |
| 534 | if (map->avx) f |= 8 << REGF_WDSHIFT; |
| 535 | else f |= 7 << REGF_WDSHIFT; |
| 536 | |
| 537 | printf(";; SSE/AVX registers:\n"); |
| 538 | for (i = 0; i < N(map->fx->xmm); i++) |
| 539 | regdump(map, 0, f | i); |
| 540 | |
| 541 | printf(";; SSE/AVX floating-point state:\n"); |
| 542 | dump_mxflags(0, map->fx); |
| 543 | } |
| 544 | |
| 545 | #endif |
| 546 | |
| 547 | /*----- ARM32 -------------------------------------------------------------*/ |
| 548 | |
| 549 | #if CPUFAM_ARMEL |
| 550 | |
| 551 | unsigned regdump__flags = 0; |
| 552 | |
| 553 | void regdump_init(void) |
| 554 | { |
| 555 | if (cpu_feature_p(CPUFEAT_ARM_VFP)) regdump__flags |= REGF_VFP; |
| 556 | if (cpu_feature_p(CPUFEAT_ARM_D32)) regdump__flags |= REGF_D32; |
| 557 | } |
| 558 | |
| 559 | static void dump_flags(const char *lbl, unsigned f) |
| 560 | { |
| 561 | static const char |
| 562 | *modetab[] = { "?00", "?01", "?02", "?03", "?04", "?05", "?06", "?07", |
| 563 | "?08", "?09", "?10", "?11", "?12", "?13", "?14", "?15", |
| 564 | "usr", "fiq", "irq", "svc", "?20", "?21", "mon", "abt", |
| 565 | "?24", "?25", "hyp", "und", "?28", "?29", "?30", "sys" }, |
| 566 | *condtab[] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", |
| 567 | "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" }; |
| 568 | |
| 569 | printf(";; "); |
| 570 | if (lbl) printf("%s: ", lbl); |
| 571 | printf(" cpsr = 0x%08x\n", f); |
| 572 | printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c\n", |
| 573 | (f >> 31)&1u ? '+' : '-', |
| 574 | (f >> 30)&1u ? '+' : '-', |
| 575 | (f >> 29)&1u ? '+' : '-', |
| 576 | (f >> 28)&1u ? '+' : '-', |
| 577 | (f >> 27)&1u ? '+' : '-', |
| 578 | (f >> 19)&1u ? '1' : '0', |
| 579 | (f >> 18)&1u ? '1' : '0', |
| 580 | (f >> 17)&1u ? '1' : '0', |
| 581 | (f >> 16)&1u ? '1' : '0'); |
| 582 | printf(";;\t\tsystem: %cj it=%s:%c%c%c%c %ce %ca %ci %cf %ct m=%s\n", |
| 583 | (f >> 24)&1u ? '+' : '-', |
| 584 | condtab[(f >> 12)&15u], |
| 585 | (f >> 11)&1u ? '1' : '0', |
| 586 | (f >> 10)&1u ? '1' : '0', |
| 587 | (f >> 26)&1u ? '1' : '0', |
| 588 | (f >> 25)&1u ? '1' : '0', |
| 589 | (f >> 9)&1u ? '+' : '-', |
| 590 | (f >> 8)&1u ? '+' : '-', |
| 591 | (f >> 7)&1u ? '+' : '-', |
| 592 | (f >> 6)&1u ? '+' : '-', |
| 593 | (f >> 5)&1u ? '+' : '-', |
| 594 | modetab[(f >> 0)&31u]); |
| 595 | } |
| 596 | |
| 597 | static void dump_fpflags(const char *lbl, unsigned f) |
| 598 | { |
| 599 | static const char *rcmap[] = { "nr", "+∞", "-∞", "0" }; |
| 600 | |
| 601 | printf(";; "); |
| 602 | if (lbl) printf("%s: ", lbl); |
| 603 | printf(" fpscr = 0x%08x\n", f); |
| 604 | printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n", |
| 605 | (f >> 31)&1u ? '+' : '-', |
| 606 | (f >> 30)&1u ? '+' : '-', |
| 607 | (f >> 29)&1u ? '+' : '-', |
| 608 | (f >> 28)&1u ? '+' : '-', |
| 609 | (f >> 27)&1u ? '+' : '-'); |
| 610 | printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", |
| 611 | (f >> 15)&1u ? '+' : '-', |
| 612 | (f >> 12)&1u ? '+' : '-', |
| 613 | (f >> 11)&1u ? '+' : '-', |
| 614 | (f >> 10)&1u ? '+' : '-', |
| 615 | (f >> 9)&1u ? '+' : '-', |
| 616 | (f >> 8)&1u ? '+' : '-'); |
| 617 | printf(";;\t\terror: %cide %cixe %cufe %cofe %cdze %cioe\n", |
| 618 | (f >> 7)&1u ? '+' : '-', |
| 619 | (f >> 4)&1u ? '+' : '-', |
| 620 | (f >> 3)&1u ? '+' : '-', |
| 621 | (f >> 2)&1u ? '+' : '-', |
| 622 | (f >> 1)&1u ? '+' : '-', |
| 623 | (f >> 0)&1u ? '+' : '-'); |
| 624 | printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n", |
| 625 | (f >> 26)&1u ? '+' : '-', |
| 626 | (f >> 25)&1u ? '+' : '-', |
| 627 | (f >> 24)&1u ? '+' : '-', |
| 628 | rcmap[(f >> 22)&3u], |
| 629 | (f >> 20)&3u, |
| 630 | (f >> 16)&7u); |
| 631 | } |
| 632 | |
| 633 | void regdump_gp(const struct regmap *map) |
| 634 | { |
| 635 | unsigned i; |
| 636 | |
| 637 | printf(";; General-purpose registers:\n"); |
| 638 | for (i = 0; i < 16; i++) |
| 639 | regdump(map, 0, |
| 640 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_32 | REGSRC_GP | i); |
| 641 | |
| 642 | printf(";; Flags:\n"); |
| 643 | regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_CPSR); |
| 644 | } |
| 645 | |
| 646 | void regdump_fp(const struct regmap *map) |
| 647 | { |
| 648 | unsigned i, n; |
| 649 | |
| 650 | if (!(regdump__flags®F_VFP)) { |
| 651 | printf(";; Floating-point and SIMD not available\n"); |
| 652 | return; |
| 653 | } |
| 654 | |
| 655 | printf(";; Floating-point/SIMD registers:\n"); |
| 656 | if (regdump__flags®F_D32) n = 32; |
| 657 | else n = 16; |
| 658 | for (i = 0; i < n; i++) |
| 659 | regdump(map, 0, |
| 660 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR | |
| 661 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | |
| 662 | REGSRC_SIMD | i | (6 << REGF_WDSHIFT)); |
| 663 | |
| 664 | printf(";; Floating-point state:\n"); |
| 665 | dump_fpflags(0, map->fp->fpscr); |
| 666 | } |
| 667 | |
| 668 | void regdump_simd(const struct regmap *map) { ; } |
| 669 | |
| 670 | #endif |
| 671 | |
| 672 | /*----- ARM64 -------------------------------------------------------------*/ |
| 673 | |
| 674 | #if CPUFAM_ARM64 |
| 675 | |
| 676 | void regdump_init(void) { ; } |
| 677 | |
| 678 | static void dump_flags(const char *lbl, unsigned f) |
| 679 | { |
| 680 | printf(";; "); |
| 681 | if (lbl) printf("%s: ", lbl); |
| 682 | printf(" nzcv = 0x%08x\n", f); |
| 683 | printf(";;\t\tuser: %cn %cz %cc %cv\n", |
| 684 | (f >> 31)&1u ? '+' : '-', |
| 685 | (f >> 30)&1u ? '+' : '-', |
| 686 | (f >> 29)&1u ? '+' : '-', |
| 687 | (f >> 28)&1u ? '+' : '-'); |
| 688 | } |
| 689 | |
| 690 | static void dump_fpflags(const char *lbl, const struct fpsave *fp) |
| 691 | { |
| 692 | static const char *rcmap[] = { "nr", "+∞", "-∞", "0" }; |
| 693 | int skip = lbl ? strlen(lbl) + 2 : 0; |
| 694 | |
| 695 | printf(";; "); |
| 696 | if (lbl) printf("%s: ", lbl); |
| 697 | printf(" fpsr = 0x%08x\n", fp->fpsr); |
| 698 | printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n", |
| 699 | (fp->fpsr >> 31)&1u ? '+' : '-', |
| 700 | (fp->fpsr >> 30)&1u ? '+' : '-', |
| 701 | (fp->fpsr >> 29)&1u ? '+' : '-', |
| 702 | (fp->fpsr >> 28)&1u ? '+' : '-', |
| 703 | (fp->fpsr >> 27)&1u ? '+' : '-'); |
| 704 | printf(";;\t\terror: %cidc %cixc %cufc %cofc %cdzc %cioc\n", |
| 705 | (fp->fpsr >> 7)&1u ? '+' : '-', |
| 706 | (fp->fpsr >> 4)&1u ? '+' : '-', |
| 707 | (fp->fpsr >> 3)&1u ? '+' : '-', |
| 708 | (fp->fpsr >> 2)&1u ? '+' : '-', |
| 709 | (fp->fpsr >> 1)&1u ? '+' : '-', |
| 710 | (fp->fpsr >> 0)&1u ? '+' : '-'); |
| 711 | printf(";; %*s fpcr = 0x%08x\n", skip, "", fp->fpcr); |
| 712 | printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", |
| 713 | (fp->fpcr >> 15)&1u ? '+' : '-', |
| 714 | (fp->fpcr >> 12)&1u ? '+' : '-', |
| 715 | (fp->fpcr >> 11)&1u ? '+' : '-', |
| 716 | (fp->fpcr >> 10)&1u ? '+' : '-', |
| 717 | (fp->fpcr >> 9)&1u ? '+' : '-', |
| 718 | (fp->fpcr >> 8)&1u ? '+' : '-'); |
| 719 | printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n", |
| 720 | (fp->fpcr >> 26)&1u ? '+' : '-', |
| 721 | (fp->fpcr >> 25)&1u ? '+' : '-', |
| 722 | (fp->fpcr >> 24)&1u ? '+' : '-', |
| 723 | rcmap[(fp->fpcr >> 22)&3u], |
| 724 | (fp->fpcr >> 20)&3u, |
| 725 | (fp->fpcr >> 16)&7u); |
| 726 | } |
| 727 | |
| 728 | void regdump_gp(const struct regmap *map) |
| 729 | { |
| 730 | unsigned i; |
| 731 | |
| 732 | printf(";; General-purpose registers:\n"); |
| 733 | for (i = 0; i < 32; i++) |
| 734 | regdump(map, 0, |
| 735 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_64 | REGSRC_GP | i); |
| 736 | regdump(map, 0, REGF_HEX | REGF_64 | REGSRC_GP | REGIX_PC); |
| 737 | |
| 738 | printf(";; Flags:\n"); |
| 739 | regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_NZCV); |
| 740 | } |
| 741 | |
| 742 | void regdump_fp(const struct regmap *map) |
| 743 | { |
| 744 | unsigned i; |
| 745 | |
| 746 | printf(";; Floating-point/SIMD registers:\n"); |
| 747 | for (i = 0; i < 32; i++) |
| 748 | regdump(map, 0, |
| 749 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR | |
| 750 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | |
| 751 | REGSRC_SIMD | i | (7 << REGF_WDSHIFT)); |
| 752 | |
| 753 | printf(";; Floating-point state:\n"); |
| 754 | dump_fpflags(0, map->fp); |
| 755 | } |
| 756 | |
| 757 | void regdump_simd(const struct regmap *map) { ; } |
| 758 | |
| 759 | #endif |
| 760 | |
| 761 | /*----- The main entry point ----------------------------------------------*/ |
| 762 | |
| 763 | /* --- @regdump@ --- * |
| 764 | * |
| 765 | * Arguments: @const void *base@ = pointer to base structure, corresponding |
| 766 | * to the @REGF_SRCMASK@ part of @f@ |
| 767 | * @const char *lbl@ = label to print |
| 768 | * @uint32 f@ = format control word; see @REGF_...@ |
| 769 | * |
| 770 | * Returns: --- |
| 771 | * |
| 772 | * Use: Dump a register value, or chunk of memory. |
| 773 | * |
| 774 | * This function is not usually called directly; instead, use |
| 775 | * the `reg' or `mem' assembler macros. |
| 776 | */ |
| 777 | |
| 778 | void regdump(const void *base, const char *lbl, uint32 f) |
| 779 | { |
| 780 | unsigned ix = (f®F_IXMASK) >> REGF_IXSHIFT; |
| 781 | unsigned wd = 1 << ((f®F_WDMASK) >> REGF_WDSHIFT); |
| 782 | unsigned fmt, ty; |
| 783 | uint32 fmtbit, tybit; |
| 784 | const void *p; |
| 785 | char regbuf[8]; const char *reg = regname(regbuf, f); |
| 786 | const struct regmap *map; |
| 787 | const struct fmttab *tab; |
| 788 | struct fmtinfo fi; |
| 789 | int firstp = 1; |
| 790 | int skip; |
| 791 | size_t n; |
| 792 | |
| 793 | #if CPUFAM_X86 || CPUFAM_AMD64 |
| 794 | union vreg vr; |
| 795 | #endif |
| 796 | |
| 797 | if (reg) { |
| 798 | n = strlen(reg); |
| 799 | if (n < 7) { |
| 800 | memmove(regbuf + 7 - n, reg, n + 1); |
| 801 | memset(regbuf, ' ', 7 - n); |
| 802 | } |
| 803 | } |
| 804 | |
| 805 | switch (f®F_SRCMASK) { |
| 806 | case REGSRC_ABS: |
| 807 | p = base; |
| 808 | break; |
| 809 | |
| 810 | #if CPUFAM_X86 || CPUFAM_AMD64 |
| 811 | case REGSRC_GP: |
| 812 | map = (const struct regmap *)base; |
| 813 | if (ix == REGIX_FLAGS && !(f®F_FMTMASK)) |
| 814 | { dump_flags(lbl, reg, map->gp->gp[REGIX_FLAGS]); return; } |
| 815 | p = &map->gp->gp[ix]; |
| 816 | break; |
| 817 | case REGSRC_SEG: |
| 818 | map = (const struct regmap *)base; |
| 819 | assert(wd == 1); assert((f®F_TYMASK) == REGF_16); |
| 820 | p = &map->gp->seg[ix]; |
| 821 | break; |
| 822 | case REGSRC_STMMX: |
| 823 | map = (const struct regmap *)base; |
| 824 | if (ix == REGIX_FPFLAGS) |
| 825 | { assert(!(f®F_FMTMASK)); dump_fpflags(lbl, map->fx); return; } |
| 826 | if (!((map->fx->ftw << ix)&128u)) { |
| 827 | printf(";; "); |
| 828 | if (lbl) printf("%s: ", lbl); |
| 829 | if (reg) printf("%s = ", reg); |
| 830 | printf(" dead\n"); |
| 831 | return; |
| 832 | } |
| 833 | p = &map->fx->stmmx[ix]; |
| 834 | break; |
| 835 | case REGSRC_SIMD: |
| 836 | map = (const struct regmap *)base; |
| 837 | if (ix == REGIX_FPFLAGS) |
| 838 | { assert(!(f®F_FMTMASK)); dump_mxflags(lbl, map->fx); return; } |
| 839 | if (wd <= 128) |
| 840 | p = &map->fx->xmm[ix]; |
| 841 | else { |
| 842 | vr.v128[0] = map->fx->xmm[ix]; |
| 843 | vr.v128[1] = map->avx->ymmh[ix]; |
| 844 | assert(wd == 256); |
| 845 | p = &vr; |
| 846 | } |
| 847 | break; |
| 848 | #endif |
| 849 | |
| 850 | #if CPUFAM_ARMEL |
| 851 | case REGSRC_GP: |
| 852 | map = (const struct regmap *)base; |
| 853 | if (ix == REGIX_CPSR && !(f®F_FMTMASK)) |
| 854 | { dump_flags(lbl, map->gp->r[REGIX_CPSR].u32); return; } |
| 855 | p = &map->gp->r[ix]; |
| 856 | break; |
| 857 | case REGSRC_FP: |
| 858 | case REGSRC_SIMD: |
| 859 | map = (const struct regmap *)base; |
| 860 | if (ix == REGIX_FPSCR) { |
| 861 | assert(!(f®F_FMTMASK)); |
| 862 | dump_fpflags(lbl, map->fp->fpscr); |
| 863 | return; |
| 864 | } |
| 865 | switch (regwd(f)) { |
| 866 | case 32: p = &map->fp->u.s[ix]; break; |
| 867 | case 64: p = &map->fp->u.d[ix]; break; |
| 868 | case 128: p = &map->fp->u.q[ix]; break; |
| 869 | default: assert(0); |
| 870 | } |
| 871 | break; |
| 872 | #endif |
| 873 | |
| 874 | #if CPUFAM_ARM64 |
| 875 | case REGSRC_GP: |
| 876 | map = (const struct regmap *)base; |
| 877 | if (ix == REGIX_NZCV && !(f®F_FMTMASK)) |
| 878 | { dump_flags(lbl, map->gp->r[REGIX_NZCV].u64); return; } |
| 879 | p = &map->gp->r[ix]; |
| 880 | break; |
| 881 | case REGSRC_FP: |
| 882 | case REGSRC_SIMD: |
| 883 | map = (const struct regmap *)base; |
| 884 | if (ix == REGIX_FPFLAGS) |
| 885 | { assert(!(f®F_FMTMASK)); dump_fpflags(lbl, map->fp); return; } |
| 886 | p = &map->fp->v[ix]; |
| 887 | break; |
| 888 | #endif |
| 889 | |
| 890 | default: |
| 891 | assert(0); |
| 892 | } |
| 893 | |
| 894 | skip = (lbl ? strlen(lbl) + 2 : 0) + (reg ? strlen(reg) : 0); |
| 895 | fi.f = 0; if (wd > 1) fi.f |= FMTF_VECTOR; |
| 896 | |
| 897 | for (ty = (f®F_TYMASK) >> REGF_TYSHIFT, |
| 898 | tybit = 1 << REGF_TYSHIFT; |
| 899 | ty; |
| 900 | ty >>= 1, tybit <<= 1) { |
| 901 | if (!(ty&1u)) continue; |
| 902 | |
| 903 | for (fmt = (f®F_FMTMASK) >> REGF_FMTSHIFT, |
| 904 | fmtbit = 1 << REGF_FMTSHIFT; |
| 905 | fmt; |
| 906 | fmt >>= 1, fmtbit <<= 1) { |
| 907 | |
| 908 | if (!(fmt&1u)) continue; |
| 909 | |
| 910 | for (tab = fmttab; tab->mask; tab++) |
| 911 | if (tab->mask == (fmtbit | tybit)) goto found; |
| 912 | continue; |
| 913 | found: |
| 914 | |
| 915 | if (firstp) { |
| 916 | printf(";;"); |
| 917 | if (lbl) printf(" %s:", lbl); |
| 918 | if (reg) printf(" %s =", reg); |
| 919 | firstp = 0; |
| 920 | } else if (wd > 1) |
| 921 | printf("\n;; %*s =", skip, ""); |
| 922 | else |
| 923 | fputs(" =", stdout); |
| 924 | |
| 925 | fi.p = p; fi.wd = 0; |
| 926 | while (fi.wd < wd) { putchar(' '); tab->fmt(&fi); } |
| 927 | } |
| 928 | } |
| 929 | putchar('\n'); |
| 930 | } |
| 931 | |
| 932 | /*----- Other random utilities --------------------------------------------*/ |
| 933 | |
| 934 | /* --- @regdump_freshline@ --- * |
| 935 | * |
| 936 | * Arguments: --- |
| 937 | * |
| 938 | * Returns: --- |
| 939 | * |
| 940 | * Use: Begin a fresh line of output. |
| 941 | */ |
| 942 | |
| 943 | void regdump_freshline(void) { putchar('\n'); } |
| 944 | |
| 945 | /*----- That's all, folks -------------------------------------------------*/ |