5

I have the following C struct:

typedef struct {
    uint8_t a;
    uint8_t b;
    uint32_t c;
    uint8_t* d;
}

With ctypes, via a callback, I am able to obtain a pointer to such a struct in Python, let's call it ref. I can easily obtain a, b, c this way:

from ctypes import cast, c_uint8, c_uint32, POINTER

a = cast(ref, POINTER(c_uint8)).contents.value
b = cast(ref + 1, POINTER(c_uint8)).contents.value
c = cast(ref + 2, POINTER(c_uint32)).contents.value

but I can't read the bytes from d. I tried the following:

d_pointer = cast(ref + 6, POINTER(POINTER(c_uint8))).contents
first_byte_of_d = d_pointer.contents
print type(first_byte_of_d) # prints <class 'ctypes.c_ubyte'>
print first_byte_of_d

At this last line I encounter a SIGSEGV when debugging with gdb. So the question is, how should one access the first byte of a pointer from a struct in Python?

2
  • Are you sure your bug isn't in the callback (i.e., is it properly initializing the pointer)? Commented Jul 23, 2014 at 14:08
  • Yes, I'm positive about that, as I managed to retrieve data using Java. Commented Jul 24, 2014 at 6:26

1 Answer 1

9

You are assuming that c directly follows b which is not the case. The compiler will pad a few bytes, 2 on x86, in that structure to align c.

The proper way is to declare one-to-one mapping of your structure in ctypes:

from ctypes import *

class object_t(Structure):
    _fields_ = [
        ('a', c_uint8),
        ('b', c_uint8),
        ('c', c_uint32),
        ('d', POINTER(c_uint8)),
    ]

No you can obtain the value of any member thought this type.

C example library:

#include <stdint.h>
#include <stdlib.h>

struct object_t {
  uint8_t a;
  uint8_t b;
  uint32_t c;
  uint8_t* d;
};

static struct object_t object = {'a', 'b', 12345, NULL};

struct object_t * func1(void)
{
  return &object;
}

void func2(void(*callback)(struct object_t *))
{
  callback(&object);
}

Using it from Python:

from ctypes import *

class object_t(Structure):
    _fields_ = [
        ('a', c_uint8),
        ('b', c_uint8),
        ('c', c_uint32),
        ('d', POINTER(c_uint8)),
    ]

callback_t = CFUNCTYPE(None, POINTER(object_t))

lib = CDLL('./file.dll')

func1 = lib.func1
func1.argtypes = None
func1.restype  = POINTER(object_t)

func2 = lib.func2
func2.argtypes = [callback_t]
func2.restype   = None

ret = func1()

a = ret.contents.a
b = ret.contents.b
c = ret.contents.c
d = ret.contents.d

def mycallback(obj):
    a = obj.contents.a
    b = obj.contents.b
    c = obj.contents.c
    d = obj.contents.d

func2(callback_t(mycallback))
Sign up to request clarification or add additional context in comments.

1 Comment

This saved me: "The proper way is to declare one-to-one mapping of your structure in ctypes". I knew about this, but for some reason I thought I couldn't cast ref to object_t (the docs say "The cast() function can be used to cast a ctypes instance into a pointer to a different ctypes data type"). Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.