LLDB Python 脚本中的指针运算

ben*_*wad 3 c++ python xcode pointers lldb

我一直在尝试为 Xcode 中的自定义字符串类型创建自定义数据格式化程序。以下代码为我获取字符串中第一个字符的地址:

def MyStringSummary(valobj, internal_dict):
    data_pointer = valobj.GetChildMemberWithName('AllocatorInstance').GetChildMemberWithName('Data')
    print data_pointer.GetValue()
Run Code Online (Sandbox Code Playgroud)

这会打印出指针地址。当我查看该地址的内容时,我可以看到用于存储该数据的宽字符,所以我想我要做的就是将此指针转换为指向wchar_t,然后我就得到了第一个字符。我的第一个方法是这样的:

if data_pointer.TypeIsPointerType():
    mychar = data_pointer.Dereference()
    print mychar.GetValue()
else:
    print "data_pointer is not a pointer!"
Run Code Online (Sandbox Code Playgroud)

这证实了 data_pointer一个指针,但Dereference()调用似乎没有解决任何问题:mychar.GetValue()只是返回None。另一个问题 - 然后我是否能够通过循环并data_pointer每次增加固定数量的地址并继续取消引用并找到下一个字符,然后将其添加到输出字符串中?如果是这样,我将如何做到这一点?

编辑:

为了帮助澄清这个问题,我将发布一些关于字符串底层数据结构的信息。该定义太长,无法在此处发布(它也继承了它从通用数组基类所做的大部分功能),但我将提供更多详细信息。

查看StringVar.AllocationInstance.Data指针位置时,我可以看到我们为每个字符使用了 16 位。我正在查看的字符串中的所有字符都只有 8 位,每个字符后面还有 8 位 0。所以,这就是我在调试器中执行此操作时发生的情况:

(lldb) p (char*)(StringVar.AllocatorInstance.Data)
(char *) $4 = 0x10653360 "P"
(lldb) p (char*)(StringVar.AllocatorInstance.Data)+1
(char *) $6 = 0x10653361 ""
(lldb) p (char*)(StringVar.AllocatorInstance.Data)+2
(char *) $7 = 0x10653362 "a"
Run Code Online (Sandbox Code Playgroud)

所以我假设它一次只显示一个字符的原因是因为它认为每个 8 位字符都是由以下 8 位空终止的。但是,当我投射到unsigned short我得到这个:

(lldb) p (unsigned short*)(StringVar.AllocatorInstance.Data)
(unsigned short *) $9 = 0x10653360
(lldb) p *(unsigned short*)(StringVar.AllocatorInstance.Data)
(wchar_t) $10 = 80
(lldb) p (char*)(unsigned short*)(StringVar.AllocatorInstance.Data)
(char *) $11 = 0x10653360 "P"
(lldb) p (char*)((unsigned short*)(StringVar.AllocatorInstance.Data)+1)
(char *) $14 = 0x10653362 "a"
(lldb) p (char*)((unsigned short*)(StringVar.AllocatorInstance.Data)+2)
(char *) $18 = 0x10653364 "r"
Run Code Online (Sandbox Code Playgroud)

...所以看起来强制转换没问题unsigned short,只要我们将每个整数强制转换为字符。知道我如何尝试将其放入 Python 数据格式化程序中吗?

Jas*_*nda 5

Data看起来可能是UTF-16。我做了一个快速的 C 程序,它看起来有点像你的问题描述,并在交互式 Python 解释器中玩了一点。我认为这可能足以为您指明编写自己的格式化程序的正确方向?

int main ()
{
    struct String *mystr = AllocateString();
    mystr->AllocatorInstance.len = 10;
    mystr->AllocatorInstance.Data = (void *) malloc (10);
    memset (mystr->AllocatorInstance.Data, 0, 10);
    ((char *)mystr->AllocatorInstance.Data)[0] = 'h';
    ((char *)mystr->AllocatorInstance.Data)[2] = 'e';
    ((char *)mystr->AllocatorInstance.Data)[4] = 'l';
    ((char *)mystr->AllocatorInstance.Data)[6] = 'l';
    ((char *)mystr->AllocatorInstance.Data)[8] = 'o';

    FreeString (mystr);
}
Run Code Online (Sandbox Code Playgroud)

使用lldb.frame,lldb.process快捷方式(仅在执行交互时有效script),我们可以Data轻松地将 读入 python 字符串缓冲区:

>>> valobj = lldb.frame.FindVariable("mystr")
>>> address = valobj.GetChildMemberWithName('AllocatorInstance').GetChildMemberWithName('Data').GetValueAsUnsigned()
>>> size = valobj.GetChildMemberWithName('AllocatorInstance').GetChildMemberWithName('len').GetValueAsUnsigned()
>>> print address
4296016096
>>> print size
10
>>> err = lldb.SBError()
>>> print err
error: <NULL>
>>> membuf = lldb.process.ReadMemory (address, size, err)
>>> print err
success
>>> membuf
'h\x00e\x00l\x00l\x00o\x00'
Run Code Online (Sandbox Code Playgroud)

从这一点上,你可以做任何通常的 python 数组类型的事情 -

>>> for b in membuf:
...   print ord(b)
... 
104
0
101
0
108
0
108
0
111
0
Run Code Online (Sandbox Code Playgroud)

我不确定如何告诉 Python 这是 UTF-16 并且应该正确内化为宽字符,这更像是 Python 问题而不是 lldb 问题——但我认为最好的SBValue办法是不使用这些方法(因为您的Data指针具有无信息类型void *,就像我在测试程序中所做的那样),但要使用SBProcess内存读取方法。