Dear Gambit enthusiasts,
I think I figured out how to use dlopen to run functions from Gambit that live in dynamically loaded libraries (plugins); my best to date is below.
But I'm not quite happy with it--because it's kind of elaborate. That is, I couldn't figure out how to avoid some of the boiler plate below. Perhaps there is a shorter way?
So my question for Gambit enthusiasts is: Is there a cleaner way to do this--to run functions from plugin libraries? i.e. without having to pre-declare call_plugin_print8 and call_print8. What if you wanted to make such a call after inspecting a new library at runtime?
Many thanks! Gambit rocks! Thanks again for such a great system.
- Jason
;;;;;;;;;;;;;;; file: libex.cpp ;;;;;;;;;;;; ; ; a simple library of one function for ; testing/development of plugin loading ;
#include <stdio.h>
extern "C" int print8() { printf("\n In print8() : returning 8\n"); return 8; }
;;;;;;;;;;;;;;; file: libex.h ;;;;;;;;;;;;
#ifndef LIBEX_H #define LIBEX_H int print8(); #endif
;;;;;;;;;;;;;;; file: dlopen.scm ;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; dlopen.scm : plugins in Gambit ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; compile and link with: ; ; gsc -link dlopen.scm ; gcc -I/usr/local/Gambit-C/include -L/usr/local/Gambit-C/lib dlopen.c dlopen_.c -lgambc -lm -lutil -ldl -o dlopen
(c-declare #<<c-declare-end
#include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include "libex.h"
/* simple wrapper to do runtime plugin function calls (call into .so library) */ int call_plugin_print8(void* handle) { char *error; int (*my_func)(); // my_func = (int (*)()) dlsym(handle,"print8"); *(void **) (&my_func) = dlsym(handle, "print8");
if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(EXIT_FAILURE); }
return (*my_func)(); }
c-declare-end )
; from dlfcn.h ; see man dlopen ; ; void * dlopen(const char *filename, int flag); ; char * dlerror(void); ; void * dlsym(void *handle, const char *symbol); ; int dlclose(void *handle); ; ; translated into Gambit-C FFI: ; (define dlopen (c-lambda (nonnull-char-string int32) (pointer void) "dlopen")) (define dlerror (c-lambda () char-string "dlerror")) (define dlsym (c-lambda ((pointer void) nonnull-char-string) (pointer void) "dlsym")) (define dlclose (c-lambda ((pointer void)) int32 "dlclose"))
;; hmm is there a defconstant in scheme that we could use for these C header constants? ;; for now generate a function of the same name to get us the constant int value we need. (define RTLD_LAZY (c-lambda () int32 "___result = RTLD_LAZY;"))
; declare the type of the foreign function (define call_print8 (c-lambda ((pointer void)) int32 "call_plugin_print8"))
(let* ((h (dlopen "/home/jaten/dj/gamb/libex.so" (RTLD_LAZY))) (res (dlsym h "print8")))
(if (null? res) (error "could not locate symbol 'print8' in the plugin library!"))
(pretty-print (list "calling print8() gives: " (call_print8 h))) (pretty-print res) (pretty-print h) (pretty-print (list "The value of the constant RTLD_LAZY is: " (RTLD_LAZY))))
;; output:
;;; $ ./dlopen ; ; In print8() : returning 8 ;8 ;#<void* #2 0x7fdd20c1868c> ;#<void* #3 0x22cd7b0> ;("The value of the constant RTLD_LAZY is: " 1)
;;;;;;;;;;;;;;; file: Makefile ;;;;;;;;;;;;
all: gsc -link dlopen.scm gcc -I/usr/local/Gambit-C/include -L/usr/local/Gambit-C/lib dlopen.c dlopen_.c -lgambc -lm -lutil -ldl -o dlopen g++ -c libex.cpp -fpic -o libex.o g++ -shared libex.o -o libex.so @echo "testing ./dlopen" ./dlopen
clean: rm -f dlopen dlopen_.c dlopen.c dlopen.o1 dlopen.o2 *~ *.o *.so
Afficher les réponses par date
On 2011-04-06, at 3:31 AM, Jason E. Aten wrote:
Dear Gambit enthusiasts,
I think I figured out how to use dlopen to run functions from Gambit that live in dynamically loaded libraries (plugins); my best to date is below.
But I'm not quite happy with it--because it's kind of elaborate. That is, I couldn't figure out how to avoid some of the boiler plate below. Perhaps there is a shorter way?
So my question for Gambit enthusiasts is: Is there a cleaner way to do this--to run functions from plugin libraries? i.e. without having to pre-declare call_plugin_print8 and call_print8. What if you wanted to make such a call after inspecting a new library at runtime?
Many thanks! Gambit rocks! Thanks again for such a great system.
- Jason
Here's my shot at improving your code. Basically you want to convert the void* pointer returned by dlsym into an actual Scheme function that you can call without going through a proxy. In dlopen.scm I would use this code to implement the conversion:
(c-define-type void* (pointer void))
;;-----------------------------------------------------------------------------
;; Attempt #1, does not work (but should!).
(define convert.->int (c-lambda (void*) (function () int) "___result = ___arg1;"))
(define convert.int->int (c-lambda (void*) (function (int) int) "___result = ___arg1;"))
;;-----------------------------------------------------------------------------
;; Attempt #2, does work but a bit clunky.
(define (convert.->int ptr) (lambda () ((c-lambda (void*) int "___result = ___CAST(int (*)(),___arg1)();") ptr)))
(define (convert.int->int ptr) (lambda (x) ((c-lambda (void* int) int "___result = ___CAST(int (*)(int),___arg1)(___arg2);") ptr x)))
;;-----------------------------------------------------------------------------
;; Test code. Note the conversion to get a function from the void* ;; pointer returned by dlsym.
(define (test) (let* ((h (dlopen "libex.so" (RTLD_LAZY))) (print8 (convert.->int (dlsym h "print8"))) (square (convert.int->int (dlsym h "square")))) (if (not print8) (error "could not locate symbol 'print8' in the plugin library!") (pretty-print (print8))) (if (not square) (error "could not locate symbol 'square' in the plugin library!") (pretty-print (square 10)))))
(test)
;;-----------------------------------------------------------------------------
Note that I have added an int->int function in libex.c:
int print8() { printf("\n In print8() : returning 8\n"); return 8; }
int square(int x) { printf("\n In square(int x) : returning x*x\n"); return x*x; }
The first attempt, which has nicer code, does not work because I never got around to implementing that conversion in Gambit's FFI.
A more dynamic alternative, which would avoid having to decide on the function types at compile time, is to use libffi. I haven't tried it myself.
Marc