mirror of
https://github.com/encounter/objdiff.git
synced 2026-07-10 12:18:36 -07:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6768df9d80 | |||
| eaba2391e0 | |||
| bd8f5ede23 | |||
| acf46c6b54 | |||
| 9358d8ec60 | |||
| 809e2ffddc | |||
| 87fa29e8b0 | |||
| 42d4c38079 | |||
| fa26200ed7 | |||
| a0e7f9bc37 | |||
| 5898d7aebf |
@@ -0,0 +1,2 @@
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -4,8 +4,8 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
- 'LICENSE*'
|
||||
- "*.md"
|
||||
- "LICENSE*"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
@@ -32,9 +32,9 @@ jobs:
|
||||
- name: Cache Rust workspace
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: Cargo check
|
||||
run: cargo check --features all
|
||||
run: cargo check --all-targets --all-features
|
||||
- name: Cargo clippy
|
||||
run: cargo clippy --features all
|
||||
run: cargo clippy --all-targets --all-features
|
||||
|
||||
fmt:
|
||||
name: Format
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
platform: [ubuntu-latest, windows-latest, macos-latest]
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
key: ${{ matrix.target }}
|
||||
- name: Cargo build
|
||||
run: >
|
||||
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -226,7 +226,7 @@ jobs:
|
||||
key: ${{ matrix.target }}
|
||||
- name: Cargo build
|
||||
run: >
|
||||
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||
cargo ${{ matrix.build }} --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||
--bin ${{ env.CARGO_BIN_NAME }} --features ${{ matrix.features }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -258,7 +258,7 @@ jobs:
|
||||
name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ build-cli, build-gui ]
|
||||
needs: [build-cli, build-gui]
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
args: [--markdown-linebreak-ext=md]
|
||||
- id: end-of-file-fixer
|
||||
- id: fix-byte-order-marker
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
description: Run cargo fmt on all project files.
|
||||
language: system
|
||||
entry: cargo
|
||||
args: ["+nightly", "fmt", "--all"]
|
||||
pass_filenames: false
|
||||
- id: cargo clippy
|
||||
name: cargo clippy
|
||||
description: Run cargo clippy on all project files.
|
||||
language: system
|
||||
entry: cargo
|
||||
args: ["+nightly", "clippy", "--all-targets", "--all-features"]
|
||||
pass_filenames: false
|
||||
- id: cargo-deny
|
||||
name: cargo deny
|
||||
description: Run cargo deny on all project files.
|
||||
language: system
|
||||
entry: cargo
|
||||
args: ["deny", "check"]
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
Generated
+49
-26
@@ -450,6 +450,18 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "auditable-serde"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"topological-sort",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
@@ -2652,7 +2664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3268,7 +3280,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-cli"
|
||||
version = "3.0.0-beta.2"
|
||||
version = "3.0.0-beta.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argp",
|
||||
@@ -3291,7 +3303,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-core"
|
||||
version = "3.0.0-beta.2"
|
||||
version = "3.0.0-beta.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arm-attr",
|
||||
@@ -3344,7 +3356,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-gui"
|
||||
version = "3.0.0-beta.2"
|
||||
version = "3.0.0-beta.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
@@ -3380,7 +3392,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objdiff-wasm"
|
||||
version = "3.0.0-beta.2"
|
||||
version = "3.0.0-beta.4"
|
||||
dependencies = [
|
||||
"log",
|
||||
"objdiff-core",
|
||||
@@ -4474,6 +4486,9 @@ name = "semver"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -5165,6 +5180,12 @@ dependencies = [
|
||||
"winnow 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
@@ -5537,9 +5558,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.225.0"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f7eac0445cac73bcf09e6a97f83248d64356dccf9f2b100199769b6b42464e5"
|
||||
checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
@@ -5547,11 +5568,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.225.0"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1d20d0bf2c73c32a5114cf35a5c10ccf9f9aa37a3a2c0114b3e11cbf6faac12"
|
||||
checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auditable-serde",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -5577,9 +5600,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.225.0"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36e5456165f81e64cb9908a0fe9b9d852c2c74582aa3fe2be3c2da57f937d3ae"
|
||||
checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"hashbrown",
|
||||
@@ -6300,19 +6323,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.39.0"
|
||||
version = "0.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4dd9a372b25d6f35456b0a730d2adabeb0c4878066ba8f8089800349be6ecb5"
|
||||
checksum = "8e7091ed6c9abfa4e0a2ef3b39d0539da992d841fcf32c255f64fb98764ffee5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt 0.39.0",
|
||||
"wit-bindgen-rt 0.40.0",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.39.0"
|
||||
version = "0.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f108fa9b77a346372858b30c11ea903680e7e2b9d820b1a5883e9d530bf51c7e"
|
||||
checksum = "398c650cec1278cfb72e214ba26ef3440ab726e66401bd39c04f465ee3979e6b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
@@ -6330,18 +6353,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
version = "0.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
checksum = "68faed92ae696b93ea9a7b67ba6c37bf09d72c6d9a70fa824a743c3020212f11"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.39.0"
|
||||
version = "0.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ba5b852e976d35dbf6cb745746bf1bd4fc26782bab1e0c615fc71a7d8aac05"
|
||||
checksum = "83903c8dcd8084a8a67ae08190122cf0e25dc37bdc239070a00f47e22d3f0aae"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
@@ -6355,9 +6378,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.39.0"
|
||||
version = "0.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "401529c9af9304a20ed99fa01799e467b7d37727126f0c9a958895471268ad7a"
|
||||
checksum = "a7bf7f20495bcc7dc9f24c5fbcac9e919ca5ebdb7a1b1841d74447d3c8dd0c60"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
@@ -6370,9 +6393,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.225.0"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2505c917564c1d74774563bbcd3e4f8c216a6508050862fd5f449ee56e3c5125"
|
||||
checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.0",
|
||||
@@ -6414,9 +6437,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.225.0"
|
||||
version = "0.227.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebefaa234e47224f10ce60480c5bfdece7497d0f3b87a12b41ff39e5c8377a78"
|
||||
checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ strip = "debuginfo"
|
||||
codegen-units = 1
|
||||
|
||||
[workspace.package]
|
||||
version = "3.0.0-beta.2"
|
||||
version = "3.0.0-beta.4"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -143,15 +143,15 @@ If not specified, objdiff will use the default patterns listed above.
|
||||
`units` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
|
||||
|
||||
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
|
||||
>
|
||||
>
|
||||
> `target_path`: Path to the "target" or "expected" object from the project root.
|
||||
> This object is the **intended result** of the match.
|
||||
>
|
||||
>
|
||||
> `base_path`: Path to the "base" or "actual" object from the project root.
|
||||
> This object is built from the **current source code**.
|
||||
>
|
||||
>
|
||||
> `metadata.auto_generated` _(optional)_: Hides the object from the object list, but still includes it in reports.
|
||||
>
|
||||
>
|
||||
> `metadata.complete` _(optional)_: Marks the object as "complete" (or "linked") in the object list.
|
||||
> This is useful for marking objects that are fully decompiled. A value of `false` will mark the object as "incomplete".
|
||||
|
||||
@@ -173,6 +173,21 @@ $ cargo install --locked --git https://github.com/encounter/objdiff.git objdiff-
|
||||
|
||||
The binaries will be installed to `~/.cargo/bin` as `objdiff` and `objdiff-cli`.
|
||||
|
||||
## Installing `pre-commit`
|
||||
|
||||
When contributing, it's recommended to install `pre-commit` to automatically run the linter and formatter before a commit.
|
||||
|
||||
[`uv`](https://github.com/astral-sh/uv#installation) is recommended to manage Python version and tools.
|
||||
|
||||
Rust nightly is required for `cargo +nightly fmt` and `cargo +nightly clippy`.
|
||||
|
||||
```shell
|
||||
$ cargo install --locked cargo-deny
|
||||
$ rustup toolchain install nightly
|
||||
$ uv tool install pre-commit
|
||||
$ pre-commit install
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
@@ -232,6 +232,7 @@ fn report_object(
|
||||
if symbol.section != Some(section_idx)
|
||||
|| symbol.size == 0
|
||||
|| symbol.flags.contains(SymbolFlag::Hidden)
|
||||
|| symbol.flags.contains(SymbolFlag::Ignored)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ std = [
|
||||
]
|
||||
mips = [
|
||||
"any-arch",
|
||||
"dep:cpp_demangle",
|
||||
"dep:cwdemangle",
|
||||
"dep:rabbitizer",
|
||||
]
|
||||
ppc = [
|
||||
|
||||
@@ -275,4 +275,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+127
-46
@@ -84,18 +84,11 @@ impl ArchArm {
|
||||
.filter_map(|s| DisasmMode::from_symbol(&s))
|
||||
.collect();
|
||||
mapping_symbols.sort_unstable_by_key(|x| x.address);
|
||||
(s.index().0, mapping_symbols)
|
||||
(s.index().0 - 1, mapping_symbols)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn endian(&self) -> unarm::Endian {
|
||||
match self.endianness {
|
||||
object::Endianness::Little => unarm::Endian::Little,
|
||||
object::Endianness::Big => unarm::Endian::Big,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_flags(&self, diff_config: &DiffObjConfig) -> unarm::ParseFlags {
|
||||
unarm::ParseFlags {
|
||||
ual: diff_config.arm_unified_syntax,
|
||||
@@ -130,6 +123,31 @@ impl ArchArm {
|
||||
code: &[u8],
|
||||
diff_config: &DiffObjConfig,
|
||||
) -> Result<(unarm::Ins, unarm::ParsedIns)> {
|
||||
if ins_ref.opcode == thumb::Opcode::BlH as u16 && ins_ref.size == 4 {
|
||||
// Special case: combined thumb BL instruction
|
||||
let parse_flags = self.parse_flags(diff_config);
|
||||
let first_ins = thumb::Ins {
|
||||
code: match self.endianness {
|
||||
object::Endianness::Little => u16::from_le_bytes([code[0], code[1]]),
|
||||
object::Endianness::Big => u16::from_be_bytes([code[0], code[1]]),
|
||||
} as u32,
|
||||
op: thumb::Opcode::BlH,
|
||||
};
|
||||
let second_ins = thumb::Ins::new(
|
||||
match self.endianness {
|
||||
object::Endianness::Little => u16::from_le_bytes([code[2], code[3]]),
|
||||
object::Endianness::Big => u16::from_be_bytes([code[2], code[3]]),
|
||||
} as u32,
|
||||
&parse_flags,
|
||||
);
|
||||
let first_parsed = first_ins.parse(&parse_flags);
|
||||
let second_parsed = second_ins.parse(&parse_flags);
|
||||
return Ok((
|
||||
unarm::Ins::Thumb(first_ins),
|
||||
first_parsed.combine_thumb_bl(&second_parsed),
|
||||
));
|
||||
}
|
||||
|
||||
let code = match (self.endianness, ins_ref.size) {
|
||||
(object::Endianness::Little, 2) => u16::from_le_bytes([code[0], code[1]]) as u32,
|
||||
(object::Endianness::Little, 4) => {
|
||||
@@ -153,11 +171,7 @@ impl ArchArm {
|
||||
} else {
|
||||
let ins = thumb::Ins { code, op: thumb::Opcode::from(ins_ref.opcode as u8) };
|
||||
let parsed = ins.parse(&self.parse_flags(diff_config));
|
||||
if ins.is_half_bl() {
|
||||
todo!("Combine thumb BL instructions");
|
||||
} else {
|
||||
(unarm::Ins::Thumb(ins), parsed)
|
||||
}
|
||||
(unarm::Ins::Thumb(ins), parsed)
|
||||
};
|
||||
Ok((ins, parsed_ins))
|
||||
}
|
||||
@@ -185,60 +199,127 @@ impl Arch for ArchArm {
|
||||
let first_mapping_idx = mapping_symbols
|
||||
.binary_search_by_key(&start_addr, |x| x.address)
|
||||
.unwrap_or_else(|idx| idx - 1);
|
||||
let first_mapping = mapping_symbols[first_mapping_idx].mapping;
|
||||
let mut mode = mapping_symbols[first_mapping_idx].mapping;
|
||||
|
||||
let mut mappings_iter =
|
||||
mapping_symbols.iter().skip(first_mapping_idx + 1).take_while(|x| x.address < end_addr);
|
||||
let mut mappings_iter = mapping_symbols
|
||||
.iter()
|
||||
.copied()
|
||||
.skip(first_mapping_idx + 1)
|
||||
.take_while(|x| x.address < end_addr);
|
||||
let mut next_mapping = mappings_iter.next();
|
||||
|
||||
let ins_count = code.len() / first_mapping.instruction_size(start_addr);
|
||||
let ins_count = code.len() / mode.instruction_size(start_addr);
|
||||
let mut ops = Vec::<ScannedInstruction>::with_capacity(ins_count);
|
||||
|
||||
let endian = self.endian();
|
||||
let parse_flags = self.parse_flags(diff_config);
|
||||
let mut parser = unarm::Parser::new(first_mapping, start_addr, endian, parse_flags, code);
|
||||
|
||||
while let Some((address, ins, _parsed_ins)) = parser.next() {
|
||||
let size = parser.mode.instruction_size(address);
|
||||
if let Some(next) = next_mapping {
|
||||
let next_address = parser.address;
|
||||
if next_address >= next.address {
|
||||
// Change mapping
|
||||
parser.mode = next.mapping;
|
||||
next_mapping = mappings_iter.next();
|
||||
}
|
||||
let mut address = start_addr;
|
||||
while address < end_addr {
|
||||
while let Some(next) = next_mapping.take_if(|x| address >= x.address) {
|
||||
// Change mapping
|
||||
mode = next.mapping;
|
||||
next_mapping = mappings_iter.next();
|
||||
}
|
||||
let (opcode, branch_dest) = match ins {
|
||||
unarm::Ins::Arm(x) => {
|
||||
let opcode = x.op as u16 | (1 << 15);
|
||||
let branch_dest = match x.op {
|
||||
|
||||
let mut ins_size = mode.instruction_size(address);
|
||||
let data = &code[(address - start_addr) as usize..];
|
||||
if data.len() < ins_size {
|
||||
// Push the remainder as data
|
||||
ops.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef {
|
||||
address: address as u64,
|
||||
size: data.len() as u8,
|
||||
opcode: u16::MAX,
|
||||
},
|
||||
branch_dest: None,
|
||||
});
|
||||
break;
|
||||
}
|
||||
let code = match (self.endianness, ins_size) {
|
||||
(object::Endianness::Little, 2) => u16::from_le_bytes([data[0], data[1]]) as u32,
|
||||
(object::Endianness::Little, 4) => {
|
||||
u32::from_le_bytes([data[0], data[1], data[2], data[3]])
|
||||
}
|
||||
(object::Endianness::Big, 2) => u16::from_be_bytes([data[0], data[1]]) as u32,
|
||||
(object::Endianness::Big, 4) => {
|
||||
u32::from_be_bytes([data[0], data[1], data[2], data[3]])
|
||||
}
|
||||
_ => {
|
||||
// Invalid instruction size
|
||||
ops.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef {
|
||||
address: address as u64,
|
||||
size: ins_size as u8,
|
||||
opcode: u16::MAX,
|
||||
},
|
||||
branch_dest: None,
|
||||
});
|
||||
address += ins_size as u32;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let (opcode, branch_dest) = match mode {
|
||||
unarm::ParseMode::Arm => {
|
||||
let ins = arm::Ins::new(code, &parse_flags);
|
||||
let opcode = ins.op as u16 | (1 << 15);
|
||||
let branch_dest = match ins.op {
|
||||
arm::Opcode::B | arm::Opcode::Bl => {
|
||||
address.checked_add_signed(x.field_branch_offset())
|
||||
address.checked_add_signed(ins.field_branch_offset())
|
||||
}
|
||||
arm::Opcode::BlxI => address.checked_add_signed(x.field_blx_offset()),
|
||||
arm::Opcode::BlxI => address.checked_add_signed(ins.field_blx_offset()),
|
||||
_ => None,
|
||||
};
|
||||
(opcode, branch_dest)
|
||||
}
|
||||
unarm::Ins::Thumb(x) => {
|
||||
let opcode = x.op as u16;
|
||||
let branch_dest = match x.op {
|
||||
unarm::ParseMode::Thumb => {
|
||||
let ins = thumb::Ins::new(code, &parse_flags);
|
||||
let opcode = ins.op as u16;
|
||||
let branch_dest = match ins.op {
|
||||
thumb::Opcode::B | thumb::Opcode::Bl => {
|
||||
address.checked_add_signed(x.field_branch_offset_8())
|
||||
address.checked_add_signed(ins.field_branch_offset_8())
|
||||
}
|
||||
thumb::Opcode::BlH if data.len() >= 4 => {
|
||||
// Combine BL instructions
|
||||
let second_ins = thumb::Ins::new(
|
||||
match self.endianness {
|
||||
object::Endianness::Little => {
|
||||
u16::from_le_bytes([data[2], data[3]]) as u32
|
||||
}
|
||||
object::Endianness::Big => {
|
||||
u16::from_be_bytes([data[2], data[3]]) as u32
|
||||
}
|
||||
},
|
||||
&parse_flags,
|
||||
);
|
||||
if let Some(low) = match second_ins.op {
|
||||
thumb::Opcode::Bl => Some(second_ins.field_low_branch_offset_11()),
|
||||
thumb::Opcode::BlxI => Some(second_ins.field_low_blx_offset_11()),
|
||||
_ => None,
|
||||
} {
|
||||
ins_size = 4;
|
||||
address.checked_add_signed(
|
||||
(ins.field_high_branch_offset_11() + (low as i32)) << 9 >> 9,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
thumb::Opcode::BLong => {
|
||||
address.checked_add_signed(x.field_branch_offset_11())
|
||||
address.checked_add_signed(ins.field_branch_offset_11())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
(opcode, branch_dest)
|
||||
}
|
||||
unarm::Ins::Data => (u16::MAX, None),
|
||||
unarm::ParseMode::Data => (u16::MAX, None),
|
||||
};
|
||||
|
||||
ops.push(ScannedInstruction {
|
||||
ins_ref: InstructionRef { address: address as u64, size: size as u8, opcode },
|
||||
ins_ref: InstructionRef { address: address as u64, size: ins_size as u8, opcode },
|
||||
branch_dest: branch_dest.map(|x| x as u64),
|
||||
});
|
||||
address += ins_size as u32;
|
||||
}
|
||||
|
||||
Ok(ops)
|
||||
@@ -391,7 +472,7 @@ fn push_args(
|
||||
let reloc_arg = find_reloc_arg(parsed_ins, relocation);
|
||||
let mut writeback = false;
|
||||
let mut deref = false;
|
||||
for (i, arg) in parsed_ins.args_iter().enumerate() {
|
||||
for (i, &arg) in parsed_ins.args_iter().enumerate() {
|
||||
// Emit punctuation before separator
|
||||
if deref {
|
||||
match arg {
|
||||
@@ -463,19 +544,19 @@ fn push_args(
|
||||
| args::Argument::CoOpcode(value)
|
||||
| args::Argument::SatImm(value) => {
|
||||
arg_cb(InstructionPart::basic("#"))?;
|
||||
arg_cb(InstructionPart::unsigned(*value))?;
|
||||
arg_cb(InstructionPart::unsigned(value))?;
|
||||
}
|
||||
args::Argument::SImm(value)
|
||||
| args::Argument::OffsetImm(args::OffsetImm { post_indexed: _, value }) => {
|
||||
arg_cb(InstructionPart::basic("#"))?;
|
||||
arg_cb(InstructionPart::signed(*value))?;
|
||||
arg_cb(InstructionPart::signed(value))?;
|
||||
}
|
||||
args::Argument::BranchDest(value) => {
|
||||
arg_cb(InstructionPart::branch_dest(cur_addr.wrapping_add_signed(*value)))?;
|
||||
arg_cb(InstructionPart::branch_dest(cur_addr.wrapping_add_signed(value)))?;
|
||||
}
|
||||
args::Argument::CoOption(value) => {
|
||||
arg_cb(InstructionPart::basic("{"))?;
|
||||
arg_cb(InstructionPart::unsigned(*value))?;
|
||||
arg_cb(InstructionPart::unsigned(value))?;
|
||||
arg_cb(InstructionPart::basic("}"))?;
|
||||
}
|
||||
args::Argument::CoprocNum(value) => {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use alloc::{collections::BTreeMap, string::ToString, vec::Vec};
|
||||
use alloc::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::ops::Range;
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
@@ -15,7 +19,7 @@ use crate::{
|
||||
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory, display::InstructionPart},
|
||||
obj::{
|
||||
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
|
||||
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction,
|
||||
ResolvedInstructionRef, ResolvedRelocation, ScannedInstruction, SymbolFlag, SymbolFlagSet,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -26,6 +30,7 @@ pub struct ArchMips {
|
||||
pub isa_extension: Option<IsaExtension>,
|
||||
pub ri_gp_value: i32,
|
||||
pub paired_relocations: Vec<BTreeMap<u64, i64>>,
|
||||
pub ignored_symbols: BTreeSet<usize>,
|
||||
}
|
||||
|
||||
const EF_MIPS_ABI: u32 = 0x0000F000;
|
||||
@@ -118,7 +123,25 @@ impl ArchMips {
|
||||
paired_relocations[section_index] = addends;
|
||||
}
|
||||
|
||||
Ok(Self { endianness, abi, isa_extension, ri_gp_value, paired_relocations })
|
||||
let mut ignored_symbols = BTreeSet::new();
|
||||
for obj_symbol in object.symbols() {
|
||||
let Ok(name) = obj_symbol.name() else { continue };
|
||||
if let Some(prefix) = name.strip_suffix(".NON_MATCHING") {
|
||||
ignored_symbols.insert(obj_symbol.index().0);
|
||||
if let Some(target_symbol) = object.symbol_by_name(prefix) {
|
||||
ignored_symbols.insert(target_symbol.index().0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
endianness,
|
||||
abi,
|
||||
isa_extension,
|
||||
ri_gp_value,
|
||||
paired_relocations,
|
||||
ignored_symbols,
|
||||
})
|
||||
}
|
||||
|
||||
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
|
||||
@@ -263,6 +286,13 @@ impl Arch for ArchMips {
|
||||
})
|
||||
}
|
||||
|
||||
fn demangle(&self, name: &str) -> Option<String> {
|
||||
cpp_demangle::Symbol::new(name)
|
||||
.ok()
|
||||
.and_then(|s| s.demangle(&cpp_demangle::DemangleOptions::default()).ok())
|
||||
.or_else(|| cwdemangle::demangle(name, &cwdemangle::DemangleOptions::default()))
|
||||
}
|
||||
|
||||
fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
|
||||
match flags {
|
||||
RelocationFlags::Elf(r_type) => match r_type {
|
||||
@@ -294,6 +324,14 @@ impl Arch for ArchMips {
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
|
||||
let mut flags = SymbolFlagSet::default();
|
||||
if self.ignored_symbols.contains(&symbol.index().0) {
|
||||
flags |= SymbolFlag::Ignored;
|
||||
}
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
fn push_args(
|
||||
|
||||
@@ -137,7 +137,7 @@ impl Arch for ArchX86 {
|
||||
{
|
||||
instruction.set_memory_displacement64(PLACEHOLDER);
|
||||
// Formatter always writes the displacement as Int32
|
||||
reloc_replace = Some((OpKind::Memory, NumberKind::Int32, PLACEHOLDER));
|
||||
reloc_replace = Some((OpKind::Memory, 4, PLACEHOLDER));
|
||||
} else if reloc_offset == offsets.immediate_offset() as u64
|
||||
&& reloc_size == offsets.immediate_size()
|
||||
{
|
||||
@@ -155,18 +155,12 @@ impl Arch for ArchX86 {
|
||||
_ => OpKind::default(),
|
||||
}
|
||||
};
|
||||
let number_kind = match reloc_size {
|
||||
2 => NumberKind::UInt16,
|
||||
4 => NumberKind::UInt32,
|
||||
8 => NumberKind::UInt64,
|
||||
_ => NumberKind::default(),
|
||||
};
|
||||
if is_branch {
|
||||
instruction.set_near_branch64(PLACEHOLDER);
|
||||
} else {
|
||||
instruction.set_immediate32(PLACEHOLDER as u32);
|
||||
}
|
||||
reloc_replace = Some((op_kind, number_kind, PLACEHOLDER));
|
||||
reloc_replace = Some((op_kind, reloc_size, PLACEHOLDER));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +245,7 @@ impl Arch for ArchX86 {
|
||||
|
||||
struct InstructionFormatterOutput<'a> {
|
||||
cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>,
|
||||
reloc_replace: Option<(OpKind, NumberKind, u64)>,
|
||||
reloc_replace: Option<(OpKind, usize, u64)>,
|
||||
error: Option<anyhow::Error>,
|
||||
skip_next: bool,
|
||||
}
|
||||
@@ -326,11 +320,18 @@ impl FormatterOutput for InstructionFormatterOutput<'_> {
|
||||
return;
|
||||
}
|
||||
|
||||
if let (Some(operand), Some((target_op_kind, target_number_kind, target_value))) =
|
||||
if let (Some(operand), Some((target_op_kind, reloc_size, target_value))) =
|
||||
(instruction_operand, self.reloc_replace)
|
||||
{
|
||||
#[allow(clippy::match_like_matches_macro)]
|
||||
if instruction.op_kind(operand) == target_op_kind
|
||||
&& number_kind == target_number_kind
|
||||
&& match (number_kind, reloc_size) {
|
||||
(NumberKind::Int8 | NumberKind::UInt8, 1)
|
||||
| (NumberKind::Int16 | NumberKind::UInt16, 2)
|
||||
| (NumberKind::Int32 | NumberKind::UInt32, 4)
|
||||
| (NumberKind::Int64 | NumberKind::UInt64, 8) => true,
|
||||
_ => false,
|
||||
}
|
||||
&& value == target_value
|
||||
{
|
||||
if let Err(e) = (self.cb)(InstructionPart::reloc()) {
|
||||
@@ -571,4 +572,43 @@ mod test {
|
||||
.unwrap();
|
||||
assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_instruction_with_reloc_4() {
|
||||
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
|
||||
let code = [0x8b, 0x15, 0xa4, 0x21, 0x7e, 0x00];
|
||||
let opcode = iced_x86::Mnemonic::Mov as u16;
|
||||
let mut parts = Vec::new();
|
||||
arch.display_instruction(
|
||||
ResolvedInstructionRef {
|
||||
ins_ref: InstructionRef { address: 0x1234, size: 6, opcode },
|
||||
code: &code,
|
||||
relocation: Some(ResolvedRelocation {
|
||||
relocation: &Relocation {
|
||||
flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
|
||||
address: 0x1234 + 2,
|
||||
target_symbol: 0,
|
||||
addend: 0,
|
||||
},
|
||||
symbol: &Default::default(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&DiffObjConfig::default(),
|
||||
&mut |part| {
|
||||
parts.push(part.into_static());
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(parts, &[
|
||||
InstructionPart::opcode("mov", opcode),
|
||||
InstructionPart::opaque("edx"),
|
||||
InstructionPart::basic(","),
|
||||
InstructionPart::basic(" "),
|
||||
InstructionPart::basic("["),
|
||||
InstructionPart::reloc(),
|
||||
InstructionPart::basic("]"),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,27 +139,13 @@ pub fn diff_data_section(
|
||||
) -> Result<(SectionDiff, SectionDiff)> {
|
||||
let left_section = &left_obj.sections[left_section_idx];
|
||||
let right_section = &right_obj.sections[right_section_idx];
|
||||
let left_max = left_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.filter_map(|s| {
|
||||
if s.section != Some(left_section_idx) || s.kind == SymbolKind::Section {
|
||||
return None;
|
||||
}
|
||||
s.address.checked_sub(left_section.address).map(|a| a + s.size)
|
||||
})
|
||||
let left_max = symbols_matching_section(&left_obj.symbols, left_section_idx)
|
||||
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| a + s.size))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.min(left_section.size);
|
||||
let right_max = right_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.filter_map(|s| {
|
||||
if s.section != Some(right_section_idx) || s.kind == SymbolKind::Section {
|
||||
return None;
|
||||
}
|
||||
s.address.checked_sub(right_section.address).map(|a| a + s.size)
|
||||
})
|
||||
let right_max = symbols_matching_section(&right_obj.symbols, right_section_idx)
|
||||
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| a + s.size))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.min(right_section.size);
|
||||
@@ -412,21 +398,13 @@ pub fn diff_generic_section(
|
||||
left_section_idx: usize,
|
||||
_right_section_idx: usize,
|
||||
) -> Result<(SectionDiff, SectionDiff)> {
|
||||
let match_percent = if left_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||
let match_percent = if symbols_matching_section(&left_obj.symbols, left_section_idx)
|
||||
.map(|(i, _)| &left_diff.symbols[i])
|
||||
.all(|d| d.match_percent == Some(100.0))
|
||||
{
|
||||
100.0 // Avoid fp precision issues
|
||||
} else {
|
||||
let (matched, total) = left_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||
let (matched, total) = symbols_matching_section(&left_obj.symbols, left_section_idx)
|
||||
.map(|(i, s)| (s, &left_diff.symbols[i]))
|
||||
.fold((0.0, 0.0), |(matched, total), (s, d)| {
|
||||
(matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32)
|
||||
@@ -449,19 +427,11 @@ pub fn diff_bss_section(
|
||||
right_section_idx: usize,
|
||||
) -> Result<(SectionDiff, SectionDiff)> {
|
||||
let left_section = &left_obj.sections[left_section_idx];
|
||||
let left_sizes = left_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section)
|
||||
let left_sizes = symbols_matching_section(&left_obj.symbols, left_section_idx)
|
||||
.filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size)))
|
||||
.collect::<Vec<_>>();
|
||||
let right_section = &right_obj.sections[right_section_idx];
|
||||
let right_sizes = right_obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, s)| s.section == Some(right_section_idx) && s.kind != SymbolKind::Section)
|
||||
let right_sizes = symbols_matching_section(&right_obj.symbols, right_section_idx)
|
||||
.filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size)))
|
||||
.collect::<Vec<_>>();
|
||||
let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes);
|
||||
@@ -487,3 +457,15 @@ pub fn diff_bss_section(
|
||||
SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] },
|
||||
))
|
||||
}
|
||||
|
||||
fn symbols_matching_section(
|
||||
symbols: &[Symbol],
|
||||
section_idx: usize,
|
||||
) -> impl Iterator<Item = (usize, &Symbol)> + '_ {
|
||||
symbols.iter().enumerate().filter(move |(_, s)| {
|
||||
s.section == Some(section_idx)
|
||||
&& s.kind != SymbolKind::Section
|
||||
&& s.size > 0
|
||||
&& !s.flags.contains(SymbolFlag::Ignored)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ pub fn instruction_hover(
|
||||
out
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum SymbolFilter<'a> {
|
||||
None,
|
||||
Search(&'a Regex),
|
||||
@@ -580,7 +580,11 @@ fn symbol_matches_filter(
|
||||
if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
|
||||
return false;
|
||||
}
|
||||
if !show_hidden_symbols && (symbol.size == 0 || symbol.flags.contains(SymbolFlag::Hidden)) {
|
||||
if !show_hidden_symbols
|
||||
&& (symbol.size == 0
|
||||
|| symbol.flags.contains(SymbolFlag::Hidden)
|
||||
|| symbol.flags.contains(SymbolFlag::Ignored))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
match filter {
|
||||
@@ -701,7 +705,7 @@ pub fn display_sections(
|
||||
});
|
||||
}
|
||||
}
|
||||
sections.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
sections.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
sections
|
||||
}
|
||||
|
||||
|
||||
@@ -371,7 +371,10 @@ fn generate_mapping_symbols(
|
||||
};
|
||||
let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]);
|
||||
for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() {
|
||||
if symbol_section_kind(target_obj, target_symbol) != base_section_kind {
|
||||
if target_symbol.size == 0
|
||||
|| target_symbol.flags.contains(SymbolFlag::Ignored)
|
||||
|| symbol_section_kind(target_obj, target_symbol) != base_section_kind
|
||||
{
|
||||
continue;
|
||||
}
|
||||
match base_section_kind {
|
||||
@@ -526,6 +529,9 @@ fn matching_symbols(
|
||||
)?;
|
||||
}
|
||||
for (symbol_idx, symbol) in left.symbols.iter().enumerate() {
|
||||
if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
|
||||
continue;
|
||||
}
|
||||
let section_kind = symbol_section_kind(left, symbol);
|
||||
if section_kind == SectionKind::Unknown {
|
||||
continue;
|
||||
@@ -547,6 +553,9 @@ fn matching_symbols(
|
||||
}
|
||||
if let Some(right) = right {
|
||||
for (symbol_idx, symbol) in right.symbols.iter().enumerate() {
|
||||
if symbol.size == 0 || symbol.flags.contains(SymbolFlag::Ignored) {
|
||||
continue;
|
||||
}
|
||||
let section_kind = symbol_section_kind(right, symbol);
|
||||
if section_kind == SectionKind::Unknown {
|
||||
continue;
|
||||
@@ -572,9 +581,10 @@ fn unmatched_symbols<'obj, 'used>(
|
||||
where
|
||||
'obj: 'used,
|
||||
{
|
||||
obj.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| {
|
||||
// Skip symbols that have already been matched
|
||||
!used.is_some_and(|u| u.contains(&symbol_idx))
|
||||
obj.symbols.iter().enumerate().filter(move |&(symbol_idx, symbol)| {
|
||||
!symbol.flags.contains(SymbolFlag::Ignored)
|
||||
// Skip symbols that have already been matched
|
||||
&& !used.is_some_and(|u| u.contains(&symbol_idx))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ flags! {
|
||||
HasExtra,
|
||||
/// Symbol size was missing and was inferred
|
||||
SizeInferred,
|
||||
/// Symbol should be ignored by any diffing
|
||||
Ignored,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,3 +15,20 @@ fn read_arm() {
|
||||
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "arm")]
|
||||
fn read_thumb() {
|
||||
let diff_config = diff::DiffObjConfig { mips_register_prefix: true, ..Default::default() };
|
||||
let obj = obj::read::parse(include_object!("data/arm/thumb.o"), &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(obj);
|
||||
let symbol_idx = obj
|
||||
.symbols
|
||||
.iter()
|
||||
.position(|s| s.name == "THUMB_BRANCH_ServerDisplay_UncategorizedMove")
|
||||
.unwrap();
|
||||
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(diff.instruction_rows);
|
||||
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
@@ -37,3 +37,11 @@ fn cross_endian_diff() {
|
||||
assert_eq!(left_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None);
|
||||
assert_eq!(right_diff.instruction_rows[2].kind, diff::InstructionDiffKind::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "mips")]
|
||||
fn filter_non_matching() {
|
||||
let diff_config = diff::DiffObjConfig::default();
|
||||
let obj = obj::read::parse(include_object!("data/mips/vw_main.c.o"), &diff_config).unwrap();
|
||||
insta::assert_debug_snapshot!(obj.symbols);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ fn diff_ppc() {
|
||||
let base_diff = diff.right.as_ref().unwrap();
|
||||
let sections_display = display::display_sections(
|
||||
&target_obj,
|
||||
&target_diff,
|
||||
target_diff,
|
||||
display::SymbolFilter::None,
|
||||
false,
|
||||
false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use objdiff_core::{diff, obj};
|
||||
use objdiff_core::{diff, diff::display::SymbolFilter, obj};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -40,3 +40,18 @@ fn read_x86_64() {
|
||||
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "x86")]
|
||||
fn display_section_ordering() {
|
||||
let diff_config = diff::DiffObjConfig::default();
|
||||
let obj = obj::read::parse(include_object!("data/x86/basenode.obj"), &diff_config).unwrap();
|
||||
let obj_diff =
|
||||
diff::diff_objs(Some(&obj), None, None, &diff_config, &diff::MappingConfig::default())
|
||||
.unwrap()
|
||||
.left
|
||||
.unwrap();
|
||||
let section_display =
|
||||
diff::display::display_sections(&obj, &obj_diff, SymbolFilter::None, false, false, false);
|
||||
insta::assert_debug_snapshot!(section_display);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user