在LLDB的Python脚本环境里,char类型的变量(尤其是char指针和char数组)如果不加处理,打印出来往往是地址或者转义不直观的内容。以程序的argv为例,它是一个char**类型,直接输出SBValue对象并不能看到实际的命令行参数文本。我们需要通过LLDB提供的Python API把内存中的字符逐个读出来。

为什么直接打印char变量不够用
当你在脚本中拿到一个指向char的SBValue,例如通过frame.FindVariable("argv"),调用其str()往往只能看到类似(char **) argv = 0x00007ffeefbff6c0这样的信息。这是因为LLDB默认把指针当成对象描述,而不是字符串内容。
读取char指针指向的内容
对于char*或char**类型的变量,核心思路是先取指针目标,再按索引读取每一个unsigned char,最后拼成Python字符串。下面是一段读取argv并打印的示例脚本:
import lldb
def print_argv(debugger, command, result, internal_dict):
# 获取当前帧
target = debugger.GetSelectedTarget()
process = target.GetProcess()
frame = process.GetSelectedThread().GetSelectedFrame()
# 找到argv变量,它是char**类型
argv_val = frame.FindVariable("argv")
if not argv_val.IsValid():
print("无法找到argv变量")
return
# argv[0]是程序名,argv[1..]是参数,以NULL结尾
idx = 0
while True:
arg_ptr = argv_val.GetChildAtIndex(idx)
if not arg_ptr.IsValid():
break
# 取char*指向的字符串
cstr_val = arg_ptr.GetChildAtIndex(0, lldb.eNoDynamicValues, True)
if not cstr_val.IsValid():
break
# 逐字符读取直到遇到
chars = []
i = 0
while True:
ch = cstr_val.GetChildAtIndex(i, lldb.eNoDynamicValues, True)
if not ch.IsValid():
break
# 以unsigned char读取避免符号扩展
val = ch.GetValueAsUnsigned()
if val == 0:
break
chars.append(chr(val))
i += 1
print("argv[" + str(idx) + "] = " + "".join(chars))
idx += 1
# 注册为LLDB命令
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand("command script add -f " + __name__ + ".print_argv print_argv")
封装为通用char字符串读取函数
为了避免重复代码,可以把读取char*的逻辑抽成函数,这样任何char变量都能复用:
import lldb
def read_cstring(char_ptr_val):
# char_ptr_val应为char*类型的SBValue
if not char_ptr_val.IsValid():
return ""
chars = []
i = 0
while True:
ch = char_ptr_val.GetChildAtIndex(i, lldb.eNoDynamicValues, True)
if not ch.IsValid():
break
val = ch.GetValueAsUnsigned()
if val == 0:
break
chars.append(chr(val))
i += 1
return "".join(chars)
def dump_argv(debugger, command, result, internal_dict):
frame = debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
argv_val = frame.FindVariable("argv")
if not argv_val.IsValid():
print("argv not found")
return
idx = 0
while True:
arg_ptr = argv_val.GetChildAtIndex(idx)
if not arg_ptr.IsValid():
break
cstr = read_cstring(arg_ptr.GetChildAtIndex(0, lldb.eNoDynamicValues, True))
print("argv[" + str(idx) + "] = " + cstr)
idx += 1
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand("command script add -f " + __name__ + ".dump_argv dump_argv")
注意事项
- 使用GetValueAsUnsigned而不是GetValueAsString,可以避免LLDB对特殊字符的转义干扰。
- 遍历时必须检测NULL结尾,否则会越界读取无效内存。
- 如果char变量是数组而非指针,同样可以用GetChildAtIndex按索引访问,只是不需要先解引用指针。
小结
在LLDB Python脚本中高效打印char变量,关键在于把SBValue当作内存对象来遍历,而不是依赖默认描述。通过unsigned char转换和NULL结尾判断,你可以稳定地取出argv等字符串内容,并封装成可复用的调试命令。