vestigia

Detect where execution depends on secrets.
git clone git://git.ppad.tech/vestigia.git
Log | Files | Refs | README | LICENSE

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 }