vestigia-qemu.c (28656B)
1 /* 2 * vestigia-qemu.c: a QEMU user-mode plugin recording a run's leakage 3 * trace, summarised per instruction. 4 * 5 * Copyright (c) 2026 Jared Tobin 6 * License: MIT 7 * 8 * For every guest instruction executed it records how many times it 9 * ran, every non-sequential transfer out of it (a branch taken, a 10 * call, a jump, a return) with its count, an order-sensitive 11 * fingerprint of the data addresses it touched, and an order- 12 * sensitive fingerprint of the instructions that followed it. Two 13 * runs of a program that differ only in a secret then differ exactly 14 * where the execution depended on the secret: a transfer whose counts 15 * differ is a secret-dependent branch, as is one taking the same 16 * transfers as often in another order, and an instruction run as 17 * often but touching other addresses computed one from the secret. 18 * Nothing needs aligning, and no taint is propagated, so effects of 19 * control flow are seen as well as those of data flow. 20 * 21 * It reads each instruction's bytes, x86_64's or aarch64's, for three 22 * kinds. A return goes where the call before it sent it, and is 23 * marked. An integer division takes a time that depends on its 24 * operands, so it gets an order-sensitive fingerprint of their 25 * values: the dividend and a register divisor read before it runs, 26 * and a divisor in memory as it is loaded. A division run as often 27 * but on other operands computed them from the secret. And a call, 28 * with the returns, keeps a shadow stack of the calls in progress, 29 * so that an instruction that could be an origin (one ending a 30 * translation block, as every branch does, one touching memory, or a 31 * division) is given the contexts it ran in: the chains of calls, by 32 * their addresses, that reached it, up to CONTEXTS of them. 33 * 34 * Given a watch list (its `watch` argument, written by `vestigia 35 * record` under a data-independent-timing contract), it also 36 * fingerprints the operands of every instruction the list names: each 37 * one outside the contract, by its object and address there, with the 38 * registers it reads, fingerprinted before it runs, and any value it 39 * loads or stores, as it is accessed. One run as often but on other 40 * operands computed them from the secret. The list names divisions 41 * too, for their text alone: the plugin reads a division's operands 42 * from its bytes, as above. A list is read whole: 43 * 44 * O <object> 45 * D <address> <text> 46 * U <address> <memory> <n> <register>:<low>:<width>... <text> 47 * 48 * in hex where an address, each register by the name QEMU's debugger 49 * stub gives it, the bits read from its low bit; <memory> is 1 where 50 * the instruction names memory. 51 * 52 * At exit it writes, to the file named by its `out` argument: 53 * 54 * I <pc> <count> <accesses> <fingerprint> <order> <object> <address> 55 * E <from> <to> <count> 56 * R <pc> 57 * D <pc> <operands> 58 * U <pc> <operands> 59 * W <pc> <text> 60 * C <context> <caller> <call> 61 * K <pc> <partial> <context>... 62 * 63 * in hex, where <object> is the file the instruction's bytes were 64 * mapped from ("-" if none, or if the file's program headers cannot 65 * be read) and <address> its address in that file: where the file's 66 * program headers place it, as its disassembly and line tables name 67 * it. In a shared library that is often the instruction's offset in 68 * the file; in an executable linked at a fixed address it is not. A 69 * context is the call at <call> made in context <caller>, context 0 70 * being the program's own; <partial> is 1 where an instruction ran in 71 * more contexts than are kept. A guest that started a second thread 72 * gets the line `T 2` alone. 73 * 74 * The state below is unlocked, so one guest thread is recorded: a 75 * guest that starts a second one is stopped there, its dump saying 76 * so, rather than recorded by threads racing on it. 77 */ 78 #include <elf.h> 79 #include <glib.h> 80 #include <inttypes.h> 81 #include <stdio.h> 82 #include <stdlib.h> 83 #include <string.h> 84 #include <unistd.h> 85 86 #include <qemu-plugin.h> 87 88 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; 89 90 /* what an instruction is to a comparison, read from its bytes, or 91 from the watch list (OUTSIDE) */ 92 enum { PLAIN, RETURN, DIVISION, CALL, OUTSIDE }; 93 94 /* the most contexts kept for one instruction */ 95 #define CONTEXTS 16 96 97 /* registers by number: x86_64's in ModRM order, aarch64's X0 to X30; 98 aarch64's 31 is the zero register where a division names it */ 99 enum { RAX = 0, RDX = 2, ZR = 31, GPRS = 32 }; 100 101 typedef struct Decoded { 102 int kind; 103 /* a division's: its width in bits; on aarch64 its two registers, 104 on x86_64 its divisor's (-1 in memory), that register's second 105 byte where `high`, the dividend being RDX:RAX or AX */ 106 int width, a, b, high; 107 } Decoded; 108 109 /* an instruction the watch list names */ 110 typedef struct Watch { 111 int division; /* a division, named for its text alone */ 112 int nregs; 113 char **names; /* the registers it reads, by QEMU's names */ 114 int *low, *width; /* the bits read of each */ 115 struct qemu_plugin_register **handles; 116 char *text; 117 } Watch; 118 119 typedef struct Insn { 120 uint64_t pc; 121 uint64_t size; 122 uintptr_t haddr; 123 Decoded d; 124 uint64_t count; 125 uint64_t accesses; 126 uint64_t fingerprint; 127 uint64_t order; /* the successors, in the order they followed */ 128 uint64_t operands; /* a division's or a watched instruction's 129 operands, in order */ 130 const Watch *watch; /* where the watch list names it */ 131 GHashTable *succ; /* target pc -> count, for transfers */ 132 uint32_t last; /* the context it last ran in */ 133 uint32_t contexts[CONTEXTS]; 134 int ncontexts, partial; 135 int origin; /* it ends a block or divides: it could be one */ 136 /* a call's: the context it was last made in, and the one it made */ 137 uint32_t made_in, made; 138 } Insn; 139 140 /* a context: the context a call was made in, and the call */ 141 typedef struct Context { 142 uint32_t caller; 143 uint64_t call; 144 } Context; 145 146 /* a call in progress: where it returns, and the context it left */ 147 typedef struct Call { 148 uint64_t ret; 149 uint32_t left; 150 } Call; 151 152 static GHashTable *insns; /* pc -> Insn */ 153 static Insn *prev; /* the instruction executed last */ 154 static char *out_path; 155 static gint vcpus; /* the guest's threads, a vCPU each */ 156 static gboolean aarch64; /* the guest's ISA: aarch64, else x86_64 */ 157 static GArray *contexts; /* Context by number, 0 the program's own */ 158 static GHashTable *interned; /* Context -> its number */ 159 static GArray *calls; /* the calls in progress, innermost last */ 160 static uint32_t current; /* the context the guest runs in */ 161 static struct qemu_plugin_register *gpr[GPRS]; 162 static GByteArray *regbuf; 163 static GHashTable *watches; /* "<object>\n<address>" -> Watch */ 164 static GHashTable *registers; /* QEMU's name -> its handle */ 165 static GArray *regions; /* the host's mappings, as last read */ 166 static GHashTable *segments; /* a file's path -> its Segments */ 167 168 static const uint64_t fnv_offset = 0xcbf29ce484222325ULL; 169 static const uint64_t fnv_prime = 0x100000001b3ULL; 170 171 static Decoded decode_aarch64(const uint8_t *b, size_t n) { 172 Decoded d = { PLAIN, 0, 0, 0, 0 }; 173 uint32_t w; 174 if (n < 4) return d; 175 w = (uint32_t)b[0] | (uint32_t)b[1] << 8 | (uint32_t)b[2] << 16 176 | (uint32_t)b[3] << 24; 177 /* RET Xn, RETAA, RETAB */ 178 if ((w & 0xfffffc1f) == 0xd65f0000 || w == 0xd65f0bff 179 || w == 0xd65f0fff) 180 d.kind = RETURN; 181 /* BL, BLR, BLRAAZ, BLRABZ, BLRAA, BLRAB */ 182 else if ((w & 0xfc000000) == 0x94000000 183 || (w & 0xfffffc1f) == 0xd63f0000 184 || (w & 0xfffff81f) == 0xd63f081f 185 || (w & 0xfffff800) == 0xd73f0800) 186 d.kind = CALL; 187 /* UDIV, SDIV */ 188 else if ((w & 0x7fe0f800) == 0x1ac00800) { 189 d.kind = DIVISION; 190 d.width = (w >> 31) ? 64 : 32; 191 d.a = (int)((w >> 5) & 31); 192 d.b = (int)((w >> 16) & 31); 193 } 194 return d; 195 } 196 197 static Decoded decode_x86_64(const uint8_t *b, size_t n) { 198 Decoded d = { PLAIN, 0, 0, 0, 0 }; 199 size_t k = 0; 200 int opsize = 0, rex = 0; 201 /* past the legacy prefixes, then a REX */ 202 while (k < n && (b[k] == 0x66 || b[k] == 0x67 || b[k] == 0xf0 203 || b[k] == 0xf2 || b[k] == 0xf3 || b[k] == 0x2e 204 || b[k] == 0x36 || b[k] == 0x3e || b[k] == 0x26 205 || b[k] == 0x64 || b[k] == 0x65)) { 206 if (b[k] == 0x66) opsize = 1; 207 k++; 208 } 209 if (k < n && (b[k] & 0xf0) == 0x40) rex = b[k++]; 210 if (k >= n) return d; 211 /* RET, RET imm16 */ 212 if (b[k] == 0xc3 || b[k] == 0xc2) d.kind = RETURN; 213 /* CALL rel32, CALL r/m: FF with ModRM's reg 2 */ 214 else if (b[k] == 0xe8 215 || (b[k] == 0xff && k + 1 < n && ((b[k + 1] >> 3) & 7) == 2)) 216 d.kind = CALL; 217 /* DIV, IDIV: F6 or F7 with ModRM's reg 6 or 7 */ 218 else if ((b[k] == 0xf6 || b[k] == 0xf7) && k + 1 < n 219 && ((b[k + 1] >> 3) & 6) == 6) { 220 int modrm = b[k + 1], rm = modrm & 7; 221 d.kind = DIVISION; 222 d.width = b[k] == 0xf6 ? 8 : (rex & 8) ? 64 : opsize ? 16 : 32; 223 if ((modrm >> 6) != 3) d.a = -1; 224 /* without REX, a byte register 4 to 7 is AH, CH, DH, or BH */ 225 else if (d.width == 8 && rex == 0 && rm >= 4) { 226 d.a = rm - 4; 227 d.high = 1; 228 } else 229 d.a = rm | ((rex & 1) << 3); 230 } 231 return d; 232 } 233 234 static Insn *insn_at(uint64_t pc, uint64_t size, uintptr_t haddr, 235 Decoded d) { 236 Insn *i = g_hash_table_lookup(insns, &pc); 237 if (i == NULL) { 238 i = g_new0(Insn, 1); 239 i->pc = pc; 240 i->size = size; 241 i->haddr = haddr; 242 i->d = d; 243 i->fingerprint = fnv_offset; 244 i->order = fnv_offset; 245 i->operands = fnv_offset; 246 i->last = UINT32_MAX; 247 i->made_in = UINT32_MAX; 248 i->succ = g_hash_table_new_full(g_int64_hash, g_int64_equal, 249 g_free, g_free); 250 g_hash_table_insert(insns, &i->pc, i); 251 } 252 return i; 253 } 254 255 static void transfer(Insn *from, uint64_t to) { 256 uint64_t *n = g_hash_table_lookup(from->succ, &to); 257 if (n == NULL) { 258 uint64_t *k = g_new(uint64_t, 1); 259 *k = to; 260 n = g_new0(uint64_t, 1); 261 g_hash_table_insert(from->succ, k, n); 262 } 263 (*n)++; 264 } 265 266 static guint context_hash(gconstpointer p) { 267 const Context *c = p; 268 return (guint)((c->call * 0x9e3779b97f4a7c15ULL) >> 32) ^ c->caller; 269 } 270 271 static gboolean context_equal(gconstpointer a, gconstpointer b) { 272 const Context *x = a, *y = b; 273 return x->caller == y->caller && x->call == y->call; 274 } 275 276 static uint32_t context_of(uint32_t caller, uint64_t call) { 277 Context c = { caller, call }; 278 gpointer v; 279 if (g_hash_table_lookup_extended(interned, &c, NULL, &v)) 280 return GPOINTER_TO_UINT(v); 281 uint32_t n = contexts->len; 282 g_array_append_val(contexts, c); 283 g_hash_table_insert(interned, g_memdup2(&c, sizeof c), 284 GUINT_TO_POINTER(n)); 285 return n; 286 } 287 288 /* a call made: it returns past itself */ 289 static void enter(Insn *call) { 290 Call c = { call->pc + call->size, current }; 291 g_array_append_val(calls, c); 292 if (call->made_in != current) { 293 call->made_in = current; 294 call->made = context_of(current, call->pc); 295 } 296 current = call->made; 297 } 298 299 /* a return made, to pc: the innermost call returning there ends, and 300 any inside it that never returned; a return no call made leaves 301 the stack as it is */ 302 static void leave(uint64_t pc) { 303 for (guint k = calls->len; k > 0; k--) { 304 const Call *c = &g_array_index(calls, Call, k - 1); 305 if (c->ret == pc) { 306 current = c->left; 307 g_array_set_size(calls, k - 1); 308 return; 309 } 310 } 311 } 312 313 static void note_context(Insn *i) { 314 i->last = current; 315 /* once more have been seen than are kept, no more is learnt */ 316 if (i->partial) return; 317 for (int k = 0; k < i->ncontexts; k++) 318 if (i->contexts[k] == current) return; 319 if (i->ncontexts < CONTEXTS) 320 i->contexts[i->ncontexts++] = current; 321 else 322 i->partial = 1; 323 } 324 325 static void on_exec(unsigned int vcpu, void *udata) { 326 Insn *i = udata; 327 (void)vcpu; 328 if (prev != NULL) { 329 prev->order = (prev->order ^ i->pc) * fnv_prime; 330 if (i->pc != prev->pc + prev->size) transfer(prev, i->pc); 331 if (prev->d.kind == CALL) enter(prev); 332 else if (prev->d.kind == RETURN) leave(i->pc); 333 } 334 prev = i; 335 i->count++; 336 if (i->origin && i->last != current) note_context(i); 337 } 338 339 static void mix(Insn *i, uint64_t v) { 340 i->operands = (i->operands ^ v) * fnv_prime; 341 } 342 343 static uint64_t read_gpr(int r) { 344 uint64_t v = 0; 345 int n; 346 if (aarch64 && r == ZR) return 0; 347 g_byte_array_set_size(regbuf, 0); 348 n = qemu_plugin_read_register(gpr[r], regbuf); 349 /* in the guest's byte order, little-endian as the host's */ 350 if (n > 0) memcpy(&v, regbuf->data, MIN((size_t)n, sizeof v)); 351 return v; 352 } 353 354 static void on_divide(unsigned int vcpu, void *udata) { 355 Insn *i = udata; 356 uint64_t mask = i->d.width == 64 ? ~0ULL : (1ULL << i->d.width) - 1; 357 (void)vcpu; 358 if (aarch64) { 359 mix(i, read_gpr(i->d.a) & mask); 360 mix(i, read_gpr(i->d.b) & mask); 361 return; 362 } 363 if (i->d.width == 8) 364 mix(i, read_gpr(RAX) & 0xffff); 365 else { 366 mix(i, read_gpr(RAX) & mask); 367 mix(i, read_gpr(RDX) & mask); 368 } 369 /* a divisor in memory is mixed in as it is loaded */ 370 if (i->d.a >= 0) 371 mix(i, (read_gpr(i->d.a) >> (i->d.high ? 8 : 0)) & mask); 372 } 373 374 /* a watched instruction: each register it reads, its bits from the 375 low one, mixed in eight bytes at a time */ 376 static void on_outside(unsigned int vcpu, void *udata) { 377 Insn *i = udata; 378 const Watch *w = i->watch; 379 (void)vcpu; 380 for (int k = 0; k < w->nregs; k++) { 381 int n, from = w->low[k] / 8, to = (w->low[k] + w->width[k]) / 8; 382 g_byte_array_set_size(regbuf, 0); 383 n = qemu_plugin_read_register(w->handles[k], regbuf); 384 if (to > n) to = n; 385 for (int b = from; b < to; b += 8) { 386 uint64_t v = 0; 387 memcpy(&v, regbuf->data + b, MIN((size_t)(to - b), sizeof v)); 388 mix(i, v); 389 } 390 } 391 } 392 393 static void on_mem(unsigned int vcpu, qemu_plugin_meminfo_t info, 394 uint64_t vaddr, void *udata) { 395 Insn *i = udata; 396 (void)vcpu; 397 i->fingerprint = (i->fingerprint ^ vaddr) * fnv_prime; 398 i->accesses++; 399 if (i->last != current) note_context(i); 400 if (i->d.kind == DIVISION || i->d.kind == OUTSIDE) { 401 qemu_plugin_mem_value v = qemu_plugin_mem_get_value(info); 402 switch (v.type) { 403 case QEMU_PLUGIN_MEM_VALUE_U8: mix(i, v.data.u8); break; 404 case QEMU_PLUGIN_MEM_VALUE_U16: mix(i, v.data.u16); break; 405 case QEMU_PLUGIN_MEM_VALUE_U32: mix(i, v.data.u32); break; 406 case QEMU_PLUGIN_MEM_VALUE_U64: mix(i, v.data.u64); break; 407 case QEMU_PLUGIN_MEM_VALUE_U128: 408 mix(i, v.data.u128.low); 409 mix(i, v.data.u128.high); 410 break; 411 } 412 } 413 } 414 415 /* the general registers a division reads, by the names QEMU's 416 debugger stub gives them */ 417 static void find_registers(void) { 418 static const char *x86_64[16] = { 419 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", 420 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }; 421 GArray *rs = qemu_plugin_get_registers(); 422 int wanted = aarch64 ? 31 : 16; 423 for (guint k = 0; k < rs->len; k++) { 424 qemu_plugin_reg_descriptor *r = 425 &g_array_index(rs, qemu_plugin_reg_descriptor, k); 426 g_hash_table_insert(registers, g_strdup(r->name), r->handle); 427 for (int j = 0; j < wanted; j++) { 428 char name[8]; 429 if (aarch64) g_snprintf(name, sizeof name, "x%d", j); 430 if (strcmp(r->name, aarch64 ? name : x86_64[j]) == 0) 431 gpr[j] = r->handle; 432 } 433 } 434 g_array_free(rs, TRUE); 435 for (int j = 0; j < wanted; j++) 436 if (gpr[j] == NULL) { 437 fprintf(stderr, "vestigia-qemu: QEMU names no register %d, by " 438 "which a division's operands are read\n", j); 439 _exit(EXIT_FAILURE); 440 } 441 /* every register the watch list reads, or none of the run */ 442 GHashTableIter it; 443 gpointer key, val; 444 g_hash_table_iter_init(&it, watches); 445 while (g_hash_table_iter_next(&it, &key, &val)) { 446 Watch *w = val; 447 for (int k = 0; k < w->nregs; k++) { 448 w->handles[k] = g_hash_table_lookup(registers, w->names[k]); 449 /* a CPU with SVE names its SIMD&FP registers by their scalable 450 views, whose low 128 bits they are */ 451 if (w->handles[k] == NULL && w->names[k][0] == 'v') { 452 char *z = g_strdup_printf("z%s", w->names[k] + 1); 453 w->handles[k] = g_hash_table_lookup(registers, z); 454 g_free(z); 455 } 456 if (w->handles[k] == NULL) { 457 fprintf(stderr, "vestigia-qemu: QEMU names no register %s, " 458 "which the watch list reads\n", w->names[k]); 459 _exit(EXIT_FAILURE); 460 } 461 } 462 } 463 } 464 465 static void on_vcpu_init(qemu_plugin_id_t id, unsigned int vcpu) { 466 (void)id; 467 (void)vcpu; 468 if (g_atomic_int_add(&vcpus, 1) == 0) 469 find_registers(); 470 else { 471 FILE *out = fopen(out_path, "w"); 472 if (out != NULL) { 473 fprintf(out, "T 2\n"); 474 fclose(out); 475 } 476 fprintf(stderr, "vestigia-qemu: the guest started a second thread, " 477 "which a trace cannot record; the run is stopped\n"); 478 _exit(EXIT_FAILURE); 479 } 480 } 481 482 static const Watch *watched(uintptr_t haddr); 483 484 static void on_translate(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { 485 size_t n = qemu_plugin_tb_n_insns(tb); 486 (void)id; 487 for (size_t k = 0; k < n; k++) { 488 struct qemu_plugin_insn *qi = qemu_plugin_tb_get_insn(tb, k); 489 uint8_t bytes[16]; 490 size_t len = qemu_plugin_insn_data(qi, bytes, sizeof bytes); 491 Insn *i = insn_at(qemu_plugin_insn_vaddr(qi), 492 qemu_plugin_insn_size(qi), 493 (uintptr_t)qemu_plugin_insn_haddr(qi), 494 aarch64 ? decode_aarch64(bytes, len) 495 : decode_x86_64(bytes, len)); 496 /* an instruction the watch list names: a division its text, any 497 other its registers; one translated anew keeps what it had */ 498 if (i->watch == NULL && g_hash_table_size(watches) > 0) { 499 const Watch *w = watched(i->haddr); 500 if (w != NULL && (i->d.kind == DIVISION || !w->division)) { 501 i->watch = w; 502 if (i->d.kind != DIVISION) i->d.kind = OUTSIDE; 503 } 504 } 505 if (k == n - 1 || i->d.kind == DIVISION || i->d.kind == OUTSIDE) 506 i->origin = 1; 507 qemu_plugin_register_vcpu_insn_exec_cb(qi, on_exec, 508 QEMU_PLUGIN_CB_NO_REGS, i); 509 if (i->d.kind == DIVISION) 510 qemu_plugin_register_vcpu_insn_exec_cb(qi, on_divide, 511 QEMU_PLUGIN_CB_R_REGS, i); 512 else if (i->d.kind == OUTSIDE) 513 qemu_plugin_register_vcpu_insn_exec_cb(qi, on_outside, 514 QEMU_PLUGIN_CB_R_REGS, i); 515 qemu_plugin_register_vcpu_mem_cb(qi, on_mem, QEMU_PLUGIN_CB_NO_REGS, 516 QEMU_PLUGIN_MEM_RW, i); 517 } 518 } 519 520 /* the host's file-backed mappings, for naming an instruction's file */ 521 typedef struct Region { 522 uintptr_t start, end; 523 uint64_t offset; 524 char *path; 525 } Region; 526 527 static GArray *read_maps(void) { 528 GArray *rs = g_array_new(FALSE, FALSE, sizeof(Region)); 529 FILE *f = fopen("/proc/self/maps", "r"); 530 char line[4096]; 531 if (f == NULL) return rs; 532 while (fgets(line, sizeof line, f) != NULL) { 533 uintptr_t start, end; 534 uint64_t offset; 535 char perms[8], dev[16]; 536 unsigned long inode; 537 int used = 0; 538 if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %7s %" SCNx64 539 " %15s %lu %n", &start, &end, perms, &offset, dev, 540 &inode, &used) < 6) 541 continue; 542 char *path = line + used; 543 path[strcspn(path, "\n")] = '\0'; 544 if (inode == 0 || path[0] != '/') continue; 545 Region r = { start, end, offset, g_strdup(path) }; 546 g_array_append_val(rs, r); 547 } 548 fclose(f); 549 return rs; 550 } 551 552 static const Region *region_of(GArray *rs, uintptr_t a) { 553 for (guint k = 0; k < rs->len; k++) { 554 const Region *r = &g_array_index(rs, Region, k); 555 if (a >= r->start && a < r->end) return r; 556 } 557 return NULL; 558 } 559 560 /* a file's loadable segments, by which a file offset is placed at 561 the address the file's own disassembly gives it */ 562 typedef struct Segments { 563 guint n; 564 Elf64_Phdr *load; 565 } Segments; 566 567 static Segments *read_segments(const char *path) { 568 FILE *f = fopen(path, "rb"); 569 Elf64_Ehdr eh; 570 Segments *s = NULL; 571 if (f == NULL) return NULL; 572 /* a little-endian ELF64 object, as x86_64 and aarch64 build */ 573 if (fread(&eh, sizeof eh, 1, f) != 1 574 || memcmp(eh.e_ident, ELFMAG, SELFMAG) != 0 575 || eh.e_ident[EI_CLASS] != ELFCLASS64 576 || eh.e_ident[EI_DATA] != ELFDATA2LSB 577 || eh.e_phentsize != sizeof(Elf64_Phdr) 578 || fseek(f, (long)eh.e_phoff, SEEK_SET) != 0) { 579 fclose(f); 580 return NULL; 581 } 582 s = g_new0(Segments, 1); 583 s->load = g_new0(Elf64_Phdr, eh.e_phnum); 584 for (guint k = 0; k < eh.e_phnum; k++) { 585 Elf64_Phdr ph; 586 if (fread(&ph, sizeof ph, 1, f) != 1) { 587 g_free(s->load); 588 g_free(s); 589 fclose(f); 590 return NULL; 591 } 592 if (ph.p_type == PT_LOAD) s->load[s->n++] = ph; 593 } 594 fclose(f); 595 return s; 596 } 597 598 /* the segments of the file at a path, read once; NULL if they cannot 599 be read, which is said once */ 600 static Segments *segments_of(GHashTable *cache, const char *path) { 601 gpointer v; 602 if (g_hash_table_lookup_extended(cache, path, NULL, &v)) return v; 603 Segments *s = read_segments(path); 604 if (s == NULL) 605 fprintf(stderr, "vestigia-qemu: cannot read the program headers " 606 "of %s; its instructions are named by no object\n", path); 607 g_hash_table_insert(cache, g_strdup(path), s); 608 return s; 609 } 610 611 /* where a file's segments place the byte at a file offset */ 612 static gboolean address_in(const Segments *s, uint64_t off, 613 uint64_t *addr) { 614 for (guint k = 0; k < s->n; k++) { 615 const Elf64_Phdr *p = &s->load[k]; 616 if (off >= p->p_offset && off - p->p_offset < p->p_filesz) { 617 *addr = p->p_vaddr + (off - p->p_offset); 618 return TRUE; 619 } 620 } 621 return FALSE; 622 } 623 624 /* the watch list's entry for the instruction at a host address: its 625 file and address there, the mappings read anew where the address 626 lies in none known */ 627 static const Watch *watched(uintptr_t haddr) { 628 const Region *r = region_of(regions, haddr); 629 Segments *s; 630 uint64_t addr; 631 if (r == NULL) { 632 for (guint k = 0; k < regions->len; k++) 633 g_free(g_array_index(regions, Region, k).path); 634 g_array_free(regions, TRUE); 635 regions = read_maps(); 636 r = region_of(regions, haddr); 637 } 638 if (r == NULL) return NULL; 639 s = segments_of(segments, r->path); 640 if (s == NULL 641 || !address_in(s, (uint64_t)(haddr - r->start) + r->offset, &addr)) 642 return NULL; 643 char *key = g_strdup_printf("%s\n%" PRIx64, r->path, addr); 644 const Watch *w = g_hash_table_lookup(watches, key); 645 g_free(key); 646 return w; 647 } 648 649 /* read a watch list whole; a line it cannot read stops the run */ 650 static gboolean read_watches(const char *path) { 651 gchar *text = NULL; 652 gchar *object = NULL; 653 if (!g_file_get_contents(path, &text, NULL, NULL)) { 654 fprintf(stderr, "vestigia-qemu: cannot read the watch list %s\n", 655 path); 656 return FALSE; 657 } 658 gchar **lines = g_strsplit(text, "\n", -1); 659 gboolean ok = TRUE; 660 for (gchar **l = lines; ok && *l != NULL; l++) { 661 gchar *line = *l; 662 if (line[0] == '\0') continue; 663 if (line[0] == 'O' && line[1] == ' ') { 664 g_free(object); 665 object = g_strdup(line + 2); 666 continue; 667 } 668 if (object == NULL || (line[0] != 'D' && line[0] != 'U') 669 || line[1] != ' ') { 670 ok = FALSE; 671 break; 672 } 673 char *p = line + 2, *end; 674 uint64_t addr = g_ascii_strtoull(p, &end, 16); 675 Watch *w = g_new0(Watch, 1); 676 if (end == p || *end != ' ') ok = FALSE; 677 p = end + 1; 678 if (ok && line[0] == 'D') 679 w->division = 1; 680 else if (ok) { 681 /* <memory> <n> then n registers */ 682 if ((p[0] != '0' && p[0] != '1') || p[1] != ' ') ok = FALSE; 683 p += 2; 684 w->nregs = ok ? (int)g_ascii_strtoll(p, &end, 10) : 0; 685 if (end == p || *end != ' ' || w->nregs < 0 || w->nregs > 16) 686 ok = FALSE; 687 p = end + 1; 688 w->names = g_new0(char *, w->nregs); 689 w->low = g_new0(int, w->nregs); 690 w->width = g_new0(int, w->nregs); 691 w->handles = g_new0(struct qemu_plugin_register *, w->nregs); 692 for (int k = 0; ok && k < w->nregs; k++) { 693 char *sp = strchr(p, ' '); 694 if (sp == NULL) { 695 ok = FALSE; 696 break; 697 } 698 gchar *spec = g_strndup(p, (gsize)(sp - p)); 699 gchar **f = g_strsplit(spec, ":", 3); 700 g_free(spec); 701 if (g_strv_length(f) != 3) ok = FALSE; 702 else { 703 w->names[k] = g_strdup(f[0]); 704 w->low[k] = atoi(f[1]); 705 w->width[k] = atoi(f[2]); 706 if (w->low[k] % 8 != 0 || w->width[k] % 8 != 0 707 || w->width[k] <= 0) 708 ok = FALSE; 709 } 710 g_strfreev(f); 711 p = sp + 1; 712 } 713 } 714 if (!ok) break; 715 w->text = g_strdup(p); 716 g_hash_table_insert(watches, 717 g_strdup_printf("%s\n%" PRIx64, object, addr), w); 718 } 719 if (!ok) 720 fprintf(stderr, "vestigia-qemu: %s: an unreadable watch list\n", 721 path); 722 g_strfreev(lines); 723 g_free(object); 724 g_free(text); 725 return ok; 726 } 727 728 static void at_exit(qemu_plugin_id_t id, void *p) { 729 (void)id; 730 (void)p; 731 FILE *out = fopen(out_path, "w"); 732 if (out == NULL) { 733 fprintf(stderr, "vestigia-qemu: cannot write %s\n", out_path); 734 return; 735 } 736 GArray *rs = read_maps(); 737 GHashTable *segs = g_hash_table_new(g_str_hash, g_str_equal); 738 GHashTableIter it; 739 gpointer key, val; 740 g_hash_table_iter_init(&it, insns); 741 while (g_hash_table_iter_next(&it, &key, &val)) { 742 Insn *i = val; 743 const Region *r = region_of(rs, i->haddr); 744 Segments *s = r != NULL ? segments_of(segs, r->path) : NULL; 745 uint64_t addr; 746 if (s != NULL 747 && address_in(s, (uint64_t)(i->haddr - r->start) + r->offset, 748 &addr)) 749 fprintf(out, "I %" PRIx64 " %" PRIx64 " %" PRIx64 " %016" PRIx64 750 " %016" PRIx64 " %s %" PRIx64 "\n", i->pc, i->count, 751 i->accesses, i->fingerprint, i->order, r->path, addr); 752 else 753 fprintf(out, "I %" PRIx64 " %" PRIx64 " %" PRIx64 " %016" PRIx64 754 " %016" PRIx64 " - 0\n", i->pc, i->count, i->accesses, 755 i->fingerprint, i->order); 756 if (i->d.kind == RETURN) fprintf(out, "R %" PRIx64 "\n", i->pc); 757 if (i->d.kind == DIVISION) 758 fprintf(out, "D %" PRIx64 " %016" PRIx64 "\n", i->pc, i->operands); 759 if (i->d.kind == OUTSIDE) 760 fprintf(out, "U %" PRIx64 " %016" PRIx64 "\n", i->pc, i->operands); 761 if (i->watch != NULL) 762 fprintf(out, "W %" PRIx64 " %s\n", i->pc, i->watch->text); 763 if (i->ncontexts > 0) { 764 fprintf(out, "K %" PRIx64 " %d", i->pc, i->partial); 765 for (int k = 0; k < i->ncontexts; k++) 766 fprintf(out, " %" PRIx32, i->contexts[k]); 767 fprintf(out, "\n"); 768 } 769 GHashTableIter jt; 770 gpointer tk, tn; 771 g_hash_table_iter_init(&jt, i->succ); 772 while (g_hash_table_iter_next(&jt, &tk, &tn)) 773 fprintf(out, "E %" PRIx64 " %" PRIx64 " %" PRIx64 "\n", i->pc, 774 *(uint64_t *)tk, *(uint64_t *)tn); 775 } 776 for (guint k = 1; k < contexts->len; k++) { 777 const Context *c = &g_array_index(contexts, Context, k); 778 fprintf(out, "C %x %" PRIx32 " %" PRIx64 "\n", k, c->caller, 779 c->call); 780 } 781 fclose(out); 782 } 783 784 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, 785 const qemu_info_t *info, 786 int argc, char **argv) { 787 const char *watch_path = NULL; 788 aarch64 = strcmp(info->target_name, "aarch64") == 0; 789 for (int k = 0; k < argc; k++) 790 if (strncmp(argv[k], "out=", 4) == 0) 791 out_path = g_strdup(argv[k] + 4); 792 else if (strncmp(argv[k], "watch=", 6) == 0) 793 watch_path = argv[k] + 6; 794 if (out_path == NULL) { 795 fprintf(stderr, "vestigia-qemu: an out=FILE argument is needed\n"); 796 return -1; 797 } 798 insns = g_hash_table_new(g_int64_hash, g_int64_equal); 799 regbuf = g_byte_array_new(); 800 watches = g_hash_table_new(g_str_hash, g_str_equal); 801 registers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, 802 NULL); 803 regions = g_array_new(FALSE, FALSE, sizeof(Region)); 804 segments = g_hash_table_new(g_str_hash, g_str_equal); 805 if (watch_path != NULL && !read_watches(watch_path)) return -1; 806 contexts = g_array_new(FALSE, FALSE, sizeof(Context)); 807 interned = g_hash_table_new_full(context_hash, context_equal, g_free, 808 NULL); 809 calls = g_array_new(FALSE, FALSE, sizeof(Call)); 810 { 811 Context own = { 0, 0 }; 812 g_array_append_val(contexts, own); 813 } 814 qemu_plugin_register_vcpu_init_cb(id, on_vcpu_init); 815 qemu_plugin_register_vcpu_tb_trans_cb(id, on_translate); 816 qemu_plugin_register_atexit_cb(id, at_exit, NULL); 817 return 0; 818 }