[prev in list] [next in list] [prev in thread] [next in thread] 

List:       sbcl-devel
Subject:    [Sbcl-devel] [PATCH 2/4] Prefill the linkage table using references resolved at link time
From:       Eric Timmons <etimmons () mit ! edu>
Date:       2020-07-13 20:02:34
Message-ID: 20200713200236.22507-3-etimmons () mit ! edu
[Download RAW message or body]

The second genesis produces a c file that populates lisp_linkage_values with
references to foreign data and functions referenced in cold init. This
autodetection can be augmented by providing --extra-linkage-table-entries to
make-config.sh.

The system linker then resolves those references for us at link time, avoiding
the need to call into dlsym to resolve them ourselves.

os_dlsym_default is then no longer needed.
---
 .gitignore                        |   1 +
 make-config.sh                    |  21 ++-
 make-genesis-2.lisp               |   5 +-
 make-target-1.sh                  |   5 +-
 src/compiler/generic/genesis.lisp |  49 ++++-
 src/runtime/GNUmakefile           |   7 +-
 src/runtime/coreparse.c           |  26 +--
 src/runtime/os-common.c           |  78 ++++----
 src/runtime/runtime.h             |   2 -
 src/runtime/win32-os.c            | 296 ------------------------------
 10 files changed, 116 insertions(+), 374 deletions(-)

diff --git a/.gitignore b/.gitignore
index 2ea1e4263..848ef25a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ src/runtime/target-lispregs.h
 src/runtime/target-os.h
 src/runtime/ppc-linux-mcontext.h
 src/runtime/sparc-funcdef.h
+src/runtime/linkage-table-prefill-info.c
 tests/test-status.lisp-expr
 tests/test.log
 tests/*.so
diff --git a/make-config.sh b/make-config.sh
index 518f33282..9dc4972ce 100755
--- a/make-config.sh
+++ b/make-config.sh
@@ -88,7 +88,10 @@ do
         ;;
       --without)
         WITHOUT_FEATURES="$WITHOUT_FEATURES :$optarg"
-	;;
+	    ;;
+      --extra-linkage-table-entries=)
+        $optarg_ok && SBCL_EXTRA_LINKAGE_TABLE_ENTRIES=$optarg
+        ;;
       --fancy)
         WITH_FEATURES="$WITH_FEATURES $FANCY_FEATURES"
         # Lower down we add :sb-thread for platforms where it can be built.
@@ -213,6 +216,17 @@ Options:
                   Transfer the files to/from directory /home/user/sbcl
                   on host-machine.

+  --extra-linkage-table-entries=<path> Specify extra C symbols to include in
+                                       the linkage table
+
+      Path to a file specifying symbols that must be included in the
+      SBCL linkage table. Useful for statically linking libraries
+      into the runtime and ensuring the linker does not remove them.
+      The file must contain a single list of two element lists. Each
+      sublist must have a string naming a C symbol as its first
+      element and NIL or T (if the symbol names a variable) as its
+      second element.
+
 EOF
   exit 1
 fi
@@ -254,6 +268,11 @@ find_gnumake

 ./generate-version.sh

+# Copy the extra linkage entries to output folder for Genesis to find.
+if [ -n "$SBCL_EXTRA_LINKAGE_TABLE_ENTRIES" ] && [ -f \
"$SBCL_EXTRA_LINKAGE_TABLE_ENTRIES" ]; then +    cp \
"$SBCL_EXTRA_LINKAGE_TABLE_ENTRIES" output/extra-linkage-table-entries.lisp-expr +fi
+
 # Now that we've done our option parsing and found various
 # dependencies, write them out to a file to be sourced by other
 # scripts.
diff --git a/make-genesis-2.lisp b/make-genesis-2.lisp
index cb17160e1..a4a58e308 100644
--- a/make-genesis-2.lisp
+++ b/make-genesis-2.lisp
@@ -24,7 +24,10 @@
          :core-file-name "output/cold-sbcl.core"
          ;; The map file is not needed by the system, but can be
          ;; very handy when debugging cold init problems.
-         :map-file-name "output/cold-sbcl.map")
+         :map-file-name "output/cold-sbcl.map"
+         :linkage-table-prefill-info-c-name \
"src/runtime/linkage-table-prefill-info.c" +         :extra-linkage-table-entries \
(when (probe-file "output/extra-linkage-table-entries.lisp-expr") +                   \
(read-from-file "output/extra-linkage-table-entries.lisp-expr")))  #+cmu (ext:quit)
 #+clisp (ext:quit)
 #+abcl (ext:quit)
diff --git a/make-target-1.sh b/make-target-1.sh
index 3d2e0cf41..b7cde9503 100755
--- a/make-target-1.sh
+++ b/make-target-1.sh
@@ -31,8 +31,9 @@ fi

 # Build the runtime system and symbol table (.nm) file.
 #
-# This C build has to come after the first genesis in order to get
-# the sbcl.h the C build needs.
+# This C build has to come after the first genesis in order to get the
+# sbcl.h the C build needs. This also needs to come after the second
+# genesis to get the symbol prelink table needed by the runtime.
 echo //building runtime system and symbol table file

 # The clean is needed for Darwin's readonlyspace hack.
diff --git a/src/compiler/generic/genesis.lisp b/src/compiler/generic/genesis.lisp
index bd5c890ec..baf8b0d0f 100644
--- a/src/compiler/generic/genesis.lisp
+++ b/src/compiler/generic/genesis.lisp
@@ -2255,6 +2255,45 @@ (defun foreign-symbols-to-core ()
                (cold-cons (cold-intern (first rtn)) (make-fixnum-descriptor (cdr \
rtn))))  '*!initial-assembler-routines*)))

+(defun foreign-symbols-to-c (output-pathname)
+  (with-open-file (output output-pathname
+                          :direction :output
+                          :if-exists :supersede)
+    (let ((sorted-pairs (sort (%hash-table-alist *cold-foreign-symbol-table*) #'< \
:key #'cdr))) +      ;; Needed for uintptr_t. We use the raw uintptr_t as we don't \
want to have +      ;; to include any SBCL headers just to get at lispobj.
+      (format output "#include <stdint.h>~%~%")
+
+      ;; Write out the extern definitions. Everything is a void function (even
+      ;; variables) because compilers don't like void variables. Remove
+      ;; lisp_linkage_values as we need to write to it, so we should use the
+      ;; actual type.
+      (format output "extern void ~{~A()~^, ~};~%~%"
+              (remove "lisp_linkage_values"
+                      (mapcar (lambda (x)
+                                (if (listp (car x))
+                                    (caar x)
+                                    (car x)))
+                              sorted-pairs)
+                      :test #'equal))
+
+      ;; Write out the linkage values. Make this weak on ELF targets so that
+      ;; shrinkwrapping can supersede it.
+      #+elf
+      (format output "uintptr_t __attribute__((weak)) lisp_linkage_values[] = {~%")
+      #-elf
+      (format output "uintptr_t lisp_linkage_values[] = {~%")
+
+      (format output "  ~D,~%" (length sorted-pairs))
+      (dolist (pair sorted-pairs)
+        (when (listp (car pair))
+          ;; This is data, put -1 in to indicate that.
+          (format output "  (uintptr_t)-1,~%"))
+        (format output "  (uintptr_t)&~A,~%" (if (listp (car pair))
+                                                 (caar pair)
+                                                 (car pair))))
+      (format output "};~%"))))
+
 
 ;;;; general machinery for cold-loading FASL files

@@ -3589,10 +3628,14 @@ (defun write-initial-core-file (filename verbose)
 ;;;   CORE-FILE-NAME gets a Lisp core.
 ;;;   C-HEADER-DIR-NAME gets the path in which to place generated headers
 ;;;   MAP-FILE-NAME gets the name of the textual 'cold-sbcl.map' file
+;;;   LINKAGE-TABLE-PREFILL-INFO-C-NAME gets a .c file used to store the
+;;;     data used to link the runtime before entering Lisp.
 (defun sb-cold:genesis (&key object-file-names tls-init
                              defstruct-descriptions
                              core-file-name c-header-dir-name map-file-name
-                             symbol-table-file-name (verbose t))
+                             symbol-table-file-name (verbose t)
+                             linkage-table-prefill-info-c-name
+                             extra-linkage-table-entries)
   (declare (ignorable symbol-table-file-name))

   (when verbose
@@ -3611,6 +3654,8 @@ (defun sb-cold:genesis (&key object-file-names tls-init
     ;; Prefill some linkage table entries perhaps
     (loop for (name datap) in sb-vm::*linkage-space-predefined-entries*
           do (linkage-table-note-symbol name datap))
+    (loop for (name datap) in extra-linkage-table-entries
+          do (linkage-table-note-symbol name datap))
     #-(or linkage-table crossbuild-test)
     (when core-file-name
       (if symbol-table-file-name
@@ -3786,6 +3831,8 @@ (defun sb-cold:genesis (&key object-file-names tls-init
       (resolve-deferred-known-funs)
       (resolve-static-call-fixups)
       (foreign-symbols-to-core)
+      (when linkage-table-prefill-info-c-name
+        (foreign-symbols-to-c linkage-table-prefill-info-c-name))
       #+(or x86 immobile-space)
       (dolist (pair (sort (%hash-table-alist *code-fixup-notes*) #'< :key #'car))
         (write-wordindexed (make-random-descriptor (car pair))
diff --git a/src/runtime/GNUmakefile b/src/runtime/GNUmakefile
index d9ad52f82..993566cac 100644
--- a/src/runtime/GNUmakefile
+++ b/src/runtime/GNUmakefile
@@ -73,8 +73,8 @@ endif

 COMMON_SRC = alloc.c backtrace.c breakpoint.c coalesce.c coreparse.c    \
 	dynbind.c funcall.c gc-common.c globals.c hopscotch.c           \
-	interr.c interrupt.c largefile.c main.c                         \
-	monitor.c murmur_hash.c os-common.c parse.c print.c             \
+	interr.c interrupt.c largefile.c linkage-table-prefill-info.c   \
+	main.c monitor.c murmur_hash.c os-common.c parse.c print.c      \
 	purify.c pthread-futex.c regnames.c runtime.c			\
 	safepoint.c save.c sc-offset.c search.c thread.c time.c         \
 	validate.c var-io.c vars.c wrap.c
@@ -118,6 +118,9 @@ unit-tests: gc-unit-tests.o libsbcl.a
 sbcl.o: $(OBJS)
 	$(LD) $(__LDFLAGS__) -r -o $@ $^

+linkage-table-prefill-info.o: linkage-table-prefill-info.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -Wno-builtin-declaration-mismatch  -c -o $@ $<
+
 libsbcl.a: $(OBJS)
 	rm -f $@ ; ar rcs $@ $^

diff --git a/src/runtime/coreparse.c b/src/runtime/coreparse.c
index 8371ae416..6fd2c0ad7 100644
--- a/src/runtime/coreparse.c
+++ b/src/runtime/coreparse.c
@@ -93,7 +93,7 @@ open_binary(char *filename, int mode)
 int lisp_code_in_elf() { return 0; }
 #else
 extern __attribute__((weak)) lispobj
- lisp_code_start, lisp_jit_code, lisp_code_end, lisp_linkage_values;
+ lisp_code_start, lisp_jit_code, lisp_code_end;
 int lisp_code_in_elf() { return &lisp_code_start != 0; }
 #endif

@@ -743,30 +743,6 @@ process_directory(int count, struct ndir_entry *entry,
                (uword_t)&lisp_code_start, (uword_t)&lisp_code_end,
                varyobj_free_pointer);
 #endif
-        // Prefill the Lisp linkage table so that shrinkwrapped executables which \
                link in
-        // all their C library dependencies can avoid linking with -ldl.
-        // All data references are potentially needed because aliencomp doesn't emit
-        // SAP-REF-n in a way that admits elision of the linkage entry. e.g.
-        //     MOV RAX, [#x20200AA0] ; some_c_symbol
-        //     MOV RAX, [RAX]
-        // might be rendered as
-        //     MOV RAX, some_c_symbol(%rip)
-        // but that's more of a change to the asm instructions than I'm comfortable \
                making;
-        // whereas "CALL linkage_entry_for_f" -> "CALL f" is quite straightforward.
-        // (Rarely would a jmp indirection be used; maybe for newly compiled code?)
-        lispobj* ptr = &lisp_linkage_values;
-        gc_assert(ptr);
-        int entry_index = 0;
-        int count;
-        extern int lisp_linkage_table_n_prelinked;
-        count = lisp_linkage_table_n_prelinked = *ptr++;
-        for ( ; count-- ; entry_index++ ) {
-            boolean datap = *ptr == (lispobj)-1; // -1 can't be a function address
-            if (datap)
-                ++ptr;
-            arch_write_linkage_table_entry(entry_index, (void*)*ptr++, datap);
-        }
-
         // unprotect the pages
         os_protect((void*)VARYOBJ_SPACE_START, varyobj_space_size, OS_VM_PROT_ALL);
     } else
diff --git a/src/runtime/os-common.c b/src/runtime/os-common.c
index 4926e3e2d..10604cf04 100644
--- a/src/runtime/os-common.c
+++ b/src/runtime/os-common.c
@@ -8,7 +8,6 @@
  * provided with absolutely no warranty. See the COPYING and CREDITS
  * files for more information.
  */
-# define _GNU_SOURCE /* needed for RTLD_DEFAULT from dlfcn.h */
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
@@ -28,9 +27,6 @@
 #include "arch.h"
 #include "interr.h"
 #include "immobile-space.h"
-#if defined(LISP_FEATURE_OS_PROVIDES_DLOPEN) && !defined(LISP_FEATURE_WIN32)
-# include <dlfcn.h>
-#endif

 /*
  * historically, this used sysconf to select the runtime page size
@@ -147,59 +143,53 @@ os_sem_destroy(os_sem_t *sem)
  *
  * Before any code in lisp image can be called, we have to resolve all
  * references to runtime foreign symbols that used to be static, adding linkage
- * table entry for each element of REQUIRED_FOREIGN_SYMBOLS.
+ * table entry for each element of lisp_linkage_values.
  */

-#if defined(LISP_FEATURE_LINKAGE_TABLE) && !defined(LISP_FEATURE_WIN32)
-void *
-os_dlsym_default(char *name)
-{
-    void *frob = dlsym(RTLD_DEFAULT, name);
-    return frob;
-}
+int lisp_linkage_table_n_prelinked;
+
+#if defined(LISP_FEATURE_ELF)
+// Weak only works on ELF targets and we'd like this to be weak on those
+// targets for shrinkwrapping.
+extern __attribute__((weak)) lispobj lisp_linkage_values;
+#else
+extern lispobj lisp_linkage_values;
 #endif

-int lisp_linkage_table_n_prelinked;
 void os_link_runtime()
 {
-    // There is a potentially better technique we could use which would simplify
-    // this function, rendering REQUIRED_FOREIGN_SYMBOLS unnecessary, namely:
-    // all we need are two prefilled entries: one for dlsym() itself, and one
-    // for the allocation region overflow handler ("alloc" or "alloc_tramp").
-    // Lisp can fill in the linkage table as the very first action on startup.
+
+    // There is a potentially better technique we could use which would
+    // simplify this function on platforms with dlopen/dlsym, namely: all we
+    // need are two prefilled entries: one for dlsym() itself, and one for the
+    // allocation region overflow handler ("alloc" or "alloc_tramp").  Lisp can
+    // fill in the linkage table as the very first action on startup.
 #ifdef LISP_FEATURE_LINKAGE_TABLE
     int entry_index = 0;
-    lispobj symbol_name;
-    char *namechars;
-    boolean datap;
-    void* result;
-    int j;
-
-    if (lisp_linkage_table_n_prelinked)
-        return; // Linkage was already performed by coreparse
-
-    struct vector* symbols = VECTOR(SymbolValue(REQUIRED_FOREIGN_SYMBOLS,0));
-    lisp_linkage_table_n_prelinked = fixnum_value(symbols->length);
-    for (j = 0 ; j < lisp_linkage_table_n_prelinked ; ++j)
-    {
-        lispobj item = symbols->data[j];
-        datap = listp(item);
-        symbol_name = datap ? CONS(item)->car : item;
-        namechars = (void*)(intptr_t)(VECTOR(symbol_name)->data);
-        result = os_dlsym_default(namechars);

+    // Prefill the Lisp linkage table using references stored in
+    // lisp_linkage_values. This array has an interesting format. The first
+    // entry is interpreted as how many references to symbols are found in the
+    // array. Each subsequent entry is either a reference or -1 (an invalid
+    // funciton pointer). A -1 indicates that the following reference is a
+    // reference to data instead of a function.
+    lispobj *ptr = &lisp_linkage_values;
+
+    if (&lisp_linkage_values) {
+      int count;
+      count = lisp_linkage_table_n_prelinked = *ptr++;
+      for ( ; count-- ; entry_index++ ) {
         if (entry_index == 0) {
 #ifdef LISP_FEATURE_WIN32
-            os_validate_recommit(LINKAGE_TABLE_SPACE_START, os_vm_page_size);
+          os_validate_recommit(LINKAGE_TABLE_SPACE_START, os_vm_page_size);
 #endif
         }
-        if (result) {
-            arch_write_linkage_table_entry(entry_index, result, datap);
-        } else { // startup might or might not work. ymmv
-            fprintf(stderr, "Missing required foreign symbol '%s'\n", namechars);
-        }
-
-        ++entry_index;
+        boolean datap = *ptr == (lispobj)-1; // -1 can't be a function address
+        if (datap)
+          ++ptr;
+        arch_write_linkage_table_entry(entry_index, (void*)*ptr++, datap);
+      }
+      return;
     }
 #endif /* LISP_FEATURE_LINKAGE_TABLE */
 }
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index c24dd1697..4f865cfd2 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -497,8 +497,6 @@ extern char *copied_string (char *string);
 # define GENCGC_IS_PRECISE 0
 #endif

-void *os_dlsym_default(char *name);
-
 struct lisp_startup_options {
     boolean noinform;
 };
diff --git a/src/runtime/win32-os.c b/src/runtime/win32-os.c
index 964386baa..ba27724d3 100644
--- a/src/runtime/win32-os.c
+++ b/src/runtime/win32-os.c
@@ -309,302 +309,6 @@ void unmap_gc_page()

 #endif

-#if defined(LISP_FEATURE_LINKAGE_TABLE)
-/* This feature has already saved me more development time than it
- * took to implement.  In its current state, ``dynamic RT<->core
- * linking'' is a protocol of initialization of C runtime and Lisp
- * core, populating SBCL linkage table with entries for runtime
- * "foreign" symbols that were referenced in cross-compiled code.
- *
- * How it works: a sketch
- *
- * Last Genesis (resulting in cold-sbcl.core) binds foreign fixups in
- * x-compiled lisp-objs to sequential addresses from the beginning of
- * linkage-table space; that's how it ``resolves'' foreign references.
- * Obviously, this process doesn't require pre-built runtime presence.
- *
- * When the runtime loads the core (cold-sbcl.core initially,
- * sbcl.core later), runtime should do its part of the protocol by (1)
- * traversing a list of ``runtime symbols'' prepared by Genesis and
- * dumped as a static symbol value, (2) resolving each name from this
- * list to an address (stubbing unresolved ones with
- * undefined_alien_address or undefined_alien_function), (3) adding an
- * entry for each symbol somewhere near the beginning of linkage table
- * space (location is provided by the core).
- *
- * The implementation of the part described in the last paragraph
- * follows. C side is currently more ``hackish'' and less clear than
- * the Lisp code; OTOH, related Lisp changes are scattered, and some
- * of them play part in complex interrelations -- beautiful but taking
- * much time to understand --- but my subset of PE-i386 parser below
- * is in one place (here) and doesn't have _any_ non-trivial coupling
- * with the rest of the Runtime.
- *
- * What do we gain with this feature, after all?
- *
- * One things that I have to do rather frequently: recompile and
- * replace runtime without rebuilding the core. Doubtlessly, slam.sh
- * was a great time-saver here, but relinking ``cold'' core and bake a
- * ``warm'' one takes, as it seems, more than 10x times of bare
- * SBCL.EXE build time -- even if everything is recompiled, which is
- * now unnecessary. Today, if I have a new idea for the runtime,
- * getting from C-x C-s M-x ``compile'' to fully loaded SBCL
- * installation takes 5-15 seconds.
- *
- * Another thing (that I'm not currently using, but obviously
- * possible) is delivering software patches to remote system on
- * customer site. As you are doing minor additions or corrections in
- * Lisp code, it doesn't take much effort to prepare a tiny ``FASL
- * bundle'' that rolls up your patch, redumps and -- presto -- 100MiB
- * program is fixed by sending and loading a 50KiB thingie.
- *
- * However, until LISP_FEATURE_LINKAGE_TABLE, if your bug were fixed
- * by modifying two lines of _C_ sources, a customer described above
- * had to be ready to receive and reinstall a new 100MiB
- * executable. With the aid of code below, deploying such a fix
- * requires only sending ~300KiB (when stripped) of SBCL.EXE.
- *
- * But there is more to it: as the common linkage-table is used for
- * DLLs and core, its entries may be overridden almost without a look
- * into SBCL internals. Therefore, ``patching'' C runtime _without_
- * restarting target systems is also possible in many situations
- * (it's not as trivial as loading FASLs into a running daemon, but
- * easy enough to be a viable alternative if any downtime is highly
- * undesirable).
- *
- * During my (rather limited) commercial Lisp development experience
- * I've already been through a couple of situations where such
- * ``deployment'' issues were important; from my _total_ programming
- * experience I know -- _sometimes_ they are a two orders of magnitude
- * more important than those I observed.
- *
- * The possibility of entire runtime ``hot-swapping'' in running
- * process is not purely theoretical, as it could seem. There are 2-3
- * problems whose solution is not obvious (call stack patching, for
- * instance), but it's literally _nothing_ if compared with
- * e.g. LISP_FEATURE_SB_AUTO_FPU_SWITCH.  By the way, one of the
- * problems with ``hot-swapping'', that could become a major one in
- * many other environments, is nonexistent in SBCL: we already have a
- * ``global quiesce point'' that is generally required for this kind
- * of worldwide revolution -- around collect_garbage.
- *
- * What's almost unnoticeable from the C side (where you are now, dear
- * reader): using the same style for all linking is beautiful. I tried
- * to leave old-style linking code in place for the sake of
- * _non-linkage-table_ platforms (they probably don't have -ldl or its
- * equivalent, like LL/GPA, at all) -- but i did it usually by moving
- * the entire `old style' code under #-linkage-table and
- * refactoring the `new style' branch, instead of cutting the tail
- * piecemeal and increasing #+-ifdeffery amount & the world enthropy.
- *
- * If we look at the majority of the ``new style'' code units, it's a
- * common thing to observe how #+-ifdeffery _vanishes_ instead of
- * multiplying: #-sb-xc, #+sb-xc-host and #-sb-xc-host end up
- * needing the same code. Runtime checks of static v. dynamic symbol
- * disappear even faster. STDCALL mangling and leading underscores go
- * out of scope (and GCed, hopefully) instead of surfacing here and
- * there as a ``special case for core static symbols''. What I like
- * the most about CL development in general is a frequency of solving
- * problems and fixing bugs by simplifying code and dropping special
- * cases.
- *
- * Last important thing about the following code: besides resolving
- * symbols provided by the core itself, it detects runtime's own
- * build-time prerequisite DLLs. Any symbol that is unresolved against
- * the core is looked up in those DLLs (normally kernel32, msvcrt,
- * ws2_32... I could forget something). This action (1) resembles
- * implementation of foreign symbol lookup in SBCL itself, (2)
- * emulates shared library d.l. facilities of OSes that use flat
- * dynamic symbol namespace (or default to it). Anyone concerned with
- * portability problems of this PE-i386 stuff below will be glad to
- * hear that it could be ported to most modern Unices _by deletion_:
- * raw dlsym() with null handle usually does the same thing that i'm
- * trying to squeeze out of MS Windows by the brute force.
- *
- * My reason for _desiring_ flat symbol namespace, populated from
- * link-time dependencies, is avoiding any kind of ``requested-by-Lisp
- * symbol lists to be linked statically'', providing core v. runtime
- * independence in both directions. Minimizing future maintenance
- * effort is very important; I had gone for it consistently, starting
- * by turning "CloseHandle@4" into a simple "CloseHandle", continuing
- * by adding intermediate Genesis resulting in autogenerated symbol
- * list (farewell, void scratch(); good riddance), going to take
- * another great step for core/runtime independence... and _without_
- * flat namespace emulation, the ghosts and spirits exiled at the
- * first steps would come and take revenge: well, here are the symbols
- * that are really in msvcrt.dll.. hmm, let's link statically against
- * them, so the entry is pulled from the import library.. and those
- * entry has mangled names that we have to map.. ENOUGH, I though
- * here: fed up with stuff like that.
- *
- * Now here we are, without import libraries, without mangled symbols,
- * and without nm-generated symbol tables. Every symbol exported by
- * the runtime is added to SBCL.EXE export directory; every symbol
- * requested by the core is looked up by GetProcAddress for SBCL.EXE,
- * falling back to GetProcAddress for MSVCRT.dll, etc etc.. All ties
- * between SBCL's foreign symbols with object file symbol tables,
- * import libraries and other pre-linking symbol-resolving entities
- * _having no representation in SBCL.EXE_ were teared.
- *
- * This simplistic approach proved to work well; there is only one
- * problem introduced by it, and rather minor: in real MSVCRT.dll,
- * what's used to be available as open() is now called _open();
- * similar thing happened to many other `lowio' functions, though not
- * every one, so it's not a kind of name mangling but rather someone's
- * evil creative mind in action.
- *
- * When we look up any of those poor `uglified' functions in CRT
- * reference on MSDN, we can see a notice resembling this one:
- *
- * `unixishname()' is obsolete and provided for backward
- * compatibility; new standard-compliant function, `_unixishname()',
- * should be used instead.  Sentences of that kind were there for
- * several years, probably even for a decade or more (a propos,
- * MSVCRT.dll, as the name to link against, predates year 2000, so
- * it's actually possible). Reasoning behing it (what MS people had in
- * mind) always seemed strange to me: if everyone uses open() and that
- * `everyone' is important to you, why rename the function?  If no one
- * uses open(), why provide or retain _open() at all? <kidding>After
- * all, names like _open() are entirely non-informative and just plain
- * ugly; compare that with CreateFileW() or InitCommonControlsEx(),
- * the real examples of beauty and clarity.</kidding>
- *
- * Anyway, if the /standard/ name on Windows is _open() (I start to
- * recall, vaguely, that it's because of _underscore names being
- * `reserved to system' and all other ones `available for user', per
- * ANSI/ISO C89) -- well, if the /standard/ name is _open, SBCL should
- * use it when it uses MSVCRT and not some ``backward-compatible''
- * stuff. Deciding this way, I added a hack to SBCL's syscall macros,
- * so "[_]open" as a syscall name is interpreted as a request to link
- * agains "_open" on win32 and "open" on every other system.
- *
- * Of course, this name-parsing trick lacks conceptual clarity; we're
- * going to get rid of it eventually. */
-
-u32 os_get_build_time_shared_libraries(u32 excl_maximum,
-                                       void* opt_root,
-                                       void** opt_store_handles,
-                                       const char *opt_store_names[])
-{
-    void* base = opt_root ? opt_root : (void*)runtime_module_handle;
-    /* base defaults to 0x400000 with GCC/mingw32. If you dereference
-     * that location, you'll see 'MZ' bytes */
-    void* base_magic_location =
-        base + ((IMAGE_DOS_HEADER*)base)->e_lfanew;
-
-    /* dos header provided the offset from `base' to
-     * IMAGE_FILE_HEADER where PE-i386 really starts */
-
-    void* check_duplicates[excl_maximum];
-
-    if ((*(u32*)base_magic_location)!=0x4550) {
-        /* We don't need this DLL thingie _that_ much. If the world
-         * has changed to a degree where PE magic isn't found, let's
-         * silently return `no libraries detected'. */
-        return 0;
-    } else {
-        /* We traverse PE-i386 structures of SBCL.EXE in memory (not
-         * in the file). File and memory layout _surely_ differ in
-         * some places and _may_ differ in some other places, but
-         * fortunately, those places are irrelevant to the task at
-         * hand. */
-
-        IMAGE_FILE_HEADER* image_file_header = (base_magic_location + 4);
-        IMAGE_OPTIONAL_HEADER* image_optional_header =
-            (void*)(image_file_header + 1);
-        IMAGE_DATA_DIRECTORY* image_import_direntry =
-            &image_optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
-        IMAGE_IMPORT_DESCRIPTOR* image_import_descriptor =
-            base + image_import_direntry->VirtualAddress;
-        u32 nlibrary, j;
-
-        for (nlibrary=0u; nlibrary < excl_maximum
-                          && image_import_descriptor->FirstThunk;
-             ++image_import_descriptor)
-        {
-            HMODULE hmodule;
-            odxprint(runtime_link, "Now should know DLL: %s",
-                     (char*)(base + image_import_descriptor->Name));
-            /* Code using image thunk data to get its handle was here, with a
-             * number of platform-specific tricks (like using VirtualQuery for
-             * old OSes lacking GetModuleHandleEx).
-             *
-             * It's now replaced with requesting handle by name, which is
-             * theoretically unreliable (with SxS, multiple modules with same
-             * name are quite possible), but good enough to find the
-             * link-time dependencies of our executable or DLL. */
-
-            hmodule = (HMODULE)
-                GetModuleHandle(base + image_import_descriptor->Name);
-
-            if (hmodule)
-            {
-                /* We may encouncer some module more than once while
-                   traversing import descriptors (it's usually a
-                   result of non-trivial linking process, like doing
-                   ld -r on some groups of files before linking
-                   everything together.
-
-                   Anyway: using a module handle more than once will
-                   do no harm, but it slows down the startup (even
-                   now, our startup time is not a pleasant topic to
-                   discuss when it comes to :linkage-table; there is
-                   an obvious direction to go for speed, though --
-                   instead of resolving symbols one-by-one, locate PE
-                   export directories -- they are sorted by symbol
-                   name -- and merge them, at one pass, with sorted
-                   list of required symbols (the best time to sort the
-                   latter list is during Genesis -- that's why I don't
-                   proceed with memory copying, qsort() and merge
-                   right here)). */
-
-                for (j=0; j<nlibrary; ++j)
-                {
-                    if(check_duplicates[j] == hmodule)
-                        break;
-                }
-                if (j<nlibrary) continue; /* duplicate => skip it in
-                                           * outer loop */
-
-                check_duplicates[nlibrary] = hmodule;
-                if (opt_store_handles) {
-                    opt_store_handles[nlibrary] = hmodule;
-                }
-                if (opt_store_names) {
-                    opt_store_names[nlibrary] = (const char *)
-                        (base + image_import_descriptor->Name);
-                }
-                odxprint(runtime_link, "DLL detection: %u, base %p: %s",
-                         nlibrary, hmodule,
-                         (char*)(base + image_import_descriptor->Name));
-                ++ nlibrary;
-            }
-        }
-        return nlibrary;
-    }
-}
-
-static u32 buildTimeImageCount = 0;
-static void* buildTimeImages[16];
-
-/* Resolve symbols against the executable and its build-time dependencies */
-void* os_dlsym_default(char* name)
-{
-    unsigned int i;
-    void* result = 0;
-    if (buildTimeImageCount == 0) {
-        buildTimeImageCount =
-            1 + os_get_build_time_shared_libraries(15u,
-            NULL, 1+(void**)buildTimeImages, NULL);
-    }
-    for (i = 0; i<buildTimeImageCount && (!result); ++i) {
-        result = GetProcAddress(buildTimeImages[i], name);
-    }
-    return result;
-}
-
-#endif /* LINKAGE_TABLE */
-
 #if defined(LISP_FEATURE_SB_THREAD)
 /* We want to get a slot in TIB that (1) is available at constant
    offset, (2) is our private property, so libraries wouldn't legally
--
2.27.0


_______________________________________________
Sbcl-devel mailing list
Sbcl-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sbcl-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic