I have the following c function.
JavaScript
x
3
1
/* returns (uint8_t *)outbuf */
2
uint8_t *func(uint8_t *inbuf, uint32_t inbuf_len, uint32_t *outbuf_len);
3
This function returns outbuf
, the output length is unknown before calling the function so the function receives a pointer to the length as an argument outbuf_len
, also the caller is responsible to free outbuf
.
I want to get the result of this function from python, so I started writing the following code:
JavaScript
1
15
15
1
import ctypes as ct
2
3
libb = ct.cdll.LoadLibrary('./a.so')
4
libb.func.restype = ct.c_char_p
5
6
inbuf = bytearray(inbuf_len)
7
inbuf = python_data
8
arr = ct.c_ubyte * inbuf_len
9
10
outbuf_len = ct.c_uint # there is no ct.c_uint_p...
11
12
outbuf = libb.func(arr.from_buffer_copy(inbuf), inbuf_len, outbuf_len)
13
14
print hexlify(outbuf) #prints only the first 4 bytes of outbuf
15
The problems i have is:
- I didn’t find pointer to uint in
ctypes
types, so how can I pass theoutbuf_len
pointer to the C function? - when printing the
outbuf
, only the first 4 bytes that are pointed by the pointer are printed. - How do I free() the
outbuf
buffer from python?
I have the source of the C function so it is possible to change how arguments are passed the the C function.
Thanks.
Advertisement
Answer
If you’ll be passing Python byte strings as the input buffer, here’s a way to do it. I made a minimal example of the C call:
test.c
JavaScript
1
20
20
1
#include <stdint.h>
2
#include <stdlib.h>
3
4
#define API __declspec(dllexport)
5
6
// double every character in the input buffer as an example
7
API uint8_t *func(uint8_t *inbuf, uint32_t inbuf_len, uint32_t *outbuf_len) {
8
*outbuf_len = inbuf_len * 2;
9
uint8_t* outbuf = malloc(*outbuf_len);
10
for(uint32_t i = 0; i < inbuf_len; ++i) {
11
outbuf[i*2] = inbuf[i];
12
outbuf[i*2+1] = inbuf[i];
13
}
14
return outbuf;
15
}
16
17
API void freebuf(uint8_t* buf) {
18
free(buf);
19
}
20
test.py
JavaScript
1
26
26
1
import ctypes as ct
2
3
dll = ct.CDLL('./test')
4
5
# c_char_p accepts Python byte strings and is compatible with C uint8_t*
6
# but don't use it for the output buffer because ctypes converts the pointer
7
# back to a Python byte string and you would lose access to the pointer for
8
# later freeing it. Use POINTER(ct.c_char) to get the actual pointer back.
9
dll.func.argtypes = ct.c_char_p, ct.c_uint32, ct.POINTER(ct.c_uint32)
10
dll.func.restype = ct.POINTER(ct.c_char)
11
dll.freebuf.argtypes = ct.POINTER(ct.c_char),
12
dll.freebuf.restype = None
13
14
def func(inbuf):
15
outlen = ct.c_uint32() # create storage for the output length and pass by reference
16
outbuf = dll.func(inbuf, len(inbuf), ct.byref(outlen))
17
# Slicing the returned POINTER(c_char) returns a Python byte string.
18
# If you used POINTER(c_uint8) for the return value instead,
19
# you'd get a list of integer byte values.
20
data = outbuf[:outlen.value]
21
# Can free the pointer now if you want, or return it for freeing later
22
dll.freebuf(outbuf)
23
return data
24
25
print(func(b'ABC'))
26
Output:
JavaScript
1
2
1
b'AABBCC'
2