Skip to content

Commit da6ea88

Browse files
committed
Restore objdiff-cli oneshot mode (JSON output)
1 parent 08ebea8 commit da6ea88

5 files changed

Lines changed: 561 additions & 3 deletions

File tree

objdiff-cli/src/cmd/diff.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crossterm::{
1919
},
2020
};
2121
use objdiff_core::{
22+
bindings::diff::DiffResult,
2223
build::{
2324
BuildConfig, BuildStatus,
2425
watcher::{Watcher, create_watcher},
@@ -28,7 +29,7 @@ use objdiff_core::{
2829
build_globset,
2930
path::{check_path_buf, platform_path, platform_path_serde_option},
3031
},
31-
diff::{DiffObjConfig, MappingConfig, ObjectDiff},
32+
diff::{self, DiffObjConfig, DiffSide, MappingConfig, ObjectDiff},
3233
jobs::{
3334
Job, JobQueue, JobResult,
3435
objdiff::{ObjDiffConfig, start_build},
@@ -40,7 +41,10 @@ use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf};
4041

4142
use crate::{
4243
cmd::apply_config_args,
43-
util::term::crossterm_panic_handler,
44+
util::{
45+
output::{OutputFormat, write_output},
46+
term::crossterm_panic_handler,
47+
},
4448
views::{EventControlFlow, EventResult, UiView, function_diff::FunctionDiffUi},
4549
};
4650

@@ -60,6 +64,12 @@ pub struct Args {
6064
#[argp(option, short = 'u')]
6165
/// Unit name within project
6266
unit: Option<String>,
67+
#[argp(option, short = 'o', from_str_fn(platform_path))]
68+
/// Output file (one-shot mode) ("-" for stdout)
69+
output: Option<Utf8PlatformPathBuf>,
70+
#[argp(option)]
71+
/// Output format (json, json-pretty, proto) (default: json)
72+
format: Option<String>,
6373
#[argp(positional)]
6474
/// Function symbol to diff
6575
symbol: Option<String>,
@@ -158,7 +168,41 @@ pub fn run(args: Args) -> Result<()> {
158168
_ => bail!("Either target and base or project and unit must be specified"),
159169
};
160170

161-
run_interactive(args, target_path, base_path, project_config, unit_options)
171+
if let Some(output) = &args.output {
172+
run_oneshot(&args, output, target_path.as_deref(), base_path.as_deref(), unit_options)
173+
} else {
174+
run_interactive(args, target_path, base_path, project_config, unit_options)
175+
}
176+
}
177+
178+
fn run_oneshot(
179+
args: &Args,
180+
output: &Utf8PlatformPath,
181+
target_path: Option<&Utf8PlatformPath>,
182+
base_path: Option<&Utf8PlatformPath>,
183+
unit_options: Option<ProjectOptions>,
184+
) -> Result<()> {
185+
let output_format = OutputFormat::from_option(args.format.as_deref())?;
186+
let (diff_config, mapping_config) = build_config_from_args(args, None, unit_options.as_ref())?;
187+
let target = target_path
188+
.map(|p| {
189+
obj::read::read(p.as_ref(), &diff_config, DiffSide::Target)
190+
.with_context(|| format!("Loading {p}"))
191+
})
192+
.transpose()?;
193+
let base = base_path
194+
.map(|p| {
195+
obj::read::read(p.as_ref(), &diff_config, DiffSide::Base)
196+
.with_context(|| format!("Loading {p}"))
197+
})
198+
.transpose()?;
199+
let result =
200+
diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?;
201+
let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d)));
202+
let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d)));
203+
let diff_result = DiffResult::new(left, right, &diff_config)?;
204+
write_output(&diff_result, Some(output), output_format)?;
205+
Ok(())
162206
}
163207

164208
fn build_config_from_args(

objdiff-core/protos/diff.proto

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
syntax = "proto3";
2+
3+
package objdiff.diff;
4+
5+
// Top-level diff result containing left and right object diffs
6+
message DiffResult {
7+
optional DiffObject left = 1;
8+
optional DiffObject right = 2;
9+
}
10+
11+
// Diff information for a single object file
12+
message DiffObject {
13+
repeated DiffSection sections = 1;
14+
repeated DiffSymbol symbols = 2;
15+
}
16+
17+
// Diff information for a section
18+
message DiffSection {
19+
string name = 1;
20+
DiffSectionKind kind = 2;
21+
uint64 size = 3;
22+
uint64 address = 4;
23+
optional float match_percent = 5;
24+
repeated DiffDataSegment data_diff = 6;
25+
repeated DiffDataRelocation reloc_diff = 7;
26+
}
27+
28+
enum DiffSectionKind {
29+
SECTION_UNKNOWN = 0;
30+
SECTION_CODE = 1;
31+
SECTION_DATA = 2;
32+
SECTION_BSS = 3;
33+
SECTION_COMMON = 4;
34+
}
35+
36+
// Symbol with diff information
37+
message DiffSymbol {
38+
string name = 1;
39+
optional string demangled_name = 2;
40+
uint64 address = 3;
41+
uint64 size = 4;
42+
uint32 flags = 5;
43+
// The symbol index in the _other_ object that this symbol was diffed against
44+
optional uint32 target_symbol = 6;
45+
optional float match_percent = 7;
46+
// Instruction diff rows (for code symbols)
47+
repeated DiffInstructionRow instructions = 8;
48+
// Data diff (for data symbols)
49+
repeated DiffDataSegment data_diff = 9;
50+
}
51+
52+
// Symbol visibility flags (bitmask)
53+
enum DiffSymbolFlag {
54+
SYMBOL_NONE = 0;
55+
SYMBOL_GLOBAL = 1;
56+
SYMBOL_LOCAL = 2;
57+
SYMBOL_WEAK = 4;
58+
SYMBOL_COMMON = 8;
59+
SYMBOL_HIDDEN = 16;
60+
}
61+
62+
// A single instruction diff row
63+
message DiffInstructionRow {
64+
DiffKind diff_kind = 1;
65+
optional DiffInstruction instruction = 2;
66+
repeated DiffInstructionArgDiff arg_diff = 3;
67+
}
68+
69+
// Parsed instruction information
70+
message DiffInstruction {
71+
uint64 address = 1;
72+
uint32 size = 2;
73+
// Formatted instruction string
74+
string formatted = 3;
75+
// Formatted instruction parts
76+
repeated DiffInstructionPart parts = 4;
77+
// Relocation information (if present)
78+
optional DiffRelocation relocation = 5;
79+
// Branch destination address
80+
optional uint64 branch_dest = 6;
81+
// Source line number (if present)
82+
optional uint32 line_number = 7;
83+
}
84+
85+
// An instruction part
86+
message DiffInstructionPart {
87+
oneof part {
88+
// Basic text (whitespace, punctuation, etc.)
89+
string basic = 1;
90+
// Opcode/mnemonic
91+
DiffOpcode opcode = 2;
92+
// Argument
93+
DiffInstructionArg arg = 3;
94+
// Separator between arguments (comma)
95+
bool separator = 4;
96+
}
97+
}
98+
99+
message DiffOpcode {
100+
// Mnemonic string
101+
string mnemonic = 1;
102+
// Base opcode ID
103+
uint32 opcode = 2;
104+
}
105+
106+
// An instruction argument
107+
message DiffInstructionArg {
108+
oneof arg {
109+
// Signed immediate value
110+
sint64 signed = 1;
111+
// Unsigned immediate value
112+
uint64 unsigned = 2;
113+
// Opaque string value (register names, etc.)
114+
string opaque = 3;
115+
// Relocation
116+
bool reloc = 4;
117+
// Branch destination address
118+
uint64 branch_dest = 5;
119+
}
120+
}
121+
122+
// Relocation information
123+
message DiffRelocation {
124+
uint32 type = 1;
125+
string type_name = 2;
126+
uint32 target_symbol = 3;
127+
int64 addend = 4;
128+
}
129+
130+
// Argument diff information
131+
message DiffInstructionArgDiff {
132+
// If set, this argument differs from the other side.
133+
// The value is a rotating index for coloring.
134+
optional uint32 diff_index = 1;
135+
}
136+
137+
// Diff kind for instructions and data
138+
enum DiffKind {
139+
DIFF_NONE = 0;
140+
DIFF_REPLACE = 1;
141+
DIFF_DELETE = 2;
142+
DIFF_INSERT = 3;
143+
DIFF_OP_MISMATCH = 4;
144+
DIFF_ARG_MISMATCH = 5;
145+
}
146+
147+
// Data diff segment
148+
message DiffDataSegment {
149+
DiffKind kind = 1;
150+
bytes data = 2;
151+
// Size may be larger than data length for BSS
152+
uint64 size = 3;
153+
}
154+
155+
// Data relocation diff
156+
message DiffDataRelocation {
157+
DiffRelocation relocation = 1;
158+
DiffKind kind = 2;
159+
// Address range this relocation covers
160+
uint64 start = 3;
161+
uint64 end = 4;
162+
}
8.36 KB
Binary file not shown.

0 commit comments

Comments
 (0)