Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,19 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None,
use_errno=False,
use_last_error=False,
winmode=None):
if name:
name = _os.fspath(name)

# If the filename that has been provided is an iOS/tvOS/watchOS
# .fwork file, dereference the location to the true origin of the
# binary.
if name.endswith(".fwork"):
with open(name) as f:
name = _os.path.join(
_os.path.dirname(_sys.executable),
f.read().strip()
)

self._name = name
flags = self._func_flags_
if use_errno:
Expand Down Expand Up @@ -467,6 +480,8 @@ def LoadLibrary(self, name):

if _os.name == "nt":
pythonapi = PyDLL("python dll", None, _sys.dllhandle)
elif _sys.platform == "android":
pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2])
elif _sys.platform == "cygwin":
pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2])
else:
Expand Down Expand Up @@ -498,15 +513,14 @@ def WinError(code=None, descr=None):
c_ssize_t = c_longlong

# functions

from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr

## void *memmove(void *, const void *, size_t);
# XXX: RUSTPYTHON
# memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)

## void *memset(void *, int, size_t)
# XXX: RUSTPYTHON
# memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)

def PYFUNCTYPE(restype, *argtypes):
class CFunctionType(_CFuncPtr):
Expand All @@ -515,30 +529,27 @@ class CFunctionType(_CFuncPtr):
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
return CFunctionType

# XXX: RUSTPYTHON
# _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
def cast(obj, typ):
return _cast(obj, obj, typ)

# XXX: RUSTPYTHON
# _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
def string_at(ptr, size=-1):
"""string_at(addr[, size]) -> string
"""string_at(ptr[, size]) -> string

Return the string at addr."""
Return the byte string at void *ptr."""
return _string_at(ptr, size)

try:
from _ctypes import _wstring_at_addr
except ImportError:
pass
else:
# XXX: RUSTPYTHON
# _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
_wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
def wstring_at(ptr, size=-1):
"""wstring_at(addr[, size]) -> string
"""wstring_at(ptr[, size]) -> string

Return the string at addr."""
Return the wide-character string at void *ptr."""
return _wstring_at(ptr, size)


Expand Down
79 changes: 70 additions & 9 deletions crates/vm/src/stdlib/ctypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ pub(crate) mod _ctypes {
}
}

#[cfg(windows)]
#[pyfunction(name = "LoadLibrary")]
fn load_library_windows(
name: String,
Expand All @@ -203,20 +204,33 @@ pub(crate) mod _ctypes {
Ok(id)
}

#[cfg(not(windows))]
#[pyfunction(name = "dlopen")]
fn load_library_unix(
name: String,
name: Option<String>,
_load_flags: OptionalArg<i32>,
vm: &VirtualMachine,
) -> PyResult<usize> {
// TODO: audit functions first
// TODO: load_flags
let cache = library::libcache();
let mut cache_write = cache.write();
let (id, _) = cache_write
.get_or_insert_lib(&name, vm)
.map_err(|e| vm.new_os_error(e.to_string()))?;
Ok(id)
match name {
Some(name) => {
let cache = library::libcache();
let mut cache_write = cache.write();
let (id, _) = cache_write
.get_or_insert_lib(&name, vm)
.map_err(|e| vm.new_os_error(e.to_string()))?;
Ok(id)
}
None => {
// If None, call libc::dlopen(null, mode) to get the current process handle
let handle = unsafe { libc::dlopen(std::ptr::null(), libc::RTLD_NOW) };
if handle.is_null() {
return Err(vm.new_os_error("dlopen() error"));
}
Ok(handle as usize)
}
}
}

#[pyfunction(name = "FreeLibrary")]
Expand All @@ -228,10 +242,57 @@ pub(crate) mod _ctypes {
}

#[pyfunction(name = "POINTER")]
pub fn pointer(_cls: PyTypeRef) {}
pub fn create_pointer_type(cls: PyObjectRef, vm: &VirtualMachine) -> PyResult {
// Get the _pointer_type_cache
let ctypes_module = vm.import("_ctypes", 0)?;
let cache = ctypes_module.get_attr("_pointer_type_cache", vm)?;

// Check if already in cache using __getitem__
if let Ok(cached) = vm.call_method(&cache, "__getitem__", (cls.clone(),))
&& !vm.is_none(&cached)
{
return Ok(cached);
}

// Get the _Pointer base class
let pointer_base = ctypes_module.get_attr("_Pointer", vm)?;

// Create the name for the pointer type
let name = if let Ok(type_obj) = cls.get_attr("__name__", vm) {
format!("LP_{}", type_obj.str(vm)?)
} else if let Ok(s) = cls.str(vm) {
format!("LP_{}", s)
} else {
"LP_unknown".to_string()
};

// Create a new type that inherits from _Pointer
let type_type = &vm.ctx.types.type_type;
let bases = vm.ctx.new_tuple(vec![pointer_base]);
let dict = vm.ctx.new_dict();
dict.set_item("_type_", cls.clone(), vm)?;

let new_type = type_type
.as_object()
.call((vm.ctx.new_str(name), bases, dict), vm)?;

// Store in cache using __setitem__
vm.call_method(&cache, "__setitem__", (cls, new_type.clone()))?;

Ok(new_type)
}

#[pyfunction(name = "pointer")]
pub fn pointer_fn(_inst: PyObjectRef) {}
pub fn create_pointer_inst(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
// Get the type of the object
let obj_type = obj.class().to_owned();

// Create pointer type for this object's type
let ptr_type = create_pointer_type(obj_type.into(), vm)?;

// Create an instance of the pointer type with the object
ptr_type.call((obj,), vm)
}

#[pyfunction]
fn _pointer_type_cache() -> PyObjectRef {
Expand Down
7 changes: 4 additions & 3 deletions crates/vm/src/stdlib/ctypes/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,14 @@ impl std::fmt::Debug for PyCArray {
impl Constructor for PyCArray {
type Args = (PyTypeRef, usize);

fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
Ok(Self {
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
Self {
typ: PyRwLock::new(args.0),
length: AtomicCell::new(args.1),
value: PyRwLock::new(vm.ctx.none()),
}
.into_pyobject(vm))
.into_ref_with_type(vm, cls)
.map(Into::into)
}
}

Expand Down
15 changes: 15 additions & 0 deletions crates/vm/src/stdlib/ctypes/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,21 @@ impl PyCSimpleType {
.clone(),
))
}

#[pyclassmethod]
fn from_param(cls: PyTypeRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
// If the value is already an instance of the requested type, return it
if value.fast_isinstance(&cls) {
return Ok(value);
}

// Check for _as_parameter_ attribute
let Ok(as_parameter) = value.get_attr("_as_parameter_", vm) else {
return Err(vm.new_type_error("wrong type"));
};

PyCSimpleType::from_param(cls, as_parameter, vm)
}
}

#[pyclass(
Expand Down
148 changes: 97 additions & 51 deletions crates/vm/src/stdlib/ctypes/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,60 +120,106 @@ impl Debug for PyCFuncPtr {
}

impl Constructor for PyCFuncPtr {
type Args = (PyTupleRef, FuncArgs);
type Args = FuncArgs;

fn py_new(_cls: PyTypeRef, (tuple, _args): Self::Args, vm: &VirtualMachine) -> PyResult {
let name = tuple
.first()
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))?
.downcast_ref::<PyStr>()
.ok_or(vm.new_type_error("Expected a string"))?
.to_string();
let handler = tuple
.into_iter()
.nth(1)
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))?
.clone();
let handle = handler.try_int(vm);
let handle = match handle {
Ok(handle) => handle.as_bigint().clone(),
Err(_) => handler
.get_attr("_handle", vm)?
.try_int(vm)?
.as_bigint()
.clone(),
};
let library_cache = crate::stdlib::ctypes::library::libcache().read();
let library = library_cache
.get_lib(
handle
.to_usize()
.ok_or(vm.new_value_error("Invalid handle".to_string()))?,
)
.ok_or_else(|| vm.new_value_error("Library not found".to_string()))?;
let inner_lib = library.lib.lock();

let terminated = format!("{}\0", &name);
let code_ptr = if let Some(lib) = &*inner_lib {
let pointer: Symbol<'_, FP> = unsafe {
lib.get(terminated.as_bytes())
.map_err(|err| err.to_string())
.map_err(|err| vm.new_attribute_error(err))?
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
// Handle different argument forms like CPython:
// 1. Empty args: create uninitialized
// 2. One integer argument: function address
// 3. Tuple argument: (name, dll) form

if args.args.is_empty() {
return Self {
ptr: PyRwLock::new(None),
needs_free: AtomicCell::new(false),
arg_types: PyRwLock::new(None),
_flags_: AtomicCell::new(0),
res_type: PyRwLock::new(None),
name: PyRwLock::new(None),
handler: vm.ctx.none(),
}
.into_ref_with_type(vm, cls)
.map(Into::into);
}

let first_arg = &args.args[0];

// Check if first argument is an integer (function address)
if let Ok(addr) = first_arg.try_int(vm) {
let ptr_val = addr.as_bigint().to_usize().unwrap_or(0);
return Self {
ptr: PyRwLock::new(Some(CodePtr(ptr_val as *mut _))),
needs_free: AtomicCell::new(false),
arg_types: PyRwLock::new(None),
_flags_: AtomicCell::new(0),
res_type: PyRwLock::new(None),
name: PyRwLock::new(Some(format!("CFuncPtr@{:#x}", ptr_val))),
handler: vm.ctx.new_int(ptr_val).into(),
}
.into_ref_with_type(vm, cls)
.map(Into::into);
}

// Check if first argument is a tuple (name, dll) form
if let Some(tuple) = first_arg.downcast_ref::<PyTuple>() {
let name = tuple
.first()
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))?
.downcast_ref::<PyStr>()
.ok_or(vm.new_type_error("Expected a string"))?
.to_string();
let handler = tuple
.iter()
.nth(1)
.ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))?
.clone();

// Get library handle and load function
let handle = handler.try_int(vm);
let handle = match handle {
Ok(handle) => handle.as_bigint().clone(),
Err(_) => handler
.get_attr("_handle", vm)?
.try_int(vm)?
.as_bigint()
.clone(),
};
Some(CodePtr(*pointer as *mut _))
} else {
None
};
Ok(Self {
ptr: PyRwLock::new(code_ptr),
needs_free: AtomicCell::new(false),
arg_types: PyRwLock::new(None),
_flags_: AtomicCell::new(0),
res_type: PyRwLock::new(None),
name: PyRwLock::new(Some(name)),
handler,
let library_cache = crate::stdlib::ctypes::library::libcache().read();
let library = library_cache
.get_lib(
handle
.to_usize()
.ok_or(vm.new_value_error("Invalid handle".to_string()))?,
)
.ok_or_else(|| vm.new_value_error("Library not found".to_string()))?;
let inner_lib = library.lib.lock();

let terminated = format!("{}\0", &name);
let code_ptr = if let Some(lib) = &*inner_lib {
let pointer: Symbol<'_, FP> = unsafe {
lib.get(terminated.as_bytes())
.map_err(|err| err.to_string())
.map_err(|err| vm.new_attribute_error(err))?
};
Some(CodePtr(*pointer as *mut _))
} else {
None
};

return Self {
ptr: PyRwLock::new(code_ptr),
needs_free: AtomicCell::new(false),
arg_types: PyRwLock::new(None),
_flags_: AtomicCell::new(0),
res_type: PyRwLock::new(None),
name: PyRwLock::new(Some(name)),
handler,
}
.into_ref_with_type(vm, cls)
.map(Into::into);
}
.to_pyobject(vm))

Err(vm.new_type_error("Expected an integer address or a tuple"))
}
}

Expand Down
Loading