Live Analysis: from a running process to the code that writes its memory.
One stripped program, examined from outside while it runs: a value we know, where it sits in memory, which static function owns it, how often that code runs and who calls it, which instruction writes the field, and the same field after its buffer moves. Every command below ran exactly as shown, and the output under it is what that run printed.
xref live and the opt-in xref_live MCP tool are available starting with v0.3.0. Earlier published v0.2.0 binaries do not contain them. Linux x86-64 only.- Start a stripped program and give it some state
- Confirm the process and the binary
- Find the structure in memory
- Translate the two pointers to static addresses
- Find the code that stores that string
- Read the deposit handler as pseudo-C
- Read the function stored in the structure
- Count calls and see who makes them
- Catch the instruction that writes a field
- Find a growable buffer
- Follow the buffer after it moves
- xref:
xref 0.2.0 built from main at d2085bb (release), sha256 7517fac97d742d8c82b574c10a36e941589029c44ad6127d6867f7d95d67d394 - Toolchain:
rustc 1.97.1 (8bab26f4f 2026-07-14) - Host:
development Linux 5.15.0-191-generic x86_64 host, recorded 2026-11-03T19:03:16Z - Fixture:
rustc --edition=2021 -O -C strip=symbols -o vault-daemon tests/fixtures/live_daemon_app.rs; source sha256 febf7570f2130801942216a8ec216c32dffe0f806f64b35431cc6157e891fdc3; binary sha256 83375cf10e8a3ef314c77146fe4dd8e5ea44793197d47168782e554edf132909 - Run:
daemon PID 2927467. PIDs and runtime addresses belong to this run; ASLR changes them on every start. - Output:
stdout and stderr are copied unchanged except that spaces at line ends are trimmed; scripts/live_walkthrough/transcript.json keeps the raw capture.
maps, resolve and memscan read /proc/<pid>/maps and /proc/<pid>/mem, so they need ptrace access to the target (same user; under Yama ptrace_scope=1 the target must allow it, which the fixture does with prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY)). probe and watch open perf events on another process and need privileged perf access (root, or capabilities and kernel.perf_event_paranoid settings that allow it). In this recording only those two commands ran with sudo -n, only against the fixture; host settings were not changed.main on Linux x86-64:cargo build --release python3 scripts/live_walkthrough/record.py --commit "$(git rev-parse --short HEAD)" --out /tmp/live-walkthrough.json python3 scripts/live_walkthrough/render.py /tmp/live-walkthrough.json
1. Start a stripped program and give it some state
unprivilegedThe target is our own fixture, tests/fixtures/live_daemon_app.rs, built with -C strip=symbols: no symbols, no debug info. It keeps a vault structure on the heap and changes it when it reads commands such as DEPOSIT, ROTATE and VEC_MOVE_WRITE0 on stdin. We act as if we knew only one thing about it: the 16-byte signature DAEMONV2 + 0x778899aabbccddee that it keeps at the start of the vault.
$ ./vault-daemon DAEMON_READY pid=2927467 > DEPOSIT 777 500000 ACK op=DEPOSIT seq=1
2. Confirm the process and the binary
unprivilegedxref live maps reads /proc/<pid>/maps and smaps, checks that the file passed with -b is the executable that is running, and computes the PIE slide that links runtime addresses to static ones.
pid 2927467 starttime 342472486 match verified_build_id pie_slide +0x5581807ca000 alive true changed false 0x5581807ca000..0x5581807e3000 r--p off 0x00000000 skipped_non_writable target /tmp/xref-walkthrough/vault-daemon 0x5581807e3000..0x55818082f000 r-xp off 0x00018000 skipped_non_writable target /tmp/xref-walkthrough/vault-daemon 0x55818082f000..0x558180833000 r--p off 0x00063000 skipped_non_writable target /tmp/xref-walkthrough/vault-daemon 0x558180833000..0x558180835000 rw-p off 0x00066000 scannable_rwp target /tmp/xref-walkthrough/vault-daemon 0x55819217d000..0x55819219e000 rw-p off 0x00000000 scannable_rwp - [heap] 0x7f7565bb0000..0x7f7565bb3000 rw-p off 0x00000000 scannable_rwp - [anon] 0x7f7565bb3000..0x7f7565bdb000 r--p off 0x00000000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libc.so.6 0x7f7565bdb000..0x7f7565d70000 r-xp off 0x00028000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libc.so.6 0x7f7565d70000..0x7f7565dc8000 r--p off 0x001bd000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libc.so.6 0x7f7565dc8000..0x7f7565dc9000 ---p off 0x00215000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libc.so.6 0x7f7565dc9000..0x7f7565dcd000 r--p off 0x00215000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libc.so.6 0x7f7565dcd000..0x7f7565dcf000 rw-p off 0x00219000 skipped_unclassified_file - /usr/lib/x86_64-linux-gnu/libc.so.6 0x7f7565dcf000..0x7f7565ddc000 rw-p off 0x00000000 scannable_rwp - [anon] 0x7f7565ddc000..0x7f7565ddf000 r--p off 0x00000000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7f7565ddf000..0x7f7565df6000 r-xp off 0x00003000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7f7565df6000..0x7f7565dfa000 r--p off 0x0001a000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7f7565dfa000..0x7f7565dfb000 r--p off 0x0001d000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7f7565dfb000..0x7f7565dfc000 rw-p off 0x0001e000 skipped_unclassified_file - /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 0x7f7565e04000..0x7f7565e05000 ---p off 0x00000000 skipped_non_writable - [anon] 0x7f7565e05000..0x7f7565e09000 rw-p off 0x00000000 scannable_rwp - [anon] 0x7f7565e09000..0x7f7565e0b000 r--p off 0x00000000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7f7565e0b000..0x7f7565e35000 r-xp off 0x00002000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7f7565e35000..0x7f7565e40000 r--p off 0x0002c000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7f7565e41000..0x7f7565e43000 r--p off 0x00037000 skipped_non_writable - /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7f7565e43000..0x7f7565e45000 rw-p off 0x00039000 skipped_unclassified_file - /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7ffe75365000..0x7ffe75386000 rw-p off 0x00000000 scannable_rwp - [stack] 0x7ffe753c0000..0x7ffe753c4000 r--p off 0x00000000 skipped_device_or_special - [vvar] 0x7ffe753c4000..0x7ffe753c6000 r-xp off 0x00000000 skipped_device_or_special - [vdso] 0xffffffffff600000..0xffffffffff601000 --xp off 0x00000000 skipped_device_or_special - [vsyscall] 29 regions (6 scannable_rwp)
match verified_build_id: the ELF given to-bhas the same GNU build-id as/proc/2927467/exe, so static analysis of that file applies to this process.pie_slide +0x5581807ca000: runtime address = static address + slide (for this run only; ASLR changes it every start).29 regions (6 scannable_rwp): only private writable mappings are searched later. Shared, device and other file-backed writable mappings are skipped and listed with the reason.
3. Find the structure in memory
unprivilegedxref live memscan searches the scannable regions for the two signature words and prints a window of 8-byte words around each hit. Every word is classified against the static image: code pointer, pointer to a string, heap pointer, vtable candidate or plain number.
pid 2927467 starttime 342472486 match verified_build_id pie_slide +0x5581807ca000 alive true changed false
needle 0x32564e4f4d454144eeddccbbaa998877 align 8 elapsed 0ms regions 6/29 bytes 0x58000 unreadable 0 stop complete hits 2
hit 0x558180834250 /tmp/xref-walkthrough/vault-daemon+0x1250
-0x10 0x0000558180834240 0x000055818081ea70 code_ptr fn 0x54a70+0x0 -
-0x8 0x0000558180834248 0x0000000000000000 scalar
0x0* 0x0000558180834250 0x4441454d4f4e5632 scalar
+0x8* 0x0000558180834258 0x778899aabbccddee scalar
+0x10 0x0000558180834260 0x0000000000000001 scalar
+0x18 0x0000558180834268 0x0000000000000309 scalar
+0x20 0x0000558180834270 0x000000000007a120 scalar
+0x28 0x0000558180834278 0x13579caa196801ee scalar
+0x30 0x0000558180834280 0x0000000000000000 scalar
+0x38 0x0000558180834288 0x0000000000000000 scalar
+0x40 0x0000558180834290 0x0000000000000000 scalar
+0x48 0x0000558180834298 0x0000000000000000 scalar
+0x50 0x00005581808342a0 0x0000000000000000 scalar
+0x58 0x00005581808342a8 0x0000000000000000 scalar
+0x60 0x00005581808342b0 0x0000000000000000 scalar
hit 0x55819217dda0 [heap]+0xda0
-0x10 0x000055819217dd90 0x0000000000000000 scalar
-0x8 0x000055819217dd98 0x0000000000000071 scalar
0x0* 0x000055819217dda0 0x4441454d4f4e5632 scalar
+0x8* 0x000055819217dda8 0x778899aabbccddee scalar
+0x10 0x000055819217ddb0 0x0000000000000001 scalar
+0x18 0x000055819217ddb8 0x0000000000000309 scalar
+0x20 0x000055819217ddc0 0x000000000007a120 scalar
+0x28 0x000055819217ddc8 0x13579caa196801ee scalar
+0x30 0x000055819217ddd0 0x0000000000420042 scalar
+0x38 0x000055819217ddd8 0x00005581807e99d0 code_ptr fn 0x1f9d0+0x0 -
+0x40 0x000055819217dde0 0x00005581807d44ef static_data_ptr "XREF_DAEMON_VAULT_ACTIVE_V2"
+0x48 0x000055819217dde8 0x000055819217dd80 heap_ptr [heap]+0xd80
+0x50 0x000055819217ddf0 0x000055818082fb08 relro_ptr_candidate candidates [0x1f800, 0x21630]
+0x58 0x000055819217ddf8 0xe9992ca71d7036cd scalar
+0x60 0x000055819217de00 0x00005581807d55b7 static_data_ptr "DAEMON_OP_DEPOSIT_V2ERR invalid DEPOSIT args"
- Two hits.
/tmp/xref-walkthrough/vault-daemon+0x1250is inside the binary's own writable segment (a global mirror of the vault). The[heap]hit is the live vault, read below. +0x10 = 1is a sequence counter,+0x18 = 0x309is account 777,+0x20 = 0x7a120is the 500000 we just deposited,+0x28holds a derived value,+0x30 = 0x420042looks like a key.+0x38is acode_ptr: the structure stores a function pointer to static function0x1f9d0.+0x40points to the textXREF_DAEMON_VAULT_ACTIVE_V2;+0x48points to another heap object;+0x50points into read-only relocated data with two candidate functions, the usual shape of a Rust trait-object vtable.+0x60points to a static string. Rust string literals are not NUL-terminated, so the preview runs into the next literal. Use the address, not the preview text.
4. Translate the two pointers to static addresses
unprivilegedxref live resolve turns runtime addresses into static addresses and file offsets of the verified binary. The static addresses are what every static xref command takes.
pid 2927467 starttime 342472486 match verified_build_id pie_slide +0x5581807ca000 alive true changed false 0x5581807e99d0 runtime_to_static static 0x1f9d0 runtime 0x5581807e99d0 file 0x1e9d0 sym - /tmp/xref-walkthrough/vault-daemon+0x69d0 0x5581807d55b7 runtime_to_static static 0xb5b7 runtime 0x5581807d55b7 file 0xb5b7 sym - /tmp/xref-walkthrough/vault-daemon+0xb5b7
- The function pointer is static function
0x1f9d0; the string pointer is static address0xb5b7.
5. Find the code that stores that string
unprivilegedStatic analysis of the same stripped file: which instructions reference the string address?
0x1fa0e in 0x1f9f0 lea rax, [0xb5b7] 1 hits
xref: load 221.9µs, total 2.5ms
- Exactly one
lea, inside function0x1f9f0. That function writes the label into the vault, so it is the code that handles deposits.
6. Read the deposit handler as pseudo-C
unprivilegedxref c decompiles the function directly from machine code.
/*
* fn_1f9f0 — 0x1f9f0..0x1faea (250 bytes, 1 direct caller; synthetic FDE entry)
* params:
* arg_0 (rdi): void * — writes +0x0 (8B), +0x8 (8B), +0x10 (8B), +0x18 (8B)
* arg_1 (rsi): unk64 — 64-bit register (signedness unconstrained)
* arg_2 (rdx): unk64 — 64-bit register (signedness unconstrained)
* return: unk64 in rax (consumed by 1/1 direct call site)
*/
unk64 fn_1f9f0(void *arg_0, unk64 arg_1, unk64 arg_2) {
unk64 slot_0; /* [rsp+0x0], addr-taken */
unk64 slot_8; /* [rsp+0x8] */
unk64 slot_10; /* [rsp+0x10] */
slot_8 = arg_2;
slot_10 = arg_1;
slot_0 = 0xb5b7;
*(u64 *)(arg_0 + 96) = slot_0;
slot_0 = 0x4441454d4f4e5632;
unk64 v_1 = slot_0;
*(u64 *)(arg_0) = v_1;
slot_0 = 0x778899aabbccddee;
unk64 v_2 = slot_0;
*(u64 *)(arg_0 + 8) = v_2;
unk64 v_3 = *(u64 *)(arg_0 + 48);
unk64 v_4 = (*(u64 *)(arg_0 + 16) + 1);
*(u64 *)(arg_0 + 16) = v_4;
*(u64 *)(arg_0 + 24) = arg_1;
unk64 v_5 = (*(u64 *)(arg_0 + 32) + arg_2);
*(u64 *)(arg_0 + 32) = v_5;
unk64 v_6 = *(u64 *)(arg_0 + 56)(v_5, v_3, arg_1);
unk64 v_7 = v_6;
*(u64 *)(arg_0 + 40) = v_6;
unk64 v_20 = *(u64 *)(*(u64 *)(arg_0 + 80) + 24)(*(u64 *)(arg_0 + 72), v_4, v_7);
*(u64 *)(arg_0 + 88) = v_20;
unk64 v_8 = slot_10;
fn_21650(v_8, slot_8, v_4);
/* xchg [0x6a250], r14 */
/* xchg [0x6a258], rbx */
/* xchg [0x6a260], rax */
/* xchg [0x6a268], r15 */
/* xchg [0x6a270], r13 */
/* xchg [0x6a278], rbp */
return v_4;
}
xref: load 270.4µs, total 3.2ms
- The stores line up with the memory window from the memscan step: signature words at
+0and+8, counter+16incremented, account at+24, amount added to+32, the result of an indirect call through the pointer at+56(+0x38) stored to+40(+0x28), then a call through the vtable at+80(+0x50). - Four commands (
memscan,resolve,refs,c) took us from one known value to the layout of the structure and the code that updates it.
7. Read the function stored in the structure
unprivilegedThe code_ptr from offset +0x38 points to 0x1f9d0.
/*
* fn_1f9d0 — 0x1f9d0..0x1f9e8 (24 bytes, 0 direct callers; synthetic FDE entry)
* params:
* arg_0 (rdi): unk64 — 64-bit register (signedness unconstrained)
* arg_1 (rsi): usize — count/index (scaled by 201527)
* return: unk64 in rax
*/
unk64 fn_1f9d0(unk64 arg_0, usize arg_1) {
return ((arg_1 * 0x31337) + (0x13579bdf2468ace0 ^ arg_0));
}
xref: load 234.3µs, total 2.3ms
- A small mixing function: the derived value at
+0x28is computed from the balance and the key at+0x30.
8. Count calls and see who makes them
recorded with sudo -n · fixture onlyxref live probe places Linux uprobes on the two static addresses, only for this process (perf backend). While it is armed we send three commands to the daemon: a deposit, a key rotation and another deposit.
> DEPOSIT 777 250000 ACK op=DEPOSIT seq=2 > ROTATE 0x99 ACK op=ROTATE seq=3 > DEPOSIT 777 125000 ACK op=DEPOSIT seq=4
pid 2927467 starttime 342472486 match verified_build_id pie_slide +0x5581807ca000 alive true changed false backend perf pid_isolated_bp true elapsed 1500ms/1500ms stop duration_elapsed seeded_tids 1 dyn_tids 0 excluded_tgid 0 lost 0 probe static 0x1f9f0 runtime 0x5581807e99f0 file 0x1e9f0 insn push cfi entry_rsp+8_ra-8 fn 0x1f9f0 (-) hits 2 caller ra 0x5581807ea0ea (static 0x200ea) fn 0x1fd50 call_site 0x200e5 verified true hits 2 [unwind_evidence_only_tail_call_possible] regs tid 2927467 rdi 0x55819217dda0 rsi 0x309 rdx 0x3d090 rcx 0x55819217fe40 r8 0x0 r9 0x55819217fe32 rax 0x1 rsp 0x7ffe753848a8 rbp 0x309 [raw_register_bits_do_not_prove_source_types] regs tid 2927467 rdi 0x55819217dda0 rsi 0x309 rdx 0x1e848 rcx 0x55819217fe40 r8 0x0 r9 0x55819217fe32 rax 0x1 rsp 0x7ffe753848a8 rbp 0x309 [raw_register_bits_do_not_prove_source_types] probe static 0x1f9d0 runtime 0x5581807e99d0 file 0x1e9d0 insn mov cfi entry_rsp+8_ra-8 fn 0x1f9d0 (-) hits 1 caller ra 0x5581807e9a7a (static 0x1fa7a) fn 0x1f9f0 call_site 0x1fa76 verified true hits 1 [unwind_evidence_only_tail_call_possible] regs tid 2927467 rdi 0xb71b0 rsi 0x420042 rdx 0x3d090 rcx 0x309 r8 0x0 r9 0x55819217fe32 rax 0x7ffe75384860 rsp 0x7ffe75384858 rbp 0x309 [raw_register_bits_do_not_prove_source_types]
xref: probe_ready {"backend":"perf","pid":2927467,"attached_tids":[2927467],"probes":["0x1f9f0","0x1f9d0"]}
- The deposit handler ran twice (
hits 2), the stored function ran once (hits 1).ROTATEreplaced the pointer at+0x38, so the second deposit called a different function. - Caller of the deposit handler: function
0x1fd50at call site0x200e5(the fixture's command loop). Caller of0x1f9d0: the deposit handler itself at0x1fa76, an indirect call through the structure field, matched by its return address. - Registers at entry:
rsi 0x309is account 777,rdx 0x3d090and0x1e848are the amounts 250000 and 125000. In the hook,rdi 0xb71b0is the new balance 750000 andrsi 0x420042is the key. - The tags in brackets keep the limits visible: a return address can also come from a tail call, and register values do not prove source-level types.
9. Catch the instruction that writes a field
recorded with sudo -n · fixture onlyxref live watch arms an x86 hardware write watchpoint (DR0–DR3) on the runtime address of the field at +0x28, then we make two deposits.
> DEPOSIT 777 1000 ACK op=DEPOSIT seq=5 > DEPOSIT 777 2000 ACK op=DEPOSIT seq=6
pid 2927467 starttime 342472486 match verified_build_id pie_slide +0x5581807ca000 alive true changed false backend perf_breakpoint pid_isolated_wp true elapsed 1200ms/1200ms stop duration_elapsed seeded_tids 1 dyn_tids 0 excluded_tgid 0 lost 0 watch runtime 0x55819217ddc8 static - len 8B access write region [heap]+0xdc8 (rw-p) init 0xfedcba99ddb00a59 latest 0xfedcba99db270a59 hits 2 writer 0x1fa7d (trap 0x5581807e9a81 / static 0x1fa81) fn 0x1f9f0 (-) insn "mov [r15+0x28], rax" conf candidate_linear_predecessor_store hits 2 [candidate_linear_predecessor_store_operand_assumes_no_indirect_or_cross_fde_entry] sample tid 2927467 writer 0x1fa7d trap 0x5581807e9a81 value candidate_reg:rax(8B)=0xfedcba99de2d0a59 sample tid 2927467 writer 0x1fa7d trap 0x5581807e9a81 value candidate_reg:rax(8B)=0xfedcba99db270a59
xref: watch_ready {"backend":"perf_breakpoint","pid":2927467,"attached_tids":[2927467],"watches":["0x55819217ddc8"],"access":"write","len_bytes":8}
- Both writes come from
mov [r15+0x28], raxat static0x1fa7din function0x1f9f0, the deposit handler we decompiled. The CPU reports the trap after the store (static 0x1fa81); xref names the preceding store as the writer with confidencecandidate_linear_predecessor_store. - The value is decoded from the source register at the trap. The last sampled value equals
latest 0xfedcba99db270a59, which xref read back from/proc/<pid>/memafter the window. The two agree, but a later snapshot alone would not prove what a store wrote.
10. Find a growable buffer
unprivilegedA second structure has its own signature (XREFTYPE + 0x1122334455667788). It mirrors a Vec<u64>.
pid 2927467 starttime 342472486 match verified_build_id pie_slide +0x5581807ca000 alive true changed false
needle 0x45505954464552588877665544332211 align 8 elapsed 0ms regions 6/30 bytes 0x58000 unreadable 0 stop complete hits 1
hit 0x55819217d480 [heap]+0x480
-0x10 0x000055819217d470 0x00007f7565dca0c0 mapped_ptr /usr/lib/x86_64-linux-gnu/libc.so.6+0x10c0
-0x8 0x000055819217d478 0x0000000000000081 scalar
0x0* 0x000055819217d480 0x5852454654595045 scalar
+0x8* 0x000055819217d488 0x1122334455667788 scalar
+0x10 0x000055819217d490 0x0000000000000001 scalar
+0x18 0x000055819217d498 0x0000000000000309 scalar
+0x20 0x000055819217d4a0 0x00000000000007d0 scalar
+0x28 0x000055819217d4a8 0x8000000100000019 scalar
+0x30 0x000055819217d4b0 0xff011234ffffffff scalar
+0x38 0x000055819217d4b8 0xfffffff6389adc00 scalar
+0x40 0x000055819217d4c0 0x000000003fc00000 scalar
+0x48 0x000055819217d4c8 0x4009000000000000 scalar
+0x50 0x000055819217d4d0 0x000055819217fe90 heap_ptr [heap]+0x2e90
+0x58 0x000055819217d4d8 0x0000000000000008 scalar
+0x60 0x000055819217d4e0 0x0000000000000006 scalar
+0x68 0x000055819217d4e8 0x0000558180833840 static_data_ptr "XREF"
+0x50is a heap pointer0x000055819217fe90(the buffer),+0x58is the capacity,+0x60is the length. When the vector grows the program moves the buffer, so a watchpoint on a fixed address would go silent.
11. Follow the buffer after it moves
recorded with sudo -n · fixture only--follow watches the pointer slot and the element it points to. len_slot/min_len and guard_slot/guard_val stop the watch if the length or the signature no longer match. We write element 0, move the buffer to a new allocation, and write element 0 again.
> VEC_WRITE0 0x1111222233334444 ACK op=VEC_WRITE0 len=6 > VEC_MOVE_WRITE0 0x5555666677778888 ACK op=VEC_MOVE_WRITE0 cap=256 > VEC_WRITE0 0x9999aaaabbbbcccc ACK op=VEC_WRITE0 len=6
pid 2927467 starttime 342472486 match verified_build_id pie_slide +0x5581807ca000 alive true changed false backend perf_breakpoint pid_isolated_wp true elapsed 1600ms/1600ms stop duration_elapsed seeded_tids 1 dyn_tids 0 excluded_tgid 0 lost 0 (lower_bound) follow ptr_slot 0x55819217d4d0+0 state bound gen 2 leaf 0x55819217fee0 slots 2 root_hits 1 leaf_hits 2 writer 0x20c41 (trap 0x5581807eac44 / static 0x20c44) fn 0x1fd50 (-) insn "mov [rdx], rcx" conf ambiguous_branch_target hits 2 [trap_ip_is_intra_function_branch_target_predecessor_not_unique] sample tid 2927467 writer 0x20c41 trap 0x5581807eac44 value unknown(ambiguous_branch_target_at_trap_ip) sample tid 2927467 writer 0x20c41 trap 0x5581807eac44 value unknown(ambiguous_branch_target_at_trap_ip)
xref: watch_ready {"backend":"perf_breakpoint","pid":2927467,"attached_tids":[2927467],"watches":["0x55819217d4d0","0x55819217fe90"],"access":"write","len_bytes":8}
- At arm time the element was at
0x55819217fe90(second address in thewatch_readyline). After the move the watch re-armed on the new buffer:state bound,gen 2,leaf 0x55819217fee0. root_hits 1is the write that published the new pointer,leaf_hits 2are writes to element 0 that were observed.- Here the trap address is a branch target, so xref does not pick a single writer or value (
ambiguous_branch_target,unknown).(lower_bound)marks the counts as a lower bound: a write made to the new buffer before its pointer is published happens before the watch can move.
Use it from an agent
xref -b vault-daemon mcp --allow-live-pid <PID> exposes the same operations to an MCP client as the xref_live tool, pinned to that one verified process, next to the static xref_query tool. Without --allow-live-pid the server stays read-only and static. This session is not recorded here; see docs/mcp.md for requests and budgets, and docs/live.md for the full contract and limits.