diff options
author | Armin Rigo <arigo@tunes.org> | 2015-05-12 11:07:25 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2015-05-12 11:07:25 +0200 |
commit | fe4bb73d2191ea7b2ee5586848e1c5bbbcbaa72b (patch) | |
tree | 5c80d5c2ee50bc8e99039c7483522e2febc08cb7 /c/cdlopen.c | |
parent | 34dbd9932de50a5de29f0fdaab9e9a06526d93a7 (diff) | |
download | cffi-fe4bb73d2191ea7b2ee5586848e1c5bbbcbaa72b.tar.gz |
the big Moving Files Around step
Diffstat (limited to 'c/cdlopen.c')
-rw-r--r-- | c/cdlopen.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/c/cdlopen.c b/c/cdlopen.c new file mode 100644 index 0000000..7276273 --- /dev/null +++ b/c/cdlopen.c @@ -0,0 +1,347 @@ +/* ffi.dlopen() interface with dlopen()/dlsym()/dlclose() */ + +static void *cdlopen_fetch(PyObject *libname, void *libhandle, char *symbol) +{ + void *address; + + if (libhandle == NULL) { + PyErr_Format(FFIError, "library '%s' has been closed", + PyText_AS_UTF8(libname)); + return NULL; + } + + dlerror(); /* clear error condition */ + address = dlsym(libhandle, symbol); + if (address == NULL) { + const char *error = dlerror(); + PyErr_Format(FFIError, "symbol '%s' not found in library '%s': %s", + symbol, PyText_AS_UTF8(libname), error); + } + return address; +} + +static void cdlopen_close_ignore_errors(void *libhandle) +{ + if (libhandle != NULL) + dlclose(libhandle); +} + +static int cdlopen_close(PyObject *libname, void *libhandle) +{ + if (libhandle != NULL && dlclose(libhandle) != 0) { + const char *error = dlerror(); + PyErr_Format(FFIError, "closing library '%s': %s", + PyText_AS_UTF8(libname), error); + return -1; + } + return 0; +} + +static PyObject *ffi_dlopen(PyObject *self, PyObject *args) +{ + char *filename_or_null, *printable_filename; + void *handle; + int flags = 0; + + if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) { + PyObject *dummy; + if (!PyArg_ParseTuple(args, "|Oi:load_library", + &dummy, &flags)) + return NULL; + filename_or_null = NULL; + } + else if (!PyArg_ParseTuple(args, "et|i:load_library", + Py_FileSystemDefaultEncoding, &filename_or_null, + &flags)) + return NULL; + + if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0) + flags |= RTLD_NOW; + printable_filename = filename_or_null ? filename_or_null : "<None>"; + + handle = dlopen(filename_or_null, flags); + if (handle == NULL) { + const char *error = dlerror(); + PyErr_Format(PyExc_OSError, "cannot load library '%s': %s", + printable_filename, error); + return NULL; + } + return (PyObject *)lib_internal_new((FFIObject *)self, + printable_filename, handle); +} + +static PyObject *ffi_dlclose(PyObject *self, PyObject *args) +{ + LibObject *lib; + if (!PyArg_ParseTuple(args, "O!", &Lib_Type, &lib)) + return NULL; + + if (lib->l_libhandle == NULL) { + PyErr_Format(FFIError, "library '%s' is already closed " + "or was not created with ffi.dlopen()", + PyText_AS_UTF8(lib->l_libhandle)); + return NULL; + } + + if (cdlopen_close(lib->l_libname, lib->l_libhandle) < 0) + return NULL; + + /* Clear the dict to force further accesses to do cdlopen_fetch() + again, and fail because the library was closed. */ + PyDict_Clear(lib->l_dict); + + Py_INCREF(Py_None); + return Py_None; +} + + +static Py_ssize_t cdl_4bytes(char *src) +{ + /* read 4 bytes in little-endian order; return it as a signed integer */ + signed char *ssrc = (signed char *)src; + unsigned char *usrc = (unsigned char *)src; + return (ssrc[0] << 24) | (usrc[1] << 16) | (usrc[2] << 8) | usrc[3]; +} + +static _cffi_opcode_t cdl_opcode(char *src) +{ + return (_cffi_opcode_t)cdl_4bytes(src); +} + +typedef struct { + unsigned long long value; + int neg; +} cdl_intconst_t; + +int _cdl_realize_global_int(struct _cffi_getconst_s *gc) +{ + /* The 'address' field of 'struct _cffi_global_s' is set to point + to this function in case ffiobj_init() sees constant integers. + This fishes around after the 'ctx->globals' array, which is + initialized to contain another array, this time of + 'cdl_intconst_t' structures. We get the nth one and it tells + us what to return. + */ + cdl_intconst_t *ic; + ic = (cdl_intconst_t *)(gc->ctx->globals + gc->ctx->num_globals); + ic += gc->gindex; + gc->value = ic->value; + return ic->neg; +} + +static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + FFIObject *ffi; + static char *keywords[] = {"module_name", "_version", "_types", + "_globals", "_struct_unions", "_enums", + "_typenames", NULL}; + char *ffiname = NULL, *types = NULL, *building = NULL; + Py_ssize_t version = -1; + Py_ssize_t types_len = 0; + PyObject *globals = NULL, *struct_unions = NULL, *enums = NULL; + PyObject *typenames = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sns#O!O!O!O!:FFI", keywords, + &ffiname, &version, &types, &types_len, + &PyTuple_Type, &globals, + &PyTuple_Type, &struct_unions, + &PyTuple_Type, &enums, + &PyTuple_Type, &typenames)) + return -1; + + ffi = (FFIObject *)self; + if (ffi->ctx_is_nonempty) { + PyErr_SetString(PyExc_ValueError, + "cannot call FFI.__init__() more than once"); + return -1; + } + ffi->ctx_is_nonempty = 1; + + if (types_len > 0) { + /* unpack a string of 4-byte entries into an array of _cffi_opcode_t */ + _cffi_opcode_t *ntypes; + Py_ssize_t i, n = types_len / 4; + + building = PyMem_Malloc(n * sizeof(_cffi_opcode_t)); + if (building == NULL) + goto error; + ntypes = (_cffi_opcode_t *)building; + + for (i = 0; i < n; i++) { + ntypes[i] = cdl_opcode(types); + types += 4; + } + ffi->types_builder.ctx.types = ntypes; + ffi->types_builder.ctx.num_types = n; + building = NULL; + } + + if (globals != NULL) { + /* unpack a tuple alternating strings and ints, each two together + describing one global_s entry with no specified address or size. + The int is only used with integer constants. */ + struct _cffi_global_s *nglobs; + cdl_intconst_t *nintconsts; + Py_ssize_t i, n = PyTuple_GET_SIZE(globals) / 2; + + i = n * (sizeof(struct _cffi_global_s) + sizeof(cdl_intconst_t)); + building = PyMem_Malloc(i); + if (building == NULL) + goto error; + memset(building, 0, i); + nglobs = (struct _cffi_global_s *)building; + nintconsts = (cdl_intconst_t *)(nglobs + n); + + for (i = 0; i < n; i++) { + char *g = PyString_AS_STRING(PyTuple_GET_ITEM(globals, i * 2)); + nglobs[i].type_op = cdl_opcode(g); g += 4; + nglobs[i].name = g; + if (_CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_CONSTANT_INT || + _CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_ENUM) { + PyObject *o = PyTuple_GET_ITEM(globals, i * 2 + 1); + nglobs[i].address = &_cdl_realize_global_int; + if (PyInt_Check(o)) { + nintconsts[i].neg = PyInt_AS_LONG(o) <= 0; + nintconsts[i].value = (long long)PyInt_AS_LONG(o); + } + else { + nintconsts[i].neg = PyObject_RichCompareBool(o, Py_False, + Py_LE); + nintconsts[i].value = PyLong_AsUnsignedLongLongMask(o); + if (PyErr_Occurred()) + goto error; + } + } + } + ffi->types_builder.ctx.globals = nglobs; + ffi->types_builder.ctx.num_globals = n; + building = NULL; + } + + if (struct_unions != NULL) { + /* unpack a tuple of struct/unions, each described as a sub-tuple; + the item 0 of each sub-tuple describes the struct/union, and + the items 1..N-1 describe the fields, if any */ + struct _cffi_struct_union_s *nstructs; + struct _cffi_field_s *nfields; + Py_ssize_t i, n = PyTuple_GET_SIZE(struct_unions); + Py_ssize_t nf = 0; /* total number of fields */ + + for (i = 0; i < n; i++) { + nf += PyTuple_GET_SIZE(PyTuple_GET_ITEM(struct_unions, i)) - 1; + } + i = (n * sizeof(struct _cffi_struct_union_s) + + nf * sizeof(struct _cffi_field_s)); + building = PyMem_Malloc(i); + if (building == NULL) + goto error; + memset(building, 0, i); + nstructs = (struct _cffi_struct_union_s *)building; + nfields = (struct _cffi_field_s *)(nstructs + n); + nf = 0; + + for (i = 0; i < n; i++) { + /* 'desc' is the tuple of strings (desc_struct, desc_field_1, ..) */ + PyObject *desc = PyTuple_GET_ITEM(struct_unions, i); + Py_ssize_t j, nf1 = PyTuple_GET_SIZE(desc) - 1; + char *s = PyString_AS_STRING(PyTuple_GET_ITEM(desc, 0)); + /* 's' is the first string, describing the struct/union */ + nstructs[i].type_index = cdl_4bytes(s); s += 4; + nstructs[i].flags = cdl_4bytes(s); s += 4; + nstructs[i].name = s; + if (nstructs[i].flags & _CFFI_F_OPAQUE) { + nstructs[i].size = (size_t)-1; + nstructs[i].alignment = -1; + nstructs[i].first_field_index = -1; + nstructs[i].num_fields = 0; + assert(nf1 == 0); + } + else { + nstructs[i].size = (size_t)-2; + nstructs[i].alignment = -2; + nstructs[i].first_field_index = nf; + nstructs[i].num_fields = nf1; + } + for (j = 0; j < nf1; j++) { + char *f = PyString_AS_STRING(PyTuple_GET_ITEM(desc, j + 1)); + /* 'f' is one of the other strings beyond the first one, + describing one field each */ + nfields[nf].field_type_op = cdl_opcode(f); f += 4; + nfields[nf].field_offset = (size_t)-1; + nfields[nf].field_size = cdl_4bytes(f); f += 4; + nfields[nf].name = f; + nf++; + } + } + ffi->types_builder.ctx.struct_unions = nstructs; + ffi->types_builder.ctx.fields = nfields; + ffi->types_builder.ctx.num_struct_unions = n; + building = NULL; + } + + if (enums != NULL) { + /* unpack a tuple of strings, each of which describes one enum_s + entry */ + struct _cffi_enum_s *nenums; + Py_ssize_t i, n = PyTuple_GET_SIZE(enums); + + i = n * sizeof(struct _cffi_enum_s); + building = PyMem_Malloc(i); + if (building == NULL) + goto error; + memset(building, 0, i); + nenums = (struct _cffi_enum_s *)building; + + for (i = 0; i < n; i++) { + char *e = PyString_AS_STRING(PyTuple_GET_ITEM(enums, i)); + /* 'e' is a string describing the enum */ + nenums[i].type_index = cdl_4bytes(e); e += 4; + nenums[i].type_prim = cdl_4bytes(e); e += 4; + nenums[i].name = e; e += strlen(e) + 1; + nenums[i].enumerators = e; + } + ffi->types_builder.ctx.enums = nenums; + ffi->types_builder.ctx.num_enums = n; + building = NULL; + } + + if (typenames != NULL) { + /* unpack a tuple of strings, each of which describes one typename_s + entry */ + struct _cffi_typename_s *ntypenames; + Py_ssize_t i, n = PyTuple_GET_SIZE(typenames); + + i = n * sizeof(struct _cffi_typename_s); + building = PyMem_Malloc(i); + if (building == NULL) + goto error; + memset(building, 0, i); + ntypenames = (struct _cffi_typename_s *)building; + + for (i = 0; i < n; i++) { + char *t = PyString_AS_STRING(PyTuple_GET_ITEM(typenames, i)); + /* 't' is a string describing the typename */ + ntypenames[i].type_index = cdl_4bytes(t); t += 4; + ntypenames[i].name = t; + } + ffi->types_builder.ctx.typenames = ntypenames; + ffi->types_builder.ctx.num_typenames = n; + building = NULL; + } + + /* Above, we took directly some "char *" strings out of the strings, + typically from somewhere inside tuples. Keep them alive by + incref'ing the whole input arguments. */ + Py_INCREF(args); + Py_XINCREF(kwds); + ffi->types_builder._keepalive1 = args; + ffi->types_builder._keepalive2 = kwds; + return 0; + + error: + if (building != NULL) + PyMem_Free(building); + if (!PyErr_Occurred()) + PyErr_NoMemory(); + return -1; +} |