summaryrefslogtreecommitdiff
path: root/c/cdlopen.c
blob: 0ed319b8b59d1b5f46e197678fd060c04de47e7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/* ffi.dlopen() interface with dlopen()/dlsym()/dlclose() */

static void *cdlopen_fetch(PyObject *libname, void *libhandle,
                           const 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)
{
    const char *modname;
    PyObject *temp, *result = NULL;
    void *handle;
    int auto_close;

    handle = b_do_dlopen(args, &modname, &temp, &auto_close);
    if (handle != NULL)
    {
        result = (PyObject *)lib_internal_new((FFIObject *)self,
                                              modname, handle, auto_close);
    }
    Py_XDECREF(temp);
    return result;
}

static PyObject *ffi_dlclose(PyObject *self, PyObject *args)
{
    LibObject *lib;
    void *libhandle;
    if (!PyArg_ParseTuple(args, "O!", &Lib_Type, &lib))
        return NULL;

    libhandle = lib->l_libhandle;
    if (libhandle != NULL)
    {
        lib->l_libhandle = 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);

        if (cdlopen_close(lib->l_libname, libhandle) < 0)
            return NULL;
    }
    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;

static 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", "_includes", NULL};
    char *ffiname = "?", *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, *includes = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds,
                                     "|sns#O!O!O!O!O!:FFI", keywords,
                                     &ffiname, &version, &types, &types_len,
                                     &PyTuple_Type, &globals,
                                     &PyTuple_Type, &struct_unions,
                                     &PyTuple_Type, &enums,
                                     &PyTuple_Type, &typenames,
                                     &PyTuple_Type, &includes))
        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 (version == -1 && types_len == 0)
        return 0;
    if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) {
        PyErr_Format(PyExc_ImportError,
                     "cffi out-of-line Python module '%s' has unknown "
                     "version %p", ffiname, (void *)version);
        return -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 = PyBytes_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 PY_MAJOR_VERSION < 3
                if (PyInt_Check(o)) {
                    nintconsts[i].neg = PyInt_AS_LONG(o) <= 0;
                    nintconsts[i].value = (long long)PyInt_AS_LONG(o);
                }
                else
#endif
                {
                    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 = PyBytes_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 | _CFFI_F_EXTERNAL)) {
                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 = PyBytes_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;
                if (_CFFI_GETOP(nfields[nf].field_type_op) != _CFFI_OP_NOOP) {
                    nfields[nf].field_size = cdl_4bytes(f); f += 4;
                }
                else {
                    nfields[nf].field_size = (size_t)-1;
                }
                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 = PyBytes_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 = PyBytes_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;
    }

    if (includes != NULL) {
        PyObject *included_libs;

        included_libs = PyTuple_New(PyTuple_GET_SIZE(includes));
        if (included_libs == NULL)
            return -1;

        Py_INCREF(includes);
        ffi->types_builder.included_ffis = includes;
        ffi->types_builder.included_libs = included_libs;
    }

    /* 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;
}