如何从Python对象内部提取内存地址

独眼巨人

我正在使用返回Buffer对象的二进制Python库。该对象基本上是C对象的包装,其中包含指向实际内存缓冲区的指针。我需要的是从Python获取该指针中包含的内存地址,问题是Buffer对象没有获取它的Python方法,因此我需要做一些技巧来获取它。

目前,我发现了一种获取指针值的丑陋且不安全的方法:

我知道C对象的内部结构:

typedef struct _Buffer {
  PyObject_VAR_HEAD PyObject *parent;

  int type; /* GL_BYTE, GL_SHORT, GL_INT, GL_FLOAT */
  int ndimensions;
  int *dimensions;

  union {
    char *asbyte;
    short *asshort;
    int *asint;
    float *asfloat;
    double *asdouble;

    void *asvoid;
  } buf;
} Buffer;

因此,我编写了以下Python代码:

# + PyObject_VAR_HEAD size
# + 8 bytes PyObject_VAR_HEAD PyObject *parent
# + 4 bytes from int type
# + 4 bytes from int ndimensions
# + 8 bytes from int *dimensions
# = 24
offset = sys.getsizeof(0) + 24

buffer_pointer_addr = id(buffer) + offset
buffer_pointer_data = ctypes.string_at(buffer_pointer_addr, 8)
buffer_pointer_value = struct.unpack('Q', buffer_pointer_data)[0]

这一直在为我工作。如您所见,我使用来获取Python Buffer对象的内存地址id(buffer),但您可能知道这并不是指向缓冲区的实际指针,而仅仅是CPython中恰好是该Python对象的内存地址的Python数字。

因此,然后我添加了通过添加C结构中所有变量的大小而计算出的偏移量。我正在对字节大小(显然是完全不安全的)进行硬编码,除了PyObject_VAR_HEAD是我得到的sys.getsizeof(0)

通过添加偏移量,我获得了包含指向实际缓冲区的指针的内存地址,然后使用ctypes对其进行提取ctypes.string_at,并将指针的大小硬编码为8个字节(我在64位OS上),然后使用struct.unpack转换将其转换为实际的Python int

所以现在我的问题是:如何在不对所有大小进行硬编码的情况下实现更安全的解决方案?(如果存在)。也许有ctypes吗?如果它仅适用于CPython,就可以了。

独眼巨人

在研究了C Struct填充并基于以下假设之后,我找到了一个更安全的解决方案:

  • 该代码仅在CPython上使用。
  • 缓冲区指针位于C结构的末尾。
  • 缓冲区指针的大小可以安全地从void * C-type中提取出来,因为它将union{}是C结构中最大的指针无论如何,在大多数现代OS上,数据指针类型之间不会有不同的大小。
  • C Struct成员将恰好是问题中显示的成员

基于所有这些假设和此处找到的规则:https : //stackoverflow.com/a/38144117/8861787,我们可以放心地说,该结构的末尾将没有填充,并且我们可以提取指针而无需对任何内容进行硬编码:

# Get the size of the Buffer Python object
buffer_obj_size = sys.getsizeof(buffer)

# Get the size of void * C-type
buffer_pointer_size = ctypes.sizeof(ctypes.c_void_p)

# Calculate the address to the pointer assuming that it's at the end of the C Struct
buffer_pointer_addr = id(buffer) + buffer_obj_size - buffer_pointer_size

# Get the actual pointer value as a Python Int
buffer_pointer_value = (ctypes.c_void_p).from_address(buffer_pointer_addr).value

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章