summaryrefslogtreecommitdiff
path: root/diff.py
diff options
context:
space:
mode:
Diffstat (limited to 'diff.py')
-rwxr-xr-xdiff.py510
1 files changed, 171 insertions, 339 deletions
diff --git a/diff.py b/diff.py
index 013d93594..8dec9ef18 100755
--- a/diff.py
+++ b/diff.py
@@ -5,7 +5,6 @@ import os
import ast
import argparse
import subprocess
-import collections
import difflib
import string
import itertools
@@ -21,7 +20,7 @@ def fail(msg):
MISSING_PREREQUISITES = (
"Missing prerequisite python module {}. "
- "Run `python3 -m pip install --user colorama ansiwrap attrs watchdog python-Levenshtein cxxfilt` to install prerequisites (cxxfilt only needed with --source)."
+ "Run `python3 -m pip install --user colorama ansiwrap attrs watchdog python-Levenshtein` to install prerequisites (python-Levenshtein only needed for --algorithm=levenshtein)."
)
try:
@@ -51,21 +50,6 @@ parser.add_argument(
help="Diff .o files rather than a whole binary. This makes it possible to see symbol names. (Recommended)",
)
parser.add_argument(
- "-e",
- dest="diff_elf_symbol",
- help="Diff a given function in two ELFs, one being stripped and the other one non-stripped. Requires objdump from binutils 2.33+.",
-)
-parser.add_argument(
- "--source",
- action="store_true",
- help="Show source code (if possible). Only works with -o and -e.",
-)
-parser.add_argument(
- "--inlines",
- action="store_true",
- help="Show inline function calls (if possible). Only works with -o and -e.",
-)
-parser.add_argument(
"--base-asm",
dest="base_asm",
metavar="FILE",
@@ -142,20 +126,11 @@ parser.add_argument(
parser.add_argument(
"--algorithm",
dest="algorithm",
- default="levenshtein",
+ default="difflib",
choices=["levenshtein", "difflib"],
help="Diff algorithm to use.",
)
-parser.add_argument(
- "--max-size",
- "--max-lines",
- dest="max_lines",
- type=int,
- default=1024,
- help="The maximum length of the diff, in lines.",
-)
-
# Project-specific flags, e.g. different versions/make arguments.
if hasattr(diff_settings, "add_custom_arguments"):
diff_settings.add_custom_arguments(parser)
@@ -166,15 +141,13 @@ args = parser.parse_args()
config = {}
diff_settings.apply(config, args)
-arch = config.get("arch", "mips")
baseimg = config.get("baseimg", None)
myimg = config.get("myimg", None)
mapfile = config.get("mapfile", None)
makeflags = config.get("makeflags", [])
source_directories = config.get("source_directories", None)
-objdump_executable = config.get("objdump_executable", None)
-MAX_FUNCTION_SIZE_LINES = args.max_lines
+MAX_FUNCTION_SIZE_LINES = 4096
MAX_FUNCTION_SIZE_BYTES = MAX_FUNCTION_SIZE_LINES * 4
COLOR_ROTATION = [
@@ -190,7 +163,7 @@ COLOR_ROTATION = [
]
BUFFER_CMD = ["tail", "-c", str(10 ** 9)]
-LESS_CMD = ["less", "-SRic", "-#6"]
+LESS_CMD = ["less", "-Ric"]
DEBOUNCE_DELAY = 0.1
FS_WATCH_EXTENSIONS = [".c", ".h"]
@@ -203,30 +176,25 @@ if args.algorithm == "levenshtein":
except ModuleNotFoundError as e:
fail(MISSING_PREREQUISITES.format(e.name))
-if args.source:
- try:
- import cxxfilt
- except ModuleNotFoundError as e:
- fail(MISSING_PREREQUISITES.format(e.name))
-
-if objdump_executable is None:
- for objdump_cand in ["mips-linux-gnu-objdump", "mips64-elf-objdump"]:
- try:
- subprocess.check_call(
- [objdump_cand, "--version"],
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL,
- )
- objdump_executable = objdump_cand
- break
- except subprocess.CalledProcessError:
- pass
- except FileNotFoundError:
- pass
+binutils_prefix = None
-if not objdump_executable:
+for binutils_cand in ["mips-linux-gnu-", "mips64-elf-"]:
+ try:
+ subprocess.check_call(
+ [binutils_cand + "objdump", "--version"],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+ binutils_prefix = binutils_cand
+ break
+ except subprocess.CalledProcessError:
+ pass
+ except FileNotFoundError:
+ pass
+
+if not binutils_prefix:
fail(
- "Missing binutils; please ensure mips-linux-gnu-objdump or mips64-elf-objdump exist, or configure objdump_executable."
+ "Missing binutils; please ensure mips-linux-gnu-objdump or mips64-elf-objdump exist."
)
@@ -242,10 +210,6 @@ def eval_int(expr, emsg=None):
return None
-def eval_line_num(expr):
- return int(expr.strip().replace(":", ""), 16)
-
-
def run_make(target, capture_output=False):
if capture_output:
return subprocess.run(
@@ -271,26 +235,10 @@ def restrict_to_function(dump, fn_name):
return "\n".join(out)
-def maybe_get_objdump_source_flags():
- if not args.source:
- return []
-
- flags = [
- "--source",
- "--source-comment=| ",
- "-l",
- ]
-
- if args.inlines:
- flags.append("--inlines")
-
- return flags
-
-
def run_objdump(cmd):
flags, target, restrict = cmd
out = subprocess.check_output(
- [objdump_executable] + arch_flags + flags + [target], universal_newlines=True
+ [binutils_prefix + "objdump"] + flags + [target], universal_newlines=True
)
if restrict is not None:
return restrict_to_function(out, restrict)
@@ -343,36 +291,6 @@ def search_map_file(fn_name):
return None, None
-def dump_elf():
- if not baseimg or not myimg:
- fail("Missing myimg/baseimg in config.")
- if base_shift:
- fail("--base-shift not compatible with -e")
-
- start_addr = eval_int(args.start, "Start address must be an integer expression.")
-
- if args.end is not None:
- end_addr = eval_int(args.end, "End address must be an integer expression.")
- else:
- end_addr = start_addr + MAX_FUNCTION_SIZE_BYTES
-
- flags1 = [
- f"--start-address={start_addr}",
- f"--stop-address={end_addr}",
- ]
-
- flags2 = [
- f"--disassemble={args.diff_elf_symbol}",
- ]
-
- objdump_flags = ["-drz", "-j", ".text"]
- return (
- myimg,
- (objdump_flags + flags1, baseimg, None),
- (objdump_flags + flags2 + maybe_get_objdump_source_flags(), myimg, None),
- )
-
-
def dump_objfile():
if base_shift:
fail("--base-shift not compatible with -o")
@@ -399,7 +317,7 @@ def dump_objfile():
return (
objfile,
(objdump_flags, refobjfile, args.start),
- (objdump_flags + maybe_get_objdump_source_flags(), objfile, args.start),
+ (objdump_flags, objfile, args.start),
)
@@ -439,47 +357,29 @@ def ansi_ljust(s, width):
return s
-if arch == "mips":
- re_int = re.compile(r"[0-9]+")
- re_comment = re.compile(r"<.*?>")
- re_reg = re.compile(r"\$?\b(a[0-3]|t[0-9]|s[0-8]|at|v[01]|f[12]?[0-9]|f3[01]|k[01]|fp|ra)\b")
- re_sprel = re.compile(r"(?<=,)([0-9]+|0x[0-9a-f]+)\(sp\)")
- re_large_imm = re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}")
- re_imm = re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi)\([^)]*\)")
- forbidden = set(string.ascii_letters + "_")
- arch_flags = ["-m", "mips:4300"]
- branch_likely_instructions = {
- "beql",
- "bnel",
- "beqzl",
- "bnezl",
- "bgezl",
- "bgtzl",
- "blezl",
- "bltzl",
- "bc1tl",
- "bc1fl",
- }
- branch_instructions = branch_likely_instructions.union(
- {"b", "beq", "bne", "beqz", "bnez", "bgez", "bgtz", "blez", "bltz", "bc1t", "bc1f"}
- )
- instructions_with_address_immediates = branch_instructions.union({"jal", "j"})
-elif arch == "aarch64":
- re_int = re.compile(r"[0-9]+")
- re_comment = re.compile(r"(<.*?>|//.*$)")
- # GPRs and FP registers: X0-X30, W0-W30, [DSHQ]0..31
- # The zero registers and SP should not be in this list.
- re_reg = re.compile(r"\$?\b([dshq][12]?[0-9]|[dshq]3[01]|[xw][12]?[0-9]|[xw]30)\b")
- re_sprel = re.compile(r"sp, #-?(0x[0-9a-fA-F]+|[0-9]+)\b")
- re_large_imm = re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}")
- re_imm = re.compile(r"(?<!sp, )#-?(0x[0-9a-fA-F]+|[0-9]+)\b")
- arch_flags = []
- forbidden = set(string.ascii_letters + "_")
- branch_likely_instructions = set()
- branch_instructions = {"bl", "b", "b.eq", "b.ne", "b.cs", "b.hs", "b.cc", "b.lo", "b.mi", "b.pl", "b.vs", "b.vc", "b.hi", "b.ls", "b.ge", "b.lt", "b.gt", "b.le", "cbz", "cbnz", "tbz", "tbnz"}
- instructions_with_address_immediates = branch_instructions.union({"adrp"})
-else:
- fail("Unknown architecture.")
+re_int = re.compile(r"[0-9]+")
+re_comments = re.compile(r"<.*?>")
+re_regs = re.compile(r"\$?\b(a[0-3]|t[0-9]|s[0-8]|at|v[01]|f[12]?[0-9]|f3[01]|fp)\b")
+re_sprel = re.compile(r",([1-9][0-9]*|0x[1-9a-f][0-9a-f]*)\(sp\)")
+re_large_imm = re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}")
+re_imm = re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi)\([^)]*\)")
+forbidden = set(string.ascii_letters + "_")
+branch_likely_instructions = {
+ "beql",
+ "bnel",
+ "beqzl",
+ "bnezl",
+ "bgezl",
+ "bgtzl",
+ "blezl",
+ "bltzl",
+ "bc1tl",
+ "bc1fl",
+}
+branch_instructions = branch_likely_instructions.union(
+ {"b", "beq", "bne", "beqz", "bnez", "bgez", "bgtz", "blez", "bltz", "bc1t", "bc1f"}
+)
+jump_instructions = branch_instructions.union({"jal", "j"})
def hexify_int(row, pat):
@@ -512,18 +412,11 @@ def parse_relocated_line(line):
return before, imm, after
-def process_mips_reloc(row, prev):
+def process_reloc(row, prev):
before, imm, after = parse_relocated_line(prev)
repl = row.split()[-1]
if imm != "0":
- # MIPS uses relocations with addends embedded in the code as immediates.
- # If there is an immediate, show it as part of the relocation. Ideally
- # we'd show this addend in both %lo/%hi, but annoyingly objdump's output
- # doesn't include enough information to pair up %lo's and %hi's...
- # TODO: handle unambiguous cases where all addends for a symbol are the
- # same, or show "+???".
- mnemonic = prev.split()[0]
- if mnemonic in instructions_with_address_immediates and not imm.startswith("0x"):
+ if before.strip() == "jal" and not imm.startswith("0x"):
imm = "0x" + imm
repl += "+" + imm if int(imm, 0) > 0 else imm
if "R_MIPS_LO16" in row:
@@ -538,63 +431,38 @@ def process_mips_reloc(row, prev):
return before + repl + after
-def cleanup_whitespace(line):
- return "".join(f"{o:<8s}" for o in line.strip().split("\t"))
-
-
-Line = collections.namedtuple(
- "Line",
- [
- "mnemonic",
- "diff_row",
- "original",
- "line_num",
- "branch_target",
- "source_lines",
- "comment",
- ],
-)
-
-
def process(lines):
+ mnemonics = []
+ diff_rows = []
+ rows_with_imms = []
skip_next = False
- source_lines = []
+ originals = []
+ line_nums = []
+ branch_targets = []
if not args.diff_obj:
lines = lines[7:]
if lines and not lines[-1]:
lines.pop()
- output = []
for row in lines:
if args.diff_obj and (">:" in row or not row):
continue
- if args.source and (row and row[0] != " "):
- source_lines.append(row)
- continue
-
- if "R_AARCH64_" in row:
- # TODO: handle relocation
- continue
-
if "R_MIPS_" in row:
# N.B. Don't transform the diff rows, they already ignore immediates
- # if output[-1].diff_row != "<delay-slot>":
- # output[-1] = output[-1].replace(diff_row=process_mips_reloc(row, output[-1].row_with_imm))
- new_original = process_mips_reloc(row, output[-1].original)
- output[-1] = output[-1]._replace(original=new_original)
+ # if diff_rows[-1] != '<delay-slot>':
+ # diff_rows[-1] = process_reloc(row, rows_with_imms[-1])
+ originals[-1] = process_reloc(row, originals[-1])
continue
- m_comment = re.search(re_comment, row)
- comment = m_comment[0] if m_comment else None
- row = re.sub(re_comment, "", row)
+ row = re.sub(re_comments, "", row)
row = row.rstrip()
tabs = row.split("\t")
row = "\t".join(tabs[2:])
line_num = tabs[0].strip()
row_parts = row.split("\t", 1)
mnemonic = row_parts[0].strip()
- if mnemonic not in instructions_with_address_immediates:
+ if mnemonic not in jump_instructions:
row = re.sub(re_int, lambda s: hexify_int(row, s), row)
original = row
if skip_next:
@@ -603,45 +471,38 @@ def process(lines):
mnemonic = "<delay-slot>"
if mnemonic in branch_likely_instructions:
skip_next = True
- row = re.sub(re_reg, "<reg>", row)
- row = re.sub(re_sprel, "addr(sp)", row)
+ row = re.sub(re_regs, "<reg>", row)
+ row = re.sub(re_sprel, ",addr(sp)", row)
row_with_imm = row
- if mnemonic in instructions_with_address_immediates:
+ if mnemonic in jump_instructions:
row = row.strip()
row, _ = split_off_branch(row)
row += "<imm>"
else:
- row = normalize_imms(row)
+ row = re.sub(re_imm, "<imm>", row)
- branch_target = None
+ mnemonics.append(mnemonic)
+ rows_with_imms.append(row_with_imm)
+ diff_rows.append(row)
+ originals.append(original)
+ line_nums.append(line_num)
if mnemonic in branch_instructions:
target = row_parts[1].strip().split(",")[-1]
if mnemonic in branch_likely_instructions:
target = hex(int(target, 16) - 4)[2:]
- branch_target = target.strip()
-
- output.append(
- Line(
- mnemonic=mnemonic,
- diff_row=row,
- original=original,
- line_num=line_num,
- branch_target=branch_target,
- source_lines=source_lines,
- comment=comment,
- )
- )
- source_lines = []
-
+ branch_targets.append(target)
+ else:
+ branch_targets.append(None)
if args.stop_jrra and mnemonic == "jr" and row_parts[1].strip() == "ra":
break
- # Cleanup whitespace, after relocation fixups have happened
- output = [
- line._replace(original=cleanup_whitespace(line.original)) for line in output
+ # Cleanup whitespace
+ originals = [original.strip() for original in originals]
+ originals = [
+ "".join(f"{o:<8s}" for o in original.split("\t")) for original in originals
]
-
- return output
+ # return diff_rows, diff_rows, line_nums
+ return mnemonics, diff_rows, originals, line_nums, branch_targets
def format_single_line_diff(line1, line2, column_width):
@@ -674,14 +535,10 @@ def normalize_imms(row):
return re.sub(re_imm, "<imm>", row)
-def normalize_stack(row):
- return re.sub(re_sprel, "addr(sp)", row)
-
-
def split_off_branch(line):
parts = line.split(",")
if len(parts) < 2:
- parts = line.split(None, 1)
+ parts = line.split()
off = len(line) - len(parts[-1])
return line[:off], line[off:]
@@ -744,13 +601,20 @@ def diff_sequences(seq1, seq2):
def do_diff(basedump, mydump):
+ asm_lines1 = basedump.split("\n")
+ asm_lines2 = mydump.split("\n")
+
output = []
# TODO: status line?
# output.append(sha1sum(mydump))
- lines1 = process(basedump.split("\n"))
- lines2 = process(mydump.split("\n"))
+ mnemonics1, asm_lines1, originals1, line_nums1, branch_targets1 = process(
+ asm_lines1
+ )
+ mnemonics2, asm_lines2, originals2, line_nums2, branch_targets2 = process(
+ asm_lines2
+ )
sc1 = SymbolColorer(0)
sc2 = SymbolColorer(0)
@@ -762,160 +626,130 @@ def do_diff(basedump, mydump):
bts2 = set()
if args.show_branches:
- for (lines, btset, sc) in [
- (lines1, bts1, sc5),
- (lines2, bts2, sc6),
+ for (bts, btset, sc) in [
+ (branch_targets1, bts1, sc5),
+ (branch_targets2, bts2, sc6),
]:
- for line in lines:
- bt = line.branch_target
+ for bt in bts:
if bt is not None:
btset.add(bt + ":")
sc.color_symbol(bt + ":")
- for (tag, i1, i2, j1, j2) in diff_sequences(
- [line.mnemonic for line in lines1], [line.mnemonic for line in lines2]
- ):
- for line1, line2 in itertools.zip_longest(lines1[i1:i2], lines2[j1:j2]):
+ for (tag, i1, i2, j1, j2) in diff_sequences(mnemonics1, mnemonics2):
+ lines1 = asm_lines1[i1:i2]
+ lines2 = asm_lines2[j1:j2]
+
+ for k, (line1, line2) in enumerate(itertools.zip_longest(lines1, lines2)):
if tag == "replace":
if line1 is None:
tag = "insert"
elif line2 is None:
tag = "delete"
- elif tag == "insert":
- assert line1 is None
- elif tag == "delete":
- assert line2 is None
+
+ try:
+ original1 = originals1[i1 + k]
+ line_num1 = line_nums1[i1 + k]
+ except:
+ original1 = ""
+ line_num1 = ""
+ try:
+ original2 = originals2[j1 + k]
+ line_num2 = line_nums2[j1 + k]
+ except:
+ original2 = ""
+ line_num2 = ""
line_color1 = line_color2 = sym_color = Fore.RESET
line_prefix = " "
- if line1 and line2 and line1.diff_row == line2.diff_row:
- if maybe_normalize_large_imms(
- line1.original
- ) == maybe_normalize_large_imms(line2.original):
- out1 = line1.original
- out2 = line2.original
- elif line1.diff_row == "<delay-slot>":
- out1 = f"{Style.BRIGHT}{Fore.LIGHTBLACK_EX}{line1.original}"
- out2 = f"{Style.BRIGHT}{Fore.LIGHTBLACK_EX}{line2.original}"
+ if line1 == line2:
+ if maybe_normalize_large_imms(original1) == maybe_normalize_large_imms(
+ original2
+ ):
+ out1 = f"{original1}"
+ out2 = f"{original2}"
+ elif line1 == "<delay-slot>":
+ out1 = f"{Style.DIM}{original1}"
+ out2 = f"{Style.DIM}{original2}"
else:
- mnemonic = line1.original.split()[0]
- out1, out2 = line1.original, line2.original
+ mnemonic = original1.split()[0]
+ out1, out2 = original1, original2
branch1 = branch2 = ""
- if mnemonic in instructions_with_address_immediates:
- out1, branch1 = split_off_branch(line1.original)
- out2, branch2 = split_off_branch(line2.original)
+ if mnemonic in jump_instructions:
+ out1, branch1 = split_off_branch(original1)
+ out2, branch2 = split_off_branch(original2)
branchless1 = out1
branchless2 = out2
out1, out2 = color_imms(out1, out2)
-
- same_relative_target = False
- if line1.branch_target is not None and line2.branch_target is not None:
- relative_target1 = eval_line_num(line1.branch_target) - eval_line_num(line1.line_num)
- relative_target2 = eval_line_num(line2.branch_target) - eval_line_num(line2.line_num)
- same_relative_target = relative_target1 == relative_target2
-
- if not same_relative_target:
- branch1, branch2 = color_branch_imms(branch1, branch2)
-
+ branch1, branch2 = color_branch_imms(branch1, branch2)
out1 += branch1
out2 += branch2
if normalize_imms(branchless1) == normalize_imms(branchless2):
- if not same_relative_target:
- # only imms differences
- sym_color = Fore.LIGHTBLUE_EX
- line_prefix = "i"
+ # only imms differences
+ sym_color = Fore.LIGHTBLUE_EX
+ line_prefix = "i"
else:
+ # regs differences and maybe imms as well
+ line_color1 = line_color2 = sym_color = Fore.YELLOW
+ line_prefix = "r"
out1 = re.sub(
- re_sprel, lambda s: sc3.color_symbol(s.group()), out1,
+ re_regs, lambda s: sc1.color_symbol(s.group()), out1
)
out2 = re.sub(
- re_sprel, lambda s: sc4.color_symbol(s.group()), out2,
+ re_regs, lambda s: sc2.color_symbol(s.group()), out2
)
- if normalize_stack(branchless1) == normalize_stack(branchless2):
- # only stack differences (luckily stack and imm
- # differences can't be combined in MIPS, so we
- # don't have to think about that case)
- sym_color = Fore.YELLOW
- line_prefix = "s"
- else:
- # regs differences and maybe imms as well
- out1 = re.sub(
- re_reg, lambda s: sc1.color_symbol(s.group()), out1
- )
- out2 = re.sub(
- re_reg, lambda s: sc2.color_symbol(s.group()), out2
- )
- line_color1 = line_color2 = sym_color = Fore.YELLOW
- line_prefix = "r"
- elif line1 and line2:
+ out1 = re.sub(
+ re_sprel, lambda s: sc3.color_symbol(s.group()), out1
+ )
+ out2 = re.sub(
+ re_sprel, lambda s: sc4.color_symbol(s.group()), out2
+ )
+ out1 = f"{Fore.YELLOW}{out1}{Style.RESET_ALL}"
+ out2 = f"{Fore.YELLOW}{out2}{Style.RESET_ALL}"
+ elif tag in ["replace", "equal"]:
line_prefix = "|"
line_color1 = Fore.LIGHTBLUE_EX
line_color2 = Fore.LIGHTBLUE_EX
sym_color = Fore.LIGHTBLUE_EX
- out1 = line1.original
- out2 = line2.original
- elif line1:
+ out1 = f"{Fore.LIGHTBLUE_EX}{original1}{Style.RESET_ALL}"
+ out2 = f"{Fore.LIGHTBLUE_EX}{original2}{Style.RESET_ALL}"
+ elif tag == "delete":
line_prefix = "<"
line_color1 = line_color2 = sym_color = Fore.RED
- out1 = line1.original
+ out1 = f"{Fore.RED}{original1}{Style.RESET_ALL}"
out2 = ""
- elif line2:
+ elif tag == "insert":
line_prefix = ">"
line_color1 = line_color2 = sym_color = Fore.GREEN
out1 = ""
- out2 = line2.original
+ out2 = f"{Fore.GREEN}{original2}{Style.RESET_ALL}"
in_arrow1 = " "
in_arrow2 = " "
out_arrow1 = ""
out_arrow2 = ""
-
- if args.show_branches and line1:
- if line1.line_num in bts1:
- in_arrow1 = sc5.color_symbol(line1.line_num, "~>") + line_color1
- if line1.branch_target is not None:
- out_arrow1 = " " + sc5.color_symbol(line1.branch_target + ":", "~>")
- if args.show_branches and line2:
- if line2.line_num in bts2:
- in_arrow2 = sc6.color_symbol(line2.line_num, "~>") + line_color2
- if line2.branch_target is not None:
- out_arrow2 = " " + sc6.color_symbol(line2.branch_target + ":", "~>")
-
- if args.source and line2 and line2.comment:
- out2 += f" {line2.comment}"
-
- line_num1 = line1.line_num if line1 else ""
- line_num2 = line2.line_num if line2 else ""
-
- out1 = f"{line_color1}{line_num1} {in_arrow1} {out1}{Style.RESET_ALL}{out_arrow1}"
- out2 = f"{line_color2}{line_num2} {in_arrow2} {out2}{Style.RESET_ALL}{out_arrow2}"
- mid = f"{sym_color}{line_prefix} "
-
- if line2:
- for source_line in line2.source_lines:
- color = Style.DIM
- # File names and function names
- if source_line and source_line[0] != "|":
- color += Style.BRIGHT
- # Function names
- if source_line.endswith("():"):
- # Underline. Colorama does not provide this feature, unfortunately.
- color += "\u001b[4m"
- try:
- source_line = cxxfilt.demangle(
- source_line[:-3], external_only=False
- )
- except:
- pass
- output.append(
- format_single_line_diff(
- "",
- f" {color}{source_line}{Style.RESET_ALL}",
- args.column_width,
- )
+ line_num1 = line_num1 if out1 else ""
+ line_num2 = line_num2 if out2 else ""
+
+ if args.show_branches and out1:
+ if line_num1 in bts1:
+ in_arrow1 = sc5.color_symbol(line_num1, "~>")
+ if branch_targets1[i1 + k] is not None:
+ out_arrow1 = " " + sc5.color_symbol(
+ branch_targets1[i1 + k] + ":", "~>"
+ )
+ if args.show_branches and out2:
+ if line_num2 in bts2:
+ in_arrow2 = sc6.color_symbol(line_num2, "~>")
+ if branch_targets2[j1 + k] is not None:
+ out_arrow2 = " " + sc6.color_symbol(
+ branch_targets2[j1 + k] + ":", "~>"
)
- output.append(format_single_line_diff(out1, mid + out2, args.column_width))
+ if sym_color == line_color2:
+ line_color2 = ""
+ out1 = f"{line_color1}{line_num1} {in_arrow1} {out1}{Style.RESET_ALL}{out_arrow1}"
+ out2 = f"{sym_color}{line_prefix} {line_color2}{line_num2} {in_arrow2} {out2}{Style.RESET_ALL}{out_arrow2}"
+ output.append(format_single_line_diff(out1, out2, args.column_width))
return output[args.skip_lines :]
@@ -1078,16 +912,14 @@ class Display:
def main():
- if args.diff_elf_symbol:
- make_target, basecmd, mycmd = dump_elf()
- elif args.diff_obj:
+ if args.diff_obj:
make_target, basecmd, mycmd = dump_objfile()
else:
make_target, basecmd, mycmd = dump_binary()
if args.write_asm is not None:
mydump = run_objdump(mycmd)
- with open(args.write_asm, "w") as f:
+ with open(args.write_asm) as f:
f.write(mydump)
print(f"Wrote assembly to {args.write_asm}.")
sys.exit(0)
@@ -1148,4 +980,4 @@ def main():
display.terminate()
-main()
+main() \ No newline at end of file