LLDB与Python
加入Python模块
首先获取lldb的python模块路径
lldb -P
在Python的site-packages路径下加入.pth文件,比如Extra.pth
将模块路径写入文件即可
非command中运行
可以直接通过接口获得一个调试器的实例
lldb.debugger = lldb.SBDebugger.Create()
lldb.py提供的基本接口有以下几种:
lldb.SBDebugger : 代表调试器实例的类,拥有命令行解析和所有调试目标
lldb.SBTarget : 代表当前调试目标
lldb.SBProcess : 代表当前调试目标的执行进程,管理进程并且可访问全部内存空间
lldb.SBThread : 代表当前选择的线程,并且管理全部栈帧。及时当前Target停止仍然可以被选择
lldb.SBFrame : 代表某一栈帧,管理所属的局部变量和寄存器快照
在 https://lldb.llvm.org/python-reference.html 的最后有一个Python脚本使用lldb反汇编程序的例子
#!/usr/bin/python
import lldb
import os
def disassemble_instructions(insts):
for i in insts:
print i
# Set the path to the executable to debug
exe = "./a.out"
# Create a new debugger instance
debugger = lldb.SBDebugger.Create()
# When we step or continue, don't return from the function until the process
# stops. Otherwise we would have to handle the process events ourselves which, while doable is
#a little tricky. We do this by setting the async mode to false.
debugger.SetAsync (False)
# Create a target from a file and arch
print "Creating a target for '%s'" % exe
target = debugger.CreateTargetWithFileAndArch (exe, lldb.LLDB_ARCH_DEFAULT)
if target:
# If the target is valid set a breakpoint at main
main_bp = target.BreakpointCreateByName ("main", target.GetExecutable().GetFilename());
print main_bp
# Launch the process. Since we specified synchronous mode, we won't return
# from this function until we hit the breakpoint at main
process = target.LaunchSimple (None, None, os.getcwd())
# Make sure the launch went ok
if process:
# Print some simple process info
state = process.GetState ()
print process
if state == lldb.eStateStopped:
# Get the first thread
thread = process.GetThreadAtIndex (0)
if thread:
# Print some simple thread info
print thread
# Get the first frame
frame = thread.GetFrameAtIndex (0)
if frame:
# Print some simple frame info
print frame
function = frame.GetFunction()
# See if we have debug info (a function)
if function:
# We do have a function, print some info for the function
print function
# Now get all instructions for this function and print them
insts = function.GetInstructions(target)
disassemble_instructions (insts)
else:
# See if we have a symbol in the symbol table for where we stopped
symbol = frame.GetSymbol();
if symbol:
# We do have a symbol, print some info for the symbol
print symbol
可以看出这些主要类的相互调用关系
回调函数
在lldb的command中的可以实现回调,比如当某个断点命中时自动执行函数
(lldb) breakpoint set --func-regex <regular-expression>
(lldb) breakpoint command add -s python 1
Enter your Python command(s). Type 'DONE' to end.
def function (frame, bp_loc, internal_dict):
"""frame: the lldb.SBFrame for the location at which you stopped
bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information
internal_dict: an LLDB support object not to be used"""
name = frame.GetFunctionName()
print "function name: %s" % name
return False
DONE
函数的返回值决定是否将控制权还给用户,返回False则不还给用户继续执行,返回其他包括None则暂停
Process 2447 launched: '/Users/penguin/Test/a.out' (x86_64)
function name: func100
function name: c100
9Process 2447 exited with status = 0 (0x00000000)
CUSTOM STEPPING LOGIC
这是一个比较深奥复杂的功能。lldb的step是由一个“thread plans”的栈驱动的。每当有step的动作出现,就有一个新的plan被压入栈中,完成后被弹出。
当plan入栈后,lldb会通过“询问”该plan来决定一些功能,比如是否中断,“询问”的方式是调用约定的接口,常用的接口如下
should_step:
栈底的plan是stepping控制器,意味着当进程暂停时该plan被询问是否自由的执行(running freely),或者在当前线程单步(instruction-single-step)
explains_stop :
每个plan都会被询问,第一个声明这个stop的获得处理权利。例如在该地址有一个断点,如果脚本explains这个stop,并且should_stop返回false,这个断点将被跳过,因为处理权归脚本,而脚本不中断。这也是为什么step-over返回控制权的原因,因为它的explains一直返回false
should_stop:
a) 确定是否完成工作。如果完成那么通过调用SetPlanComplete来指示
b) 通过返回True或者False来确定是否将控制权交给用户
c) 如果没有完成,将建立任何需要的机器在下次thread继续的时候,False情况下设置SetPlanComplete为False会exit当前thread
is_stale:
轮询完成不管是否停下或者什么都没做,所有的plans将被访问is_stale,如果是他们将从栈中被弹出
以文档的中 http://llvm.org/svn/llvm-project/lldb/trunk/examples/python/scripted_step.py 第一个SimpleStep为例,这里加入输出,来观察执行状态
class SimpleStep:
def __init__(self, thread_plan, dict):
self.thread_plan = thread_plan
self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPC()
print "__init__ start_address:"
print hex(self.start_address)
def explains_stop(self, event):
# We are stepping, so if we stop for any other reason, it isn't
# because of us.
print 'explains_stop'
if self.thread_plan.GetThread().GetStopReason() == lldb.eStopReasonTrace:
print 'true'
return True
else:
return False
def should_stop(self, event):
print 'should_stop'
cur_pc = self.thread_plan.GetThread().GetFrameAtIndex(0).GetPC()
print hex(cur_pc)
if cur_pc < self.start_address or cur_pc >= self.start_address + 20:
print 'true'
self.thread_plan.SetPlanComplete(True)
return True
else:
print 'false'
return False
def should_step(self):
print 'should_step'
return True
执行命令
(lldb) command script import ~/script_step.py
(lldb) thread step-scripted -C script_step.SimpleStep
产生输出
__init__ start_address:
0x100000f30
should_step
should_stop
0x100000f31
false
should_step
explains_stop
true
should_stop
0x100000f34
false
should_step
explains_stop
true
should_stop
0x100000f38
false
should_step
explains_stop
true
should_stop
0x100000f3d
false
should_step
explains_stop
true
should_stop
0x100000f44
true
__should_step__返回True,所以之后都是单步执行(可以修改为False试试,程序跑飞了)
然后__explains_stop__宣布占据这个单步执行导致的中断
__should_stop__根据条件决定是否将程序的控制权交给user,直观的感受就是程序停下来了
增加命令
Python函数可以增加新的LLDB命令到命令解析器中,函数形式如下
def command_function(debugger, command, result, internal_dict):
"""This command takes a lot of options and does many fancy things"""
# Your code goes here
第一个参数是debugger实例,第二个参数是命令参数,
第三个参数为__lldb.SBCommandReturnObjec__类型,包含命令执行结果信息
最后是嵌入的脚本的集合
也可以使用Python类实现命令添加
class CommandObjectType:
def __init__(self, debugger, session_dict):
this call should initialize the command with respect to the command interpreter for the passed-in debugger
def __call__(self, debugger, command, exe_ctx, result):
this is the actual bulk of the command, akin to Python command functions
def get_short_help(self):
this call should return the short help text for this command[1]
def get_long_help(self):
this call should return the long help text for this command[1]
对于一个Python脚本,可以通过定义
def __lldb_init_module(debugger, internal_dict):
# Command Initialization code goes here
该函数在import时执行初始化
def ls(debugger, command, result, internal_dict):
ret = commands.getoutput('/bin/ls %s' % command)
print >>result, (ret)
# And the initialization code to add your commands
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f script_step.ls ls')
print 'The "ls" python command has been installed and is ready for use.'
如上述脚本,执行命令
(lldb) command script import ~/script_step.py
就可以直接在LLDB中使用命令ls
(lldb) ls /
Applications
Library
Network
System
...
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!