I am using python with ctypes to call a C so file.
The C structure is:
typedef struct {
uint32_t var1;
uint32_t var2;
uint32_t var3;
uint8_t var4;
uint8_t var5
} struct1;
If I call C code where I set the variables of var1 to 0x050A, var2 to 0x102 and var3 to 0x203 build it and print out the values everything works perfectly.
in C
static struct1 mstruct;
mstruct.var1=1290
mstruct.var2=258
mstruct.var3=515
Then I call a c so file that prints out the values of the structure passed to it.
printf("var1 %lx\n",mstruct.var1) # var1 050A
printf("var2 %lx\n",mstruct.var2) # var2 102
printf("var3 %lx\n",mstruct.var3) # var3 203
Note that if I just use x instead of lx it wont build and gives an error.
But when called from C everything prints out correctly
var1 050A
var2 102
var3 203
Now If i initialize with python and call the same structure I define a class as:
class struct1(ctypes.Structure):
_fields_=[
('var1',ctypes.c_uint32),
('var2',ctypes.c_uint32),
('var3',ctypes.c_uint32),
('var4',ctypes.c_uint8),
('var5',ctypes.c_uint8)]
def __init__(self):
self.var1=1290
self.var2=258
self.var3=515
if __name__=='__main__':
mstruct=struct1()
print('var1 {} type {}'.format(mstruct.var1,type(mstruct.var1)))
clib=ctypes.CDLL('csofile.so')
c_rtn=clib.print_c_struct(ctypes.byref(mstruct))
the Var1 value is correct and the type is printed as class 'int' (not ctypes uint32).
and in the C so file it prints out
var1 1020000050a
var2 50a00000203
var3 20300000102
The lowest 16 bits of Var1 are the correct value. The middle 16 bits are padded with zeros which are correct. But there is an upper 32 bits from the what var 2 should have been in var1. And Var2 started with the value from Var3.
There seems to be a mismatch like my python isn't really defining it as 32 bit but what makes it stranger is that the C is actually reading it as 64 bits and I don't understand how that could be the case. Since the C structure defines it as 32 bit I would expect it to only take 32 bits so somehow it is seeing the 64 bits by python.
If I change everything to int it works perfectly in my demo application but the real C so file was created by someone else and I will get a new one every couple days so I am trying to avoid changing it each time.
I also tried casting when I set the value in the init thinking maybe I was overriding the type but that didn't seem to change anything.
self.var1=(ctypes_c_uint32)(1290)
Any help would be greatly appreciated.
header:
#include <stdint.h>
typedef struct{
uint32_t first;
uint32_t second;
int *marray;
} my_struct;
int call_c_function(int var1,int *var2, my_struct *mstruct );
C file
#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "c_header.h"
int call_c_function(int var1,int *var2, my_struct *mstruct ){
printf("var1 %d \n",var1);
printf("var2 %d \n",*var2);
printf("mystruct first %ld \n",mstruct->first);
printf("mystruct second %ld \n",mstruct->second);
int *array_ptr;
array_ptr = mstruct->marray;
printf("C ADDR struct %p \n",mstruct);
printf("C ADDR of pointer array_ptr %p \n",&array_ptr);
printf("C ADDR stored at array_ptr %p I need Python to match this!\n",array_ptr);
printf("Value in Memory it points to %d \n",*array_ptr);
printf("size of c int %ld\n",sizeof(int));
for (int i=0;i<8;i++){
printf(" %d ",array_ptr[i]);
array_ptr[i]=8-i;
}
printf("\n ");
return 0;
}
Compiled with g++ -fPIC -shared -o clib.so c_file.cpp And Python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Aug 18 13:16:52 2020
@author: kevin.johnson
"""
import ctypes as ctypes
import struct as struct
so_file='./clib.so'
class my_struct(ctypes.Structure):
_fields_=[
('first',ctypes.c_uint32),
('second',ctypes.c_uint32),
('marray',ctypes.POINTER(ctypes.c_int))]
def __init__(self,ma):
self.first=1290
self.second=258
self.marray = ma
def so_attr(sofile):
from subprocess import Popen,PIPE
out = Popen(args='nm ./{}'.format(sofile),
shell=True,
stdout=PIPE).communicate()[0].decode("utf-8")
attrs=[i.split(" ")[-1].replace("\r", "")
for i in out.split("\n") if " T " in i]
functions = [i for i in attrs if hasattr(ctypes.CDLL(sofile),i)]
print (functions)
def arr2list(inp,length):
outp=[inp[i] for i in range(length)]
return outp
if __name__ == '__main__':
if 0:
so_attr(so_file)
else:
mn = [i for i in range(8)]
ms = ctypes.c_int*8
ma = ms(*mn)
print(type(ma))
ma_p = ctypes.cast(ma,ctypes.POINTER(ctypes.c_int))
ma_byref = ctypes.byref(ma)
mstruct_inst=my_struct(ma_p)
print ('size of python int {} '.format(ctypes.sizeof(ma)/len(ma)))
print(mstruct_inst)
print('var1 {} type {}'.format(mstruct_inst.first,type(mstruct_inst.first)))
var1=ctypes.c_int(5)
var2=ctypes.c_int(6)
print(arr2list(ma,8))
clib=ctypes.CDLL(so_file)
print('ADDRESS: {}'.format(ctypes.byref(mstruct_inst)))
print('by ref ma: {} I Need C to match this'.format(ma_byref))
print('map: {}'.format(ma_p))
print('map2: {}'.format(ctypes.cast(ctypes.byref(ma),ctypes.POINTER(ctypes.c_int))))
funct_int=clib._Z15call_c_functioniPiP9my_struct(var1,ctypes.byref(var2),ctypes.byref(mstruct_inst))
print ('C Function return {} '.format(funct_int))
print(arr2list(ma,8))
print('done')
long
on your system is 64-bits as exhibited by printf("%lx")
printing a 64-bit integer. As I see you found in the comments, your C code uint32_t
was defined as unsigned long
which would be incorrect on that system.
Your system should have a stdint.h
header which defines uint32_t
correctly for your operating system.