1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/*
* xref — Ultra blazing fast cross-references of an x86-64 ELF binary
* Copyright (c) 2026 Alexander Panasenko
*
* Contact: alex@prod.codes
* Author: https://prod.codes/about/
* SPDX-License-Identifier: MIT OR Apache-2.0
*/
//! Command line. Addresses are static image virtual addresses: `0x` hex or decimal.
use std::path::PathBuf;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "xref",
version,
about = "Ultra blazing fast cross-references and pseudo-C for x86-64 ELF and Mach-O binaries"
)]
pub struct Cli {
/// The ELF or supported thin x86-64 Mach-O binary (optional for `xref live`, required for static commands).
#[arg(short, long, env = "XREF_BIN")]
pub bin: Option<PathBuf>,
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand)]
pub enum Command {
/// Run a versioned structured query supplied as a JSON object.
Query { request: String },
/// Serve read-only MCP over stdio for this one immutable binary snapshot (or opt-in live PID inspection).
Mcp {
/// Explicitly enable the PID-bound `xref_live` MCP tool for this single verified Linux process.
#[arg(long, value_name = "PID")]
allow_live_pid: Option<u32>,
},
/// Functions, code size, data pointers, imports and panic locations.
Stats,
/// Function symbols whose name contains a pattern (non-stripped binaries).
Sym { pattern: String },
/// The function (exact `.eh_frame` range) containing each address.
Func {
#[arg(required = true, value_parser = parse_number)]
addrs: Vec<u64>,
},
/// Code and data slots naming addresses (`0x...`) or text (anything else).
Refs {
#[arg(required = true)]
targets: Vec<String>,
/// Also accept references this many bytes before a text (rustc format pieces start with a length byte).
#[arg(long, default_value_t = 1)]
back: u64,
/// Search for strings built inline from immediate stores (`movabs` / `mov [mem], imm`).
#[arg(long)]
inline: bool,
},
/// Strings built inline from immediate stores (`movabs` / `mov [mem], imm`).
Istr {
#[arg(required = true)]
targets: Vec<String>,
},
/// Instructions with an immediate operand.
Imm {
#[arg(value_parser = parse_number)]
value: u64,
/// Only `mov [memory], imm`.
#[arg(long)]
store: bool,
/// Only `cmp`.
#[arg(long)]
cmp: bool,
/// Only inside the function containing this address.
#[arg(long = "in", value_parser = parse_number)]
within: Option<u64>,
},
/// Instructions reading or writing struct fields: non-stack memory operands with these displacements.
Disp {
#[arg(required = true, value_parser = parse_number)]
values: Vec<u64>,
/// Only instructions writing the memory operand.
#[arg(long)]
write: bool,
/// Only `lea` instructions taking the field's address.
#[arg(long)]
lea: bool,
/// Functions accessing all of the given displacements (an intersection), with per-function counts.
#[arg(long)]
all: bool,
/// Only accesses whose base or index register was computed with this array element stride.
#[arg(long, value_parser = parse_number)]
stride: Option<u64>,
/// Include stack displacements and fields relative to explicitly address-taken stack buffers.
#[arg(long)]
stack: bool,
/// Only inside the function containing this address.
#[arg(long = "in", value_parser = parse_number)]
within: Option<u64>,
},
/// Direct calls and jumps to an address, and code taking its address or loading a slot holding it.
Callers {
#[arg(value_parser = parse_number)]
addr: u64,
},
/// Calls and tail jumps of the function containing an address, in code order.
Calls {
#[arg(value_parser = parse_number)]
addr: u64,
},
/// Constant and symbolic arguments at every call site of a function (rdi..r9 and stack0..N).
Args {
addr: String,
/// Maximum number of stack argument slots to inspect.
#[arg(long, default_value_t = 4)]
max: usize,
},
/// Functions satisfying multiple predicates in one parallel pass.
Find {
/// Function calls or tail-jumps to ADDR (repeatable).
#[arg(long)]
calls: Vec<String>,
/// Function references string or hex address (repeatable).
#[arg(long)]
refs: Vec<String>,
/// Function uses immediate constant N (repeatable).
#[arg(long, value_parser = parse_number)]
imm: Vec<u64>,
/// Only `mov [mem], imm` stores for `--imm`.
#[arg(long)]
store: bool,
/// Only `cmp` instructions for `--imm`.
#[arg(long)]
cmp: bool,
/// Function accesses struct field at displacement OFF (repeatable).
#[arg(long, value_parser = parse_number)]
disp: Vec<u64>,
/// Only memory writes for `--disp`.
#[arg(long)]
write: bool,
/// Only `lea` address-of uses for `--disp`.
#[arg(long)]
lea: bool,
/// Function references panic Location matching PATTERN (repeatable).
#[arg(long)]
src: Vec<String>,
/// Function byte size range (`MIN..MAX`, `MIN..`, `..MAX`, or `EXACT`).
#[arg(long)]
size: Option<String>,
/// Minimum number of predicates to satisfy (default: all); ranks by match count.
#[arg(long)]
any: Option<usize>,
/// Bytes before each `--refs` string to include in reachability window.
#[arg(long, default_value_t = 16, value_parser = parse_number)]
back: u64,
},
/// Shortest call chains from function FROM to function TO over direct calls, tail jumps and pointer slots.
Path {
from: String,
to: String,
/// Maximum call-chain depth (hops).
#[arg(long, default_value_t = 8)]
depth: usize,
},
/// Indented caller or callee tree of a function, with ranges and first panic locations.
Tree {
addr: String,
/// Show callers instead of callees.
#[arg(long)]
callers: bool,
/// Show callees (default).
#[arg(long)]
callees: bool,
/// Maximum tree depth.
#[arg(long, default_value_t = 3)]
depth: usize,
},
/// Counterpart of each function in another build of the binary, scored by shared locations, strings, immediates and call graph.
#[command(visible_alias = "diff")]
Match {
/// The other binary build to compare against.
#[arg(long)]
other: PathBuf,
#[arg(required = true)]
addrs: Vec<String>,
/// Number of top candidates to print per function.
#[arg(long, default_value_t = 5)]
top: usize,
},
/// Annotated disassembly of the function containing an address.
Asm {
#[arg(value_parser = parse_number)]
addr: u64,
/// First address to print.
#[arg(long, value_parser = parse_number)]
from: Option<u64>,
/// End address (exclusive).
#[arg(long, value_parser = parse_number)]
to: Option<u64>,
},
/// Pseudo-C decompilation of the functions containing each address.
#[command(visible_alias = "decomp")]
C {
#[arg(required = true, value_parser = parse_number)]
addrs: Vec<u64>,
/// First address to print.
#[arg(long, value_parser = parse_number)]
from: Option<u64>,
/// End address (exclusive).
#[arg(long, value_parser = parse_number)]
to: Option<u64>,
/// Write each decompiled function to `<OUT>/<start>.c` in parallel and print path, size and elapsed time.
#[arg(long)]
out: Option<PathBuf>,
/// Rewrite raw pointer offsets as inferred `->f_<off>` field hints using layout recovery.
#[arg(long)]
fields: bool,
/// Keep residual labels and gotos instead of introducing skip flags; retain structured loops and branches.
#[arg(long = "goto")]
keep_gotos: bool,
},
/// Backward program slice: minimal pseudo-C statements and branches reaching a target address or text.
Slice {
addr: String,
/// Target hex address, call name, field offset, or string substring inside the function.
#[arg(long)]
to: String,
/// Rewrite raw pointer offsets as inferred `->f_<off>` field hints before slicing.
#[arg(long)]
fields: bool,
/// Keep residual labels and gotos in the sliced pseudo-C.
#[arg(long = "goto")]
keep_gotos: bool,
},
/// Normalized cross-build pseudo-C diff between counterpart functions in two binaries.
#[command(name = "diff-c")]
DiffC {
/// The other binary build to compare against.
#[arg(long)]
other: PathBuf,
#[arg(required = true)]
addrs: Vec<String>,
/// Explicit counterpart address/symbol in `--other` (auto-matched when omitted).
#[arg(long)]
target: Option<String>,
/// Rewrite raw pointer offsets as inferred `->f_<off>` field hints before diffing.
#[arg(long)]
fields: bool,
},
/// Parallel modular pseudo-C dump grouped by recovered Rust source paths (`<OUT>/crate/src/mod.c`).
Dump {
/// Output directory for grouped `.c` module files.
#[arg(long)]
out: PathBuf,
/// Optional substring filter on source path or symbol name (e.g. `src/loops.rs`).
#[arg(long)]
src: Option<String>,
/// Rewrite raw pointer offsets as inferred `->f_<off>` field hints.
#[arg(long)]
fields: bool,
/// Keep residual labels and gotos in every dumped function.
#[arg(long = "goto")]
keep_gotos: bool,
},
/// Enum variant whose `Display`/`match` arm names a text: the jump-table index of the referencing arm.
Error {
text: String,
#[arg(long, default_value_t = 1)]
back: u64,
},
/// Jump-table arms of the function containing an address for enum variant indices, with the texts they name.
Variant {
#[arg(value_parser = parse_number)]
addr: u64,
#[arg(required = true, value_parser = parse_number)]
indices: Vec<u64>,
},
/// Jump-table indices whose arm holds each address (closest preceding arm of every table, best first).
Arm {
#[arg(required = true, value_parser = parse_number)]
addrs: Vec<u64>,
},
/// Rust source locations (panic `Location`s) the function containing an address passes.
Src {
#[arg(value_parser = parse_number)]
addr: u64,
},
/// Functions passing source locations whose file contains a pattern (and optionally at one line).
Where {
pattern: String,
#[arg(long)]
line: Option<u32>,
},
/// Distinct source files of the panic locations (optionally containing a pattern), with counts.
Files { pattern: Option<String> },
/// Reconstructed struct field layout accessed via an argument register (and optionally callees receiving it).
Layout {
addr: String,
/// Parameter or general-purpose register holding the struct pointer.
#[arg(long, default_value = "rdi")]
reg: String,
/// Follow direct/slot calls one hop where the struct pointer is forwarded.
#[arg(long)]
callees: bool,
},
/// Inspect a live Linux process via procfs (maps, address translation, and xref memory scan).
Live(crate::live::cli::LiveCommand),
}
pub fn parse_number(text: &str) -> Result<u64, String> {
let parsed = text.strip_prefix("0x").map_or_else(|| text.parse::<u64>(), |hex| u64::from_str_radix(hex, 16));
parsed.map_err(|error| format!("{text}: {error}"))
}
/// A `0x` hex address argument; anything else is text.
pub fn hex_address(text: &str) -> Option<u64> {
u64::from_str_radix(text.strip_prefix("0x")?, 16).ok()
}
#[cfg(test)]
mod tests {
use super::{hex_address, parse_number};
#[test]
fn numbers_are_hex_with_a_prefix_and_decimal_without() {
assert_eq!(parse_number("0x194"), Ok(404));
assert_eq!(parse_number("404"), Ok(404));
assert!(parse_number("0xzz").is_err());
assert_eq!(hex_address("0x439041"), Some(0x43_9041));
assert_eq!(hex_address("some text"), None);
assert_eq!(hex_address("404"), None);
}
}