Files
swift-mirror/utils/lldb/lldbToolBox.py
Michael Gottesman d3e6ffdbad [lldb-toolbox] Add the ability to disassemble-to-file a specific function from a target without running the target.
The routine already supports dumping the assembly to file if we are at a
breakpoint using the current frame. This adds a -n option so one can without
running just dump the assembly to a file of a specific function in a binary.
2020-10-06 14:18:58 -05:00

144 lines
5.3 KiB
Python

"""
LLDB Helpers for working with the swift compiler.
Load into LLDB with 'command script import /path/to/lldbToolBox.py'
This will also import LLVM data formatters as well, assuming that llvm is next
to the swift checkout.
"""
from __future__ import print_function
import argparse
import os
import shlex
import subprocess
import sys
import tempfile
import lldb
REPO_BASE = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir,
os.pardir, os.pardir))
SWIFT_REPO = os.path.join(REPO_BASE, "swift")
LLVM_REPO = os.path.join(REPO_BASE, "llvm-project")
LLVM_DATAFORMATTER_PATH = os.path.join(LLVM_REPO, "llvm", "utils",
"lldbDataFormatters.py")
SWIFT_DATAFORMATTER_PATH = os.path.join(SWIFT_REPO, "utils",
"lldb", "lldbSwiftDataFormatters.py")
def import_llvm_dataformatters(debugger):
if not os.access(LLVM_DATAFORMATTER_PATH, os.F_OK):
print("WARNING! Could not find LLVM data formatters!")
return
cmd = 'command script import {}'.format(LLVM_DATAFORMATTER_PATH)
debugger.HandleCommand(cmd)
print("Loaded LLVM data formatters.")
def import_swift_dataformatters(debugger):
if not os.access(SWIFT_DATAFORMATTER_PATH, os.F_OK):
print("WARNING! Could not find Swift data formatters!")
return
cmd = 'command script import {}'.format(SWIFT_DATAFORMATTER_PATH)
debugger.HandleCommand(cmd)
print("Loaded Swift data formatters.")
VIEWCFG_PATH = os.path.join(SWIFT_REPO, "utils", "viewcfg")
BLOCKIFYASM_PATH = os.path.join(SWIFT_REPO, "utils", "dev-scripts",
"blockifyasm")
def disassemble_asm_cfg(debugger, command, exec_ctx, result, internal_dict):
"""
This function disassembles the current assembly frame into a temporary file
and then uses that temporary file as input to blockifyasm | viewcfg. This
will cause a pdf of the cfg to be opened on Darwin.
"""
d = exec_ctx.frame.Disassemble()
with tempfile.TemporaryFile() as f:
f.write(bytes(d, 'utf-8'))
f.flush()
f.seek(0)
p1 = subprocess.Popen([BLOCKIFYASM_PATH], stdin=f,
stdout=subprocess.PIPE)
subprocess.Popen([VIEWCFG_PATH], stdin=p1.stdout)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
def disassemble_to_file(debugger, command, exec_ctx, result, internal_dict):
"""This function disassembles the current assembly frame into a file specified
by the user.
"""
parser = argparse.ArgumentParser(prog='disassemble-to-file', description="""
Dump the disassembly of the current frame or specified function to the
specified file.
""")
parser.add_argument('file', type=argparse.FileType('w'),
default=sys.stdout)
parser.add_argument('-n', dest='func_name', help="""
Function name to disassembly. Frame used if unset.""")
args = parser.parse_args(shlex.split(command))
if args.func_name is None:
args.file.write(exec_ctx.frame.disassembly)
else:
name = args.func_name
result = exec_ctx.target.FindFunctions(name)
if result is None:
raise RuntimeError('No function with name: {}'.format(name))
if len(result) > 1:
errorStr = 'Matched multiple functions to name: {}'
raise RuntimeError(errorStr.format(name))
f = result[0].GetFunction()
args.file.write(str(f.GetInstructions(exec_ctx.target)) + "\n")
def sequence(debugger, command, exec_ctx, result, internal_dict):
"""
Combine multiple semicolon separated lldb commands into one command.
This command is particularly useful for defining aliases and breakpoint
commands. Some examples:
# Define an alias that prints rax and also steps one instruction.
command alias xs sequence p/x $rax; stepi
# Breakpoint command to show the frame's info and arguments.
breakpoint command add -o 'seq frame info; reg read arg1 arg2 arg3'
# Override `b` to allow a condition to be specified. For example:
# b someMethod if someVar > 2
command regex b
s/(.+) if (.+)/seq _regexp-break %1; break mod -c "%2"/
s/(.*)/_regexp-break %1/
"""
interpreter = debugger.GetCommandInterpreter()
for subcommand in command.split(';'):
subcommand = subcommand.strip()
if not subcommand:
continue # skip empty commands
ret = lldb.SBCommandReturnObject()
interpreter.HandleCommand(subcommand, exec_ctx, ret)
if ret.GetOutput():
print(ret.GetOutput().strip(), file=result)
if not ret.Succeeded():
result.SetError(ret.GetError())
result.SetStatus(ret.GetStatus())
return
def __lldb_init_module(debugger, internal_dict):
import_llvm_dataformatters(debugger)
import_swift_dataformatters(debugger)
debugger.HandleCommand('command script add disassemble-asm-cfg '
'-f lldbToolBox.disassemble_asm_cfg')
debugger.HandleCommand('command script add disassemble-to-file '
'-f lldbToolBox.disassemble_to_file')
debugger.HandleCommand('command script add sequence '
'-h "Run multiple semicolon separated commands" '
'-f lldbToolBox.sequence')