Simple asm diff filter
[ date: 2019-10-12 || tags: asm,perl,diff ]One thing you windows guys have is a decent binary diff. I know there’s radare2
on linux, but I’m a noob to set it up and I’d be fine with something less
fancy. Something like 2 objdump -dr
files, intelligent filter and vimdiff
.
The tricky part is the intelligent filter. I say intelligent, but in fact it’s a 20 line perl script that basically filters out things that are not important or are likely to change in a slightly modified C source.
The result is lossy, eg. there are no addresses and thus jumps can’t be followed, but having that would actually deserve the ‘intelligent’ title. My use case is simpler, eg. doing small tweaks like reordering lines, adding annotations (like READ_ONCE/WRITE_ONCE) or reducing argument counts.
Which takes eg.
0000000000000000 <btrfs_set_lock_blocking_read>:
0: e8 00 00 00 00 callq 5 <btrfs_set_lock_blocking_read+0x5>
1: R_X86_64_PLT32 __fentry__-0x4
5: 55 push %rbp
6: 53 push %rbx
7: 48 89 fb mov %rdi,%rbx
a: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
f: 65 8b 05 00 00 00 00 mov %gs:0x0(%rip),%eax # 16 <btrfs_set_lock_blocking_read+0x16>
12: R_X86_64_PC32 cpu_number-0x4
16: 89 c0 mov %eax,%eax
18: 48 0f a3 05 00 00 00 bt %rax,0x0(%rip) # 20 <btrfs_set_lock_blocking_read+0x20>
1f: 00
1c: R_X86_64_PC32 __cpu_online_mask-0x4
20: 0f 82 b5 00 00 00 jb db <btrfs_set_lock_blocking_read+0xdb>
26: 0f b6 ab 94 00 00 00 movzbl 0x94(%rbx),%ebp
2d: 40 80 fd 01 cmp $0x1,%bpl
31: 0f 87 00 00 00 00 ja 37 <btrfs_set_lock_blocking_read+0x37>
33: R_X86_64_PC32 .text.unlikely+0x11
37: 83 e5 01 and $0x1,%ebp
3a: 74 1b je 57 <btrfs_set_lock_blocking_read+0x57>
3c: 8b 8b 88 00 00 00 mov 0x88(%rbx),%ecx
42: 65 48 8b 04 25 00 00 mov %gs:0x0,%rax
49: 00 00
47: R_X86_64_32S current_task
4b: 39 88 c0 04 00 00 cmp %ecx,0x4c0(%rax)
51: 0f 84 bd 00 00 00 je 114 <btrfs_set_lock_blocking_read+0x114>
57: 8b 83 18 02 00 00 mov 0x218(%rbx),%eax
5d: 85 c0 test %eax,%eax
5f: 0f 84 b2 00 00 00 je 117 <btrfs_set_lock_blocking_read+0x117>
65: f0 ff 83 90 00 00 00 lock incl 0x90(%rbx)
6c: 8b 83 14 02 00 00 mov 0x214(%rbx),%eax
72: 85 c0 test %eax,%eax
74: 0f 84 ab 00 00 00 je 125 <btrfs_set_lock_blocking_read+0x125>
7a: f0 ff 8b 14 02 00 00 lock decl 0x214(%rbx)
81: 48 8d bb 98 00 00 00 lea 0x98(%rbx),%rdi
88: 5b pop %rbx
89: 5d pop %rbp
...
and produces
0000000000000000 <btrfs_set_lock_blocking_read>:
callq btrfs_set_lock_blocking_read
TARGET __fentry__
push %rbp
push %rbx
mov %rdi,%rbx
NOP
mov %gs:0x0(%rip),%eax
mov %eax,%eax
bt %rax,0x0(%rip)
jb btrfs_set_lock_blocking_read
movzbl 0x94(%rbx),%ebp
cmp $0x1,%bpl
ja btrfs_set_lock_blocking_read
and $0x1,%ebp
je btrfs_set_lock_blocking_read
mov 0x88(%rbx),%ecx
mov %gs:0x0,%rax
cmp %ecx,0x4c0(%rax)
je btrfs_set_lock_blocking_read
mov 0x218(%rbx),%eax
test %eax,%eax
je btrfs_set_lock_blocking_read
lock incl 0x90(%rbx)
mov 0x214(%rbx),%eax
test %eax,%eax
je btrfs_set_lock_blocking_read
lock decl 0x214(%rbx)
lea 0x98(%rbx),%rdi
pop %rbx
pop %rbp
...
This looks quite simple and when lined together, the diff is readable.
So here’s the magic script (pardon my perl skills):
#!/usr/bin/perl
@c=<>;
foreach(@c) {
chomp;
s/.*R_X86_64_PLT32\s+([^+-]+)[+-].*/TARGET $1/;
next if(/R_X86_/);
next if(/^\s*[0-9a-f]+:\s*([0-9a-f][0-9a-f]\s)+$/);
s/^\s*[0-9a-f]+:\s*([0-9a-f][0-9a-f]\s)+\s*//;
s/[0-9a-f]+ <([^+]+)\+.*>$/$1/;
s/\s+#.*$//;
s/nopl.*/NOP/;
s/xchg.*ax.*ax/NOP/;
s/data16 nop/NOP/;
s/nop/NOP/;
print("$_\n");
}
Use like:
$ objdump -dr before/locking.o > before
$ objdump -dr after/locking.o > after
$ vimdiff before after