1 ;;;; support for dynamically loading foreign object files
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
17 ;;; not needed until we implement full-blown LOAD-FOREIGN
19 (defun pick-temporary-file-name (&optional
20 ;; KLUDGE: There are various security
21 ;; nastyisms associated with easily
22 ;; guessable temporary file names,
23 ;; and we haven't done anything to
24 ;; work around them here. -- pointed
25 ;; out by Dan Barlow on sbcl-devel
27 (base "/tmp/sbcl-tmp-~D~C"))
28 (let ((code (char-code #\A)))
30 (let ((name (format nil base (sb-unix:unix-getpid) (code-char code))))
31 (multiple-value-bind (fd errno)
32 (sb-unix:unix-open name
33 (logior sb-unix:o_wronly
37 (cond ((not (null fd))
38 (sb-unix:unix-close fd)
40 ((not (= errno sb-unix:eexist))
41 (error "could not create temporary file ~S: ~A"
43 (sb-unix:get-unix-error-msg errno)))
44 ;; KLUDGE: depends on ASCII character ordering -- WHN 20000128
45 ((= code (char-code #\Z))
46 (setf code (char-code #\a)))
47 ((= code (char-code #\z))
53 ;;; On any OS where we don't support foreign object file loading, any
54 ;;; query of a foreign symbol value is answered with "no definition
57 ;;; (On any OS which *does* support foreign object file loading, this
58 ;;; placeholder implementation is overwritten by a subsequent real
60 (defun get-dynamic-foreign-symbol-address (symbol)
61 (declare (type simple-string symbol) (ignore symbol))
64 ;;; Linux implementation of GET-DYNAMIC-FOREIGN-SYMBOL-ADDRESS
65 ;;; and functions (e.g. LOAD-FOREIGN) which affect it
69 ;;; flags for dlopen()
70 (defconstant rtld-lazy 1) ; lazy function call binding?
71 (defconstant rtld-now 2) ; immediate function call binding?
72 (defconstant rtld-global #x100) ; symbols of loaded obj file
73 ; (and its dependencies) made
74 ; visible (as though the
75 ; obj file were linked directly
78 ;;; a list of tables returned from dlopen(3) (or possibly some
79 ;;; bogus value temporarily during initialization)
80 (defvar *tables-from-dlopen* nil)
81 ;;; Dynamically loaded stuff isn't there upon restoring from a save.
82 ;;; Clearing the variable this way was originally done primarily for
83 ;;; Irix, which resolves tzname at runtime, resulting in
84 ;;; *TABLES-FROM-DLOPEN* being set in the saved core image, resulting
85 ;;; in havoc upon restart; but it seems harmless and tidy for other
88 ;;; Of course, it can be inconvenient that dynamically loaded stuff
89 ;;; goes away when we save and restore. However,
90 ;;; (1) trying to avoid it by system programming here could open a
91 ;;; huge can of worms, since e.g. now we would need to worry about
92 ;;; libraries possibly being in different locations (file locations
93 ;;; or memory locations) at restore time than at save time; and
94 ;;; (2) by the time the application programmer is so deep into the
95 ;;; the use of hard core extension features as to be doing
96 ;;; dynamic loading of foreign files and saving/restoring cores,
97 ;;; he probably has the sophistication to write his own after-save
98 ;;; code to reload the libraries without much difficulty.
99 (push (lambda () (setq *tables-from-dlopen* nil))
100 sb-int:*after-save-initializations*)
102 ;;; not needed until we implement full-blown LOAD-FOREIGN
104 (defvar *dso-linker* "/usr/bin/ld")
105 (defvar *dso-linker-options* '("-G" "-o"))
108 (sb-alien:def-alien-routine dlopen system-area-pointer
109 (file sb-c-call:c-string) (mode sb-c-call:int))
110 (sb-alien:def-alien-routine dlsym system-area-pointer
111 (lib system-area-pointer)
112 (name sb-c-call:c-string))
113 (sb-alien:def-alien-routine dlerror sb-c-call:c-string)
115 ;;; Ensure that we've opened our own binary so we can resolve global
116 ;;; variables in the Lisp image that come from libraries. This used to
117 ;;; happen only in GET-DYNAMIC-FOREIGN-SYMBOL-ADDRESS, and only if no
118 ;;; libraries were dlopen()ed already, but that didn't work if
119 ;;; something was dlopen()ed before any problem global vars were used.
120 ;;; So now we do this in any function that can add to the
121 ;;; *TABLES-FROM-DLOPEN*, as well as in
122 ;;; GET-DYNAMIC-FOREIGN-SYMBOL-ADDRESS.
123 (defun ensure-lisp-table-opened ()
124 (unless *tables-from-dlopen*
125 ;; Prevent recursive call if dlopen() isn't defined.
126 (setf *tables-from-dlopen* (int-sap 0))
127 (setf *tables-from-dlopen* (list (dlopen nil rtld-lazy)))
128 (when (zerop (sb-sys:sap-int (first *tables-from-dlopen*)))
129 (error "can't open global symbol table: ~S" (dlerror)))))
131 (defun load-1-foreign (file)
132 "a primitive way to load a foreign object file. (LOAD-FOREIGN is
133 probably preferred, but as of SBCL 0.6.7 is not implemented..)
135 To use LOAD-1-FOREIGN, at the Unix command line do this:
136 echo 'int summish(int x, int y) { return 1 + x + y; }' > /tmp/ffi-test.c
137 make /tmp/ffi-test.o # i.e. cc -c -o /tmp/ffi-test.o /tmp/ffi-test.c
138 ld -shared -o /tmp/ffi-test.so /tmp/ffi-test.o
139 then in SBCL do this:
140 (LOAD-1-FOREIGN \"/tmp/ffi-test.so\")
141 (DEF-ALIEN-ROUTINE SUMMISH INT (X INT) (Y INT))
142 Now running (SUMMISH 10 20) should return 31.
144 (ensure-lisp-table-opened)
145 ;; Note: We use RTLD-GLOBAL so that it can find all the symbols
146 ;; previously loaded. We use RTLD-NOW so that dlopen() will fail if
147 ;; not all symbols are defined.
148 (let ((sap (dlopen file (logior rtld-now rtld-global))))
149 (if (zerop (sap-int sap))
150 (error "can't open object ~S: ~S" file (dlerror))
151 (pushnew sap *tables-from-dlopen* :test #'sap=)))
154 (defun get-dynamic-foreign-symbol-address (symbol)
155 (ensure-lisp-table-opened)
156 ;; Find the symbol in any of the loaded object files. Search in
157 ;; reverse order of loading, so that later loadings take precedence.
159 ;; FIXME: The way that we use PUSHNEW SAP in LOAD-1-FOREIGN means
160 ;; that the list isn't guaranteed to be in reverse order of loading,
161 ;; at least not if a file is loaded more than once. Is this the
162 ;; right thing? (In what cases does it matter?)
163 (dolist (table *tables-from-dlopen*)
164 ;; KLUDGE: We implicitly exclude the possibility that the variable
165 ;; could actually be NULL, but the man page for dlsym(3)
166 ;; recommends doing a more careful test. -- WHN 20000825
167 (let ((possible-result (sap-int (dlsym table symbol))))
168 (unless (zerop possible-result)
169 (return possible-result)))))
171 ;;; code partially ported from CMU CL to SBCL, but needs RUN-PROGRAM
173 (defun load-foreign (files &key
176 ;; Note: Since SBCL has no *ENVIRONMENT-LIST*
177 ;; variable, if this code is ever restored,
178 ;; the default should be taken from the alien
179 ;; "environ" variable.
181 (env sb-ext:*environment-list*))
183 "LOAD-FOREIGN loads a list of C object files into a running Lisp. The FILES
184 argument should be a single file or a list of files. The files may be
185 specified as namestrings or as pathnames. The libraries argument should be a
186 list of library files as would be specified to ld. They will be searched in
187 the order given. The default is just \"-lc\", i.e., the C library. The
188 base-file argument is used to specify a file to use as the starting place for
189 defined symbols. The default is the C start up code for Lisp. The ENV
190 argument is the Unix environment variable definitions for the invocation of
191 the linker. The default is the environment passed to Lisp."
192 ;; Note: dlopen() remembers the name of an object, when dlopen()ing
193 ;; the same name twice, the old object is reused.
194 (declare (ignore base-file))
195 (let ((output-file (pick-temporary-file-name
196 (concatenate 'string "/tmp/~D~C" (string (gensym)))))
197 (error-output (make-string-output-stream)))
199 (/show "running" *dso-linker*)
202 (let ((proc (sb-ext:run-program
204 (append *dso-linker-options*
206 (append (mapcar #'(lambda (name)
207 (unix-namestring name nil))
217 (error "could not run ~A" *dso-linker*))
218 (unless (zerop (sb-ext:process-exit-code proc))
219 (sb-sys:serve-all-events 0)
220 (error "~A failed:~%~A" *dso-linker*
221 (get-output-stream-string error-output)))
222 (load-1-foreign output-file))
223 #-sb-show (sb-unix:unix-unlink output-file)
224 #+sb-show (/show "not unlinking" output-file)))) ; so we can look at it