我对这个问题的初步回答虽然内容丰富,但并不完整。我最初认为报告的问题只是打印一个未使用unsigned char *
默认格式化程序 ( char *
) 的类型的 c 字符串数组。这个答案是第一位的。然后是关于如何打印程序实际处理的这个(有些独特的)帕斯卡字符串数据数组的答案。
第一个答案: lldb知道如何处理char *
好;正是这unsigned char *
一点使它表现得比平时更糟。例如,如果txtRecord
是const char *
,
(lldb) p txtRecord
(const char *) $0 = 0x0000000100000f51 ".en0=192.168.1.36.port=50278"
char *
您可以复制 lldb 为for内置的类型摘要unsigned char *
。 type summary list
列出所有内置类型摘要;复制 lldb-179.5 的摘要char *
:
(lldb) type summary add -p -C false -s ${var%s} 'unsigned char *'
(lldb) type summary add -p -C false -s ${var%s} 'const unsigned char *'
(lldb) fr va txtRecord
(const unsigned char *) txtRecord = 0x0000000100000f51 ".en0=192.168.1.36.port=50278"
(lldb) p txtRecord
(const unsigned char *) $2 = 0x0000000100000f51 ".en0=192.168.1.36.port=50278"
(lldb)
当然,您可以将这些放在您的~/.lldbinit
文件中,从现在开始它们将被 Xcode 等人拾取。
第二个答案:要打印实际使用的帕斯卡字符串数组,您需要创建一个 python 函数。它将接受两个参数,pascal 字符串缓冲区的大小 ( txtLen
) 和缓冲区的起始地址 ( txtRecord
)。创建一个类似的python文件pstrarray.py
(我喜欢将它们放在我创建的目录中~/lldb
)并通过文件将其加载到您的lldb中,~/.lldbinit
以便您可以使用以下命令:
command script import ~/lldb/pstrarray.py
python脚本有点长;我敢肯定,更熟悉 python 的人可以更简洁地表达这一点。还有一堆错误处理增加了体积。但主要思想是取两个参数:缓冲区的大小和指向缓冲区的指针。用户将使用变量名来表达这些,如pstrarray txtLen txtRecord
,在这种情况下,您可以在当前帧中查找变量,但他们可能还想使用一个精确的表达式,如pstrarray sizeof(str) str
. 所以我们需要通过表达式评估引擎将这些参数传递给一个整数大小和一个指针地址。然后我们从进程中读取内存并打印字符串。
import lldb
import shlex
import optparse
def pstrarray(debugger, command, result, dict):
command_args = shlex.split(command)
parser = create_pstrarray_options()
try:
(options, args) = parser.parse_args(command_args)
except:
return
if debugger and debugger.GetSelectedTarget() and debugger.GetSelectedTarget().GetProcess():
process = debugger.GetSelectedTarget().GetProcess()
if len(args) < 2:
print "Usage: pstrarray size-of-buffer pointer-to-array-of-pascal-strings"
return
if process.GetSelectedThread() and process.GetSelectedThread().GetSelectedFrame():
frame = process.GetSelectedThread().GetSelectedFrame()
size_of_buffer_sbval = frame.EvaluateExpression (args[0])
if not size_of_buffer_sbval.IsValid() or size_of_buffer_sbval.GetValueAsUnsigned (lldb.LLDB_INVALID_ADDRESS) == lldb.LLDB_INVALID_ADDRESS:
print 'Could not evaluate "%s" down to an integral value' % args[0]
return
size_of_buffer = size_of_buffer_sbval.GetValueAsUnsigned ()
address_of_buffer_sbval = frame.EvaluateExpression (args[1])
if not address_of_buffer_sbval.IsValid():
print 'could not evaluate "%s" down to a pointer value' % args[1]
return
address_of_buffer = address_of_buffer_sbval.GetValueAsUnsigned (lldb.LLDB_INVALID_ADDRESS)
# If the expression eval didn't give us an integer value, try it again with an & prepended.
if address_of_buffer == lldb.LLDB_INVALID_ADDRESS:
address_of_buffer_sbval = frame.EvaluateExpression ('&%s' % args[1])
if address_of_buffer_sbval.IsValid():
address_of_buffer = address_of_buffer_sbval.GetValueAsUnsigned (lldb.LLDB_INVALID_ADDRESS)
if address_of_buffer == lldb.LLDB_INVALID_ADDRESS:
print 'could not evaluate "%s" down to a pointer value' % args[1]
return
err = lldb.SBError()
pascal_string_buffer = process.ReadMemory (address_of_buffer, size_of_buffer, err)
if (err.Fail()):
print 'Failed to read memory at address 0x%x' % address_of_buffer
return
pascal_string_array = bytearray(pascal_string_buffer, 'ascii')
index = 0
while index < size_of_buffer:
length = ord(pascal_string_buffer[index])
print "%s" % pascal_string_array[index+1:index+1+length]
index = index + length + 1
def create_pstrarray_options():
usage = "usage: %prog"
description='''print an buffer which has an array of pascal strings in it'''
parser = optparse.OptionParser(description=description, prog='pstrarray',usage=usage)
return parser
def __lldb_init_module (debugger, dict):
parser = create_pstrarray_options()
pstrarray.__doc__ = parser.format_help()
debugger.HandleCommand('command script add -f %s.pstrarray pstrarray' % __name__)
以及一个运行它的示例程序:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main ()
{
unsigned char str[] = {16,'e','n','0','=','1','9','2','.','1','6','8','.','1','.','3','6',
10,'p','o','r','t','=','5','1','6','8','7'};
uint8_t *p = str;
while (p < str + sizeof (str))
{
int len = *p++;
char buf[len + 1];
strlcpy (buf, (char*) p, len + 1);
puts (buf);
p += len;
}
puts ("done"); // break here
}
并在使用中:
(lldb) br s -p break
Breakpoint 1: where = a.out`main + 231 at a.c:17, address = 0x0000000100000ed7
(lldb) r
Process 74549 launched: '/private/tmp/a.out' (x86_64)
en0=192.168.1.36
port=51687
Process 74549 stopped
* thread #1: tid = 0x1c03, 0x0000000100000ed7 a.out`main + 231 at a.c:17, stop reason = breakpoint 1.1
#0: 0x0000000100000ed7 a.out`main + 231 at a.c:17
14 puts (buf);
15 p += len;
16 }
-> 17 puts ("done"); // break here
18 }
(lldb) pstrarray sizeof(str) str
en0=192.168.1.36
port=51687
(lldb)
虽然可以在 lldb 中执行此操作很酷,但它并不像我们希望看到的那么顺利。如果缓冲区的大小和缓冲区的地址包含在单个对象中struct PStringArray {uint16_t size; uint8_t *addr;}
,那会更好。您可以为所有类型的变量定义类型摘要格式化程序,struct PStringArray
并且不需要特殊命令。您仍然需要编写一个 python 函数,但它可以直接从对象中获取所需的所有信息,因此它会消失在 lldb 类型格式系统中。您可以编写(lldb) p strs
并调用自定义格式化程序函数strs
以打印其中的所有字符串。