1 /*
2 
3   Copyright (c) 2009-2013 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 
32 */
33 
34 #include <config.h>
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdint.h>
40 #include <errno.h>
41 #include <dlfcn.h>
42 #include <ffi.h>
43 
44 #include "uim.h"
45 #include "uim-scm.h"
46 #include "uim-scm-abbrev.h"
47 #include "uim-posix.h"
48 #include "uim-notify.h"
49 #include "dynlib.h"
50 
51 static const char *ffi_strerr_;
52 
53 typedef struct {
54   int flag;
55   char *arg;
56 } opt_args;
57 
58 static uim_lisp
make_arg_cons(const opt_args * arg)59 make_arg_cons(const opt_args *arg)
60 {
61   return CONS(MAKE_SYM(arg->arg), MAKE_INT(arg->flag));
62 }
63 
64 static uim_lisp
make_arg_list(const opt_args * list)65 make_arg_list(const opt_args *list)
66 {
67   uim_lisp ret_;
68   int i = 0;
69 
70   ret_ = uim_scm_null();
71   while (list[i].arg != 0) {
72     ret_ = CONS((uim_lisp)uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)make_arg_cons,
73                                                            (void *)&list[i]), ret_);
74     i++;
75   }
76   return ret_;
77 }
78 
79 static const opt_args dlopen_mode[] = {
80 #ifdef RTLD_LAZY
81   { RTLD_LAZY,   "$RTLD_LAZY" },
82 #endif
83 #ifdef RTLD_NOW
84   { RTLD_NOW,    "$RTLD_NOW" },
85 #endif
86 #ifdef RTLD_GLOBAL
87   { RTLD_GLOBAL, "$RTLD_GLOBAL" },
88 #endif
89 #ifdef RTLD_LOCAL
90   { RTLD_LOCAL,  "$RTLD_LOCAL" },
91 #endif
92 #ifdef DL_LAZY
93   { DL_LAZY,     "$DL_LAZY" },
94 #endif
95 #ifdef RTLD_TRACE
96   { RTLD_TRACE,  "$RTLD_TRACE" },
97 #endif
98   { 0, 0 }
99 };
100 
101 static uim_lisp uim_lisp_dlopen_mode_;
102 static uim_lisp
c_uim_lisp_dlopen_mode(void)103 c_uim_lisp_dlopen_mode(void)
104 {
105   return uim_lisp_dlopen_mode_;
106 }
107 
108 static uim_lisp
c_dlstrerr(void)109 c_dlstrerr(void)
110 {
111   if (ffi_strerr_)
112     return MAKE_STR(ffi_strerr_);
113   return MAKE_STR("");
114 }
115 
116 static uim_lisp
c_dlopen(uim_lisp path_,uim_lisp mode_)117 c_dlopen(uim_lisp path_, uim_lisp mode_)
118 {
119   const char *s;
120   void *handle = dlopen(REFER_C_STR(path_), C_INT(mode_));
121 
122   if ((s = dlerror()) != NULL) {
123     ffi_strerr_ = s;
124     return uim_scm_f();
125   }
126   ffi_strerr_ = NULL;
127   return MAKE_PTR(handle);
128 }
129 
130 static uim_lisp
c_dlclose(uim_lisp handle_)131 c_dlclose(uim_lisp handle_)
132 {
133   if (!PTRP(handle_))
134     return uim_scm_f();
135   dlclose(C_PTR(handle_));
136   ffi_strerr_ = NULL;
137   return uim_scm_t();
138 }
139 
140 static uim_lisp
c_dlsym(uim_lisp handle_,uim_lisp symbol_)141 c_dlsym(uim_lisp handle_, uim_lisp symbol_)
142 {
143   const char *s;
144   void *fun;
145 
146   fun = dlsym(C_PTR(handle_), REFER_C_STR(symbol_));
147   if ((s = dlerror()) != NULL) {
148     ffi_strerr_ = s;
149     return uim_scm_f();
150   }
151   ffi_strerr_ = NULL;
152   return MAKE_PTR(fun);
153 }
154 
155 typedef enum {
156   RET_UNKNOWN,
157   RET_VOID,
158   RET_UCHAR, RET_SCHAR,
159   RET_USHORT, RET_SSHORT,
160   RET_UINT, RET_SINT,
161   RET_ULONG, RET_SLONG,
162   RET_FLOAT, RET_DOUBLE,
163   RET_STR,
164   RET_PTR,
165   RET_SCM
166 } object_type;
167 
168 #define FFI_STRERR_BAD_TYPEDEF 0
169 #define FFI_STRERR_BAD_ABI     1
170 #define FFI_STRERR_UNKOWN      2
171 
172 static const char *ffi_strerr_messages[] = {
173   "ffi_prep_cif: FFI_BAD_TYPEDEF\n",
174   "ffi_prep_cif: FFI_BAD_ABI\n",
175   "ffi_prep_cif: unkown error\n"
176 };
177 
178 static object_type
select_object_type(uim_lisp type_)179 select_object_type(uim_lisp type_)
180 {
181   if (strcmp(REFER_C_STR(type_), "void") == 0)
182     return RET_VOID;
183   if (strcmp(REFER_C_STR(type_), "unsigned-char") == 0)
184     return RET_UCHAR;
185   if (strcmp(REFER_C_STR(type_), "signed-char") == 0)
186     return RET_SCHAR;
187   if (strcmp(REFER_C_STR(type_), "char") == 0)
188     return RET_SCHAR;
189   if (strcmp(REFER_C_STR(type_), "unsigned-short") == 0)
190     return RET_USHORT;
191   if (strcmp(REFER_C_STR(type_), "signed-short") == 0)
192     return RET_SSHORT;
193   if (strcmp(REFER_C_STR(type_), "short") == 0)
194     return RET_SSHORT;
195   if (strcmp(REFER_C_STR(type_), "unsigned-int") == 0)
196     return RET_SINT;
197   if (strcmp(REFER_C_STR(type_), "signed-int") == 0)
198     return RET_SINT;
199   if (strcmp(REFER_C_STR(type_), "int") == 0)
200     return RET_SINT;
201   if (strcmp(REFER_C_STR(type_), "unsigned-long") == 0)
202     return RET_ULONG;
203   if (strcmp(REFER_C_STR(type_), "signed-long") == 0)
204     return RET_SLONG;
205   if (strcmp(REFER_C_STR(type_), "long") == 0)
206     return RET_SLONG;
207   if (strcmp(REFER_C_STR(type_), "float") == 0)
208     return RET_FLOAT;
209   if (strcmp(REFER_C_STR(type_), "double") == 0)
210     return RET_DOUBLE;
211   if (strcmp(REFER_C_STR(type_), "string") == 0)
212     return RET_STR;
213   if (strcmp(REFER_C_STR(type_), "pointer") == 0)
214     return RET_PTR;
215   if (strcmp(REFER_C_STR(type_), "scheme-object") == 0)
216     return RET_SCM;
217 
218   ERROR_OBJ("unknown object type", type_);
219   return RET_UNKNOWN;
220 }
221 
222 static uim_lisp
c_ffi_call(uim_lisp result_,uim_lisp fun_,uim_lisp argv_)223 c_ffi_call(uim_lisp result_, uim_lisp fun_, uim_lisp argv_)
224 {
225   ffi_cif cif;
226   ffi_type **arg_types;
227   void **arg_values;
228   ffi_status status;
229   ffi_type *result_type = NULL;
230   void *result;
231   int args;
232   int i;
233   void *p;
234   uim_lisp ret_;
235   object_type return_object_type;
236   int input_void = 0;
237 
238   args = uim_scm_length(argv_);
239   arg_types = uim_malloc(args * sizeof(void *));
240   arg_values = uim_malloc(args * sizeof(ffi_type *));
241 
242   return_object_type = select_object_type(result_);
243 
244   switch (return_object_type) {
245   case RET_UNKNOWN:
246     break;
247   case RET_VOID:
248     result_type = &ffi_type_void;
249     break;
250   case RET_UCHAR:
251     result_type = &ffi_type_uchar;
252     break;
253   case RET_SCHAR:
254     result_type = &ffi_type_schar;
255     break;
256   case RET_USHORT:
257     result_type = &ffi_type_ushort;
258     break;
259   case RET_SSHORT:
260     result_type = &ffi_type_sshort;
261     break;
262   case RET_ULONG:
263     result_type = &ffi_type_ulong;
264     break;
265   case RET_SLONG:
266     result_type = &ffi_type_slong;
267     break;
268   case RET_UINT:
269     result_type = &ffi_type_uint;
270     break;
271   case RET_SINT:
272     result_type = &ffi_type_sint;
273     break;
274   case RET_FLOAT:
275     result_type = &ffi_type_float;
276     break;
277   case RET_DOUBLE:
278     result_type = &ffi_type_double;
279     break;
280   case RET_STR:
281     result_type = &ffi_type_pointer;
282     break;
283   case RET_PTR:
284     result_type = &ffi_type_pointer;
285     break;
286   case RET_SCM:
287     result_type = &ffi_type_pointer;
288     break;
289   }
290 
291   result = uim_malloc(1024); /* huge? */
292 
293   for (i = 0; i < args; i++) {
294     uim_lisp arg_ = CAR(argv_);
295 
296     switch (select_object_type(CAR(arg_))) {
297     case RET_UNKNOWN:
298       break;
299     case RET_VOID:
300       input_void = 1;
301       break;
302     case RET_UCHAR:
303       p = uim_malloc(sizeof(unsigned char));
304       *((unsigned char *)p) = C_CHAR(CDR(arg_));
305       arg_types[i] = &ffi_type_uchar;
306       arg_values[i] = p;
307       break;
308     case RET_SCHAR:
309       p = uim_malloc(sizeof(signed char));
310       *((signed char *)p) = C_CHAR(CDR(arg_));
311       arg_types[i] = &ffi_type_schar;
312       arg_values[i] = p;
313       break;
314     case RET_USHORT:
315       p = uim_malloc(sizeof(unsigned short));
316       *((unsigned short *)p) = C_INT(CDR(arg_));
317       arg_types[i] = &ffi_type_ushort;
318       arg_values[i] = p;
319       break;
320     case RET_SSHORT:
321       p = uim_malloc(sizeof(unsigned short));
322       *((signed short *)p) = C_INT(CDR(arg_));
323       arg_types[i] = &ffi_type_sshort;
324       arg_values[i] = p;
325       break;
326     case RET_UINT:
327       p = uim_malloc(sizeof(unsigned int));
328       *((unsigned int *)p) = C_INT(CDR(arg_));
329       arg_types[i] = &ffi_type_uint;
330       arg_values[i] = p;
331       break;
332     case RET_SINT:
333       p = uim_malloc(sizeof(signed int));
334       *((signed int *)p) = C_INT(CDR(arg_));
335       arg_types[i] = &ffi_type_sint;
336       arg_values[i] = p;
337       break;
338     case RET_ULONG:
339       p = uim_malloc(sizeof(unsigned long));
340       *((unsigned long *)p) = C_INT(CDR(arg_));
341       arg_types[i] = &ffi_type_ulong;
342       arg_values[i] = p;
343       break;
344     case RET_SLONG:
345       p = uim_malloc(sizeof(signed long));
346       *((signed long *)p) = C_INT(CDR(arg_));
347       arg_types[i] = &ffi_type_slong;
348       arg_values[i] = p;
349       break;
350     case RET_FLOAT:
351       {
352 	char *endptr;
353 	p = uim_malloc(sizeof(float));
354 	*((double *)p) = strtof(REFER_C_STR(CDR(arg_)), &endptr);
355 	arg_types[i] = &ffi_type_float;
356 	arg_values[i] = p;
357       }
358       break;
359     case RET_DOUBLE:
360       {
361 	char *endptr;
362 	p = uim_malloc(sizeof(double));
363 	*((double *)p) = strtod(REFER_C_STR(CDR(arg_)), &endptr);
364 	arg_types[i] = &ffi_type_double;
365 	arg_values[i] = p;
366       }
367       break;
368     case RET_STR:
369       p = uim_malloc(sizeof(void *));
370       *((void **)p) = (void *)REFER_C_STR(CDR(arg_));
371       arg_types[i] = &ffi_type_pointer;
372       arg_values[i] = p;
373       break;
374     case RET_PTR:
375       p = uim_malloc(sizeof(void *));
376       if (NULLP(CDR(arg_)))
377 	*((void **)p) = NULL;
378       else
379 	*((void **)p) = C_PTR(CDR(arg_));
380       arg_types[i] = &ffi_type_pointer;
381       arg_values[i] = p;
382       break;
383     case RET_SCM:
384       p = uim_malloc(sizeof(void *));
385       *((void **)p) = CDR(arg_);
386       arg_types[i] = &ffi_type_pointer;
387       arg_values[i] = p;
388     }
389     argv_ = CDR(argv_);
390   }
391 
392   if (input_void)
393     args = 0;
394   status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args, result_type, arg_types);
395   switch (status) {
396   case FFI_OK:
397     break;
398   case FFI_BAD_TYPEDEF:
399     ffi_strerr_ = ffi_strerr_messages[FFI_STRERR_BAD_TYPEDEF];
400     break;
401   case FFI_BAD_ABI:
402     ffi_strerr_ = ffi_strerr_messages[FFI_STRERR_BAD_ABI];
403     break;
404   default:
405     ffi_strerr_ = ffi_strerr_messages[FFI_STRERR_UNKOWN];
406   }
407 
408   if (status == FFI_OK)
409     ffi_call(&cif, (void (*)(void))C_PTR(fun_), result, arg_values);
410 
411   for (i = 0; i < args; i++)
412     free(arg_values[i]);
413   free(arg_types);
414   free(arg_values);
415 
416   if (status != FFI_OK) {
417     free(result);
418     return uim_scm_f();
419   }
420   ret_ = uim_scm_f();
421 
422   switch (return_object_type) {
423   case RET_UNKNOWN:
424   case RET_VOID:
425     break;
426   case RET_UCHAR:
427     ret_ = MAKE_CHAR(*(unsigned char *)result);
428     break;
429   case RET_SCHAR:
430     ret_ = MAKE_CHAR(*(signed char *)result);
431     break;
432   case RET_USHORT:
433     ret_ = MAKE_INT(*(unsigned short *)result);
434     break;
435   case RET_SSHORT:
436     ret_ = MAKE_INT(*(signed short *)result);
437     break;
438   case RET_UINT:
439     ret_ = MAKE_INT(*(unsigned int *)result);
440     break;
441   case RET_SINT:
442     ret_ = MAKE_INT(*(signed int *)result);
443     break;
444   case RET_ULONG:
445     ret_ = MAKE_INT(*(unsigned long *)result);
446     break;
447   case RET_SLONG:
448     ret_ = MAKE_INT(*(signed long *)result);
449     break;
450   case RET_FLOAT:
451     {
452       char str[1024];
453       snprintf(str, sizeof(str), "%f", *((float *)result));
454       ret_ = MAKE_STR(str);
455     }
456     break;
457   case RET_DOUBLE:
458     {
459       char str[1024];
460       snprintf(str, sizeof(str), "%f", *((double *)result));
461       ret_ = MAKE_STR(str);
462     }
463     break;
464   case RET_STR:
465     ret_ = MAKE_STR(*((char **)result));
466     break;
467   case RET_PTR:
468     ret_ = MAKE_PTR(*((void **)result));
469     break;
470   case RET_SCM:
471     ret_ = *(uim_lisp *)result;
472     break;
473   }
474 
475   free(result);
476 
477   ffi_strerr_ = NULL;
478 
479   return ret_;
480 }
481 
482 static uim_lisp
c_ffi_function(uim_lisp handler_,uim_lisp result_,uim_lisp funstr_,uim_lisp argv_)483 c_ffi_function(uim_lisp handler_, uim_lisp result_, uim_lisp funstr_, uim_lisp argv_)
484 {
485   uim_lisp fun_ = c_dlsym(handler_, funstr_);
486   if (PTRP(fun_))
487     return c_ffi_call(result_, fun_, argv_);
488   return uim_scm_f();
489 }
490 
491 void
uim_plugin_instance_init(void)492 uim_plugin_instance_init(void)
493 {
494   uim_scm_init_proc0("dlstrerr", c_dlstrerr);
495 
496   uim_lisp_dlopen_mode_ = make_arg_list(dlopen_mode);
497   uim_scm_gc_protect(&uim_lisp_dlopen_mode_);
498   uim_scm_init_proc0("dlopen-mode", c_uim_lisp_dlopen_mode);
499   uim_scm_init_proc2("dlopen", c_dlopen);
500 
501   uim_scm_init_proc1("dlclose", c_dlclose);
502   uim_scm_init_proc2("dlsym", c_dlsym);
503 
504   uim_scm_init_proc3("ffi-call", c_ffi_call);
505   uim_scm_init_proc4("ffi-function", c_ffi_function);
506 }
507 
508 void
uim_plugin_instance_quit(void)509 uim_plugin_instance_quit(void)
510 {
511   uim_scm_gc_unprotect(&uim_lisp_dlopen_mode_);
512 }
513