0.8.0.54:
[sbcl.git] / src / code / foreign.lisp
1 ;;;; support for dynamically loading foreign object files and
2 ;;;; resolving symbols therein
3
4 ;;;; This software is part of the SBCL system. See the README file for
5 ;;;; more information.
6 ;;;;
7 ;;;; This software is derived from the CMU CL system, which was
8 ;;;; written at Carnegie Mellon University and released into the
9 ;;;; public domain. The software is in the public domain and is
10 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
11 ;;;; files for more information.
12
13 (in-package "SB-ALIEN") ; (SB-ALIEN, not SB!ALIEN, since we're in warm load.)
14
15 ;;; SEMI-KLUDGE: Preferable would be to use something like O_NOFOLLOW
16 ;;; which will refuse to open() a file if it is a symlink; but I've
17 ;;; been told that is a FreeBSD/Linux-only thing.  Meanwhile, this will
18 ;;; make our filenames a lot less predictable.
19 ;;; (The man file for open() says O_EXCL should treat even a symlink as
20 ;;; an existing file.  I wonder if it really does that.)
21 ;;; Also, no more dependence on ASCII character ordering.
22 ;;; -- mrd 20021101
23 (defun generate-random-string (&optional (len 6))
24   (let* ((characters "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
25          (num (length characters))
26          (string (make-string len)))
27     (dotimes (i len string)
28       (setf (char string i)
29             (char characters (random num))))))
30
31 (defun pick-temporary-file-name (&optional
32                                  (base "/tmp/sbcl-tmp-~D~A"))
33   (let ((code (generate-random-string)))
34     (loop
35       (let ((name (format nil base (sb-unix:unix-getpid) code)))
36         (multiple-value-bind (fd errno)
37             (sb-unix:unix-open name
38                                (logior sb-unix:o_wronly
39                                        sb-unix:o_creat
40                                        sb-unix:o_excl)
41                                #o666)
42           (cond ((not (null fd))
43                  (sb-unix:unix-close fd)
44                  (return name))
45                 ((not (= errno sb-unix:eexist))
46                  (simple-file-perror "couldn't create temporary file ~S"
47                                      name
48                                      errno))
49                 (t
50                  (setf code (generate-random-string)))))))))
51
52 ;;; On any OS where we don't support foreign object file loading, any
53 ;;; query of a foreign symbol value is answered with "no definition
54 ;;; known", i.e. NIL.
55 #-(or linux sunos FreeBSD OpenBSD)
56 (defun get-dynamic-foreign-symbol-address (symbol)
57   (declare (type simple-string symbol) (ignore symbol))
58   nil)
59
60 ;;; dlsym()-based implementation of GET-DYNAMIC-FOREIGN-SYMBOL-ADDRESS
61 ;;; and functions (e.g. LOAD-FOREIGN) which affect it.  This should 
62 ;;; work on any ELF system with dlopen(3) and dlsym(3)
63 ;;; It also works on OpenBSD, which isn't ELF, but is otherwise modern
64 ;;; enough to have a fairly well working dlopen/dlsym implementation.
65 #-(or linux sunos FreeBSD OpenBSD)
66 (macrolet ((define-unsupported-fun (fun-name)
67              `(defun ,fun-name (&rest rest)
68                 "unsupported on this system"
69                 (declare (ignore rest))
70                 (error 'unsupported-operator :name ',fun-name))))
71   (define-unsupported-fun load-1-foreign)
72   (define-unsupported-fun load-foreign))
73 #+(or linux sunos FreeBSD OpenBSD)
74 (progn
75
76 ;;; flags for dlopen()
77 (defconstant rtld-lazy 1)               ; lazy function call binding?
78 (defconstant rtld-now 2)                ; immediate function call binding?
79 (defconstant rtld-global #x100)         ; symbols of loaded obj file
80                                         ; (and its dependencies) made
81                                         ; visible (as though the
82                                         ; obj file were linked directly
83                                         ; into the program)?
84
85 ;;; a list of handles returned from dlopen(3) (or possibly some
86 ;;; bogus value temporarily during initialization)
87 (defvar *handles-from-dlopen* nil)
88
89 ;;; Dynamically loaded stuff isn't there upon restoring from a save.
90 ;;; Clearing the variable this way was originally done primarily for
91 ;;; Irix, which resolves tzname at runtime, resulting in
92 ;;; *HANDLES-FROM-DLOPEN* (which was then called *TABLES-FROM-DLOPEN*)
93 ;;; being set in the saved core image, resulting in havoc upon
94 ;;; restart; but it seems harmless and tidy for other OSes too.
95 ;;;
96 ;;; Of course, it can be inconvenient that dynamically loaded stuff
97 ;;; goes away when we save and restore. However,
98 ;;;  (1) trying to avoid it by system programming here could open a
99 ;;;      huge can of worms, since e.g. now we would need to worry about
100 ;;;      libraries possibly being in different locations (file locations
101 ;;;      or memory locations) at restore time than at save time; and
102 ;;;  (2) by the time the application programmer is so deep into the
103 ;;;      the use of hard core extension features as to be doing
104 ;;;      dynamic loading of foreign files and saving/restoring cores,
105 ;;;      he probably has the sophistication to write his own after-save
106 ;;;      code to reload the libraries without much difficulty.
107
108 ;;; dan 2001.05.10 suspects that objection (1) is bogus for
109 ;;; dlsym()-enabled systems
110
111 (push (lambda () (setq *handles-from-dlopen* nil))
112       *after-save-initializations*)
113
114 (defvar *dso-linker* "/usr/bin/ld")
115 (defvar *dso-linker-options* '("-shared" "-o"))
116
117 (sb-alien:define-alien-routine dlopen system-area-pointer
118   (file sb-alien:c-string) (mode sb-alien:int))
119 (sb-alien:define-alien-routine dlsym system-area-pointer
120   (lib system-area-pointer)
121   (name sb-alien:c-string))
122 (sb-alien:define-alien-routine dlerror sb-alien:c-string)
123
124 ;;; Ensure that we've opened our own binary so we can dynamically resolve 
125 ;;; symbols in the C runtime.  
126 ;;;
127 ;;; Old comment: This used to happen only in
128 ;;; GET-DYNAMIC-FOREIGN-SYMBOL-ADDRESS, and only if no libraries were
129 ;;; dlopen()ed already, but that didn't work if something was
130 ;;; dlopen()ed before any problem global vars were used.  So now we do
131 ;;; this in any function that can add to the *HANDLES-FROM-DLOPEN*, as
132 ;;; well as in GET-DYNAMIC-FOREIGN-SYMBOL-ADDRESS.
133 ;;;
134 ;;; FIXME: It would work just as well to do it once at startup, actually.
135 ;;; Then at least we know it's done.    -dan 2001.05.10
136 (defun ensure-runtime-symbol-table-opened ()
137   (unless *handles-from-dlopen*
138     ;; Prevent recursive call if dlopen() isn't defined.
139     (setf *handles-from-dlopen* (int-sap 0))
140     (setf *handles-from-dlopen* (list (dlopen nil rtld-lazy)))
141     (when (zerop (sb-sys:sap-int (first *handles-from-dlopen*)))
142       (error "can't open our own binary's symbol table: ~S" (dlerror)))))
143
144 (defun load-1-foreign (file)
145   "the primitive upon which the more general LOAD-FOREIGN is built: load
146   a single foreign object file
147
148   To use LOAD-1-FOREIGN, at the Unix command line do this:
149     echo 'int summish(int x, int y) { return 1 + x + y; }' > /tmp/ffi-test.c
150     make /tmp/ffi-test.o # i.e. cc -c -o /tmp/ffi-test.o /tmp/ffi-test.c
151     ld -shared -o /tmp/ffi-test.so /tmp/ffi-test.o
152   then in SBCL do this:
153     (LOAD-1-FOREIGN \"/tmp/ffi-test.so\")
154     (DEFINE-ALIEN-ROUTINE SUMMISH INT (X INT) (Y INT))
155   Now running (SUMMISH 10 20) should return 31.
156 "
157   (ensure-runtime-symbol-table-opened)
158   ;; Note: We use RTLD-GLOBAL so that it can find all the symbols
159   ;; previously loaded. We use RTLD-NOW so that dlopen() will fail if
160   ;; not all symbols are defined.
161   (let* ((real-file (or (unix-namestring file) file))
162          (sap (dlopen real-file (logior rtld-now rtld-global))))
163        (if (zerop (sap-int sap))
164            (error "can't open object ~S: ~S" real-file (dlerror))
165            (pushnew sap *handles-from-dlopen* :test #'sap=)))
166   (values))
167
168 (defun get-dynamic-foreign-symbol-address (symbol)
169   (ensure-runtime-symbol-table-opened)
170   ;; Find the symbol in any of the loaded object files. Search in
171   ;; reverse order of loading, so that later loadings take precedence.
172   ;;
173   ;; FIXME: The way that we use PUSHNEW SAP in LOAD-1-FOREIGN means
174   ;; that the list isn't guaranteed to be in reverse order of loading,
175   ;; at least not if a file is loaded more than once. Is this the
176   ;; right thing? (In what cases does it matter?)
177   (dolist (handle *handles-from-dlopen*)
178     ;; KLUDGE: We implicitly exclude the possibility that the variable
179     ;; could actually be NULL, but the man page for dlsym(3) 
180     ;; recommends doing a more careful test. -- WHN 20000825
181     (let ((possible-result (sap-int (dlsym handle symbol))))
182       (unless (zerop possible-result)
183         (return possible-result)))))
184
185 (defun load-foreign (files
186                      &key
187                      (libraries '("-lc"))
188                      ;; FIXME: The old documentation said
189                      ;;   The BASE-FILE argument is used to specify a
190                      ;;   file to use as the starting place for
191                      ;;   defined symbols. The default is the C start
192                      ;;   up code for Lisp.
193                      ;; But the code ignored the BASE-FILE argument.
194                      ;; The comment above
195                      ;;   (DECLARE (IGNORE BASE-FILE))
196                      ;; said
197                      ;;   dlopen() remembers the name of an object,
198                      ;;   when dlopen()ing the same name twice, the
199                      ;;   old object is reused.
200                      ;; So I deleted all reference to BASE-FILE,
201                      ;; including the now-bogus reference to the
202                      ;; BASE-FILE argument in the documentation. But
203                      ;; are there any other subtleties of the new code
204                      ;; which need to be documented in its place?
205                      (env nil env-p)
206                      (environment (if env-p
207                                       (unix-environment-sbcl-from-cmu env)
208                                       (posix-environ))
209                                   environment-p))
210   #+sb-doc
211   "LOAD-FOREIGN loads a list of C object files into a running Lisp. The FILES
212   argument should be a single file or a list of files. The files may be
213   specified as namestrings or as pathnames. The libraries argument should be a
214   list of library files as would be specified to ld. They will be searched in
215   the order given. The default is just \"-lc\", i.e., the C library. The
216   ENVIRONMENT argument is a list of SIMPLE-STRINGs corresponding to the Unix
217   environment (\"man environ\") definitions for the invocation of the linker.
218   The default is the environment that Lisp is itself running in. Instead of
219   using the ENVIRONMENT argument, it is also possible to use the ENV argument,
220   using the older, lossy CMU CL representation."
221   (when (and env-p environment-p)
222     (error "can't specify :ENV and :ENVIRONMENT simultaneously"))
223   (let ((output-file (pick-temporary-file-name
224                       (concatenate 'string "/tmp/~D~A" (string (gensym)))))
225         (error-output (make-string-output-stream)))
226
227     (/show "running" *dso-linker*)
228     (force-output)
229     (unwind-protect
230         (let ((proc (sb-ext:run-program
231                      *dso-linker*
232                      (append *dso-linker-options*
233                              (list output-file)
234                              (append (mapcar (lambda (name)
235                                                (unix-namestring name nil))
236                                              (if (atom files)
237                                                  (list files)
238                                                files))
239                                      libraries))
240                      :environment environment
241                      :input nil
242                      :output error-output
243                      :error :output)))
244           (unless proc
245             (error "could not run ~A" *dso-linker*))
246           (unless (zerop (sb-ext:process-exit-code proc))
247             (sb-sys:serve-all-events 0)
248             (error "~A failed:~%~A" *dso-linker*
249                    (get-output-stream-string error-output)))
250           (load-1-foreign output-file))
251       #-sb-show (sb-unix:unix-unlink output-file)
252       #+sb-show (/show "not unlinking" output-file)))) ; so we can look at it
253
254 ) ; PROGN