[Openmcl-devel] shared libs on Darwin

Gary Byers gb at clozure.com
Mon Jul 1 12:11:37 UTC 2002

On Mon, 1 Jul 2002, Michael Klingbeil wrote:

> I'm not yet sure about this, but it seems to me that walking the
> internal _dl_loaded (or its Darwin equivalent) may not be entirely
> necessary. Yes it is nice to have knowledge of other libs that were
> loaded due to external references, but is that really needed as long
> as we know about all of the libraries immediately referenced by the
> *eeps* table?

Suppose (under Linux) the user loads the GDK (Gimp Drawing Kit), and
doing so transitively loads several X11 libraries.  If they reference
an X11 symbol

(external "XTextWidth")

we want to know that that symbol came from libX11.so.  We could either
do what the code does now (tries to keep *shared-libraries* in synch
with the _dl_loaded list, and discover that libX11.so was opened
as soon as we'd opened libgdk.so), or discover that libX11.so was
open when #_dladdr told us that it was the library that contained
"XTextWidth".  In the latter case, we'd add an entry for libX11.so
on *shared-libraries* when we first discovered that it contained
something of interest.

One advantage of the latter scheme is that libraries that aren't
either (a) explicitly opened or (b) found to contain an external
address of interest aren't kept on *shared-libraries*.  (That
might be an advantage if the set of transitively-referenced
libraries varied from platform to platform: that set of libraries
may very much depend on the Linux distribution, libc version,
and other factors.)

When a library's "discovered" by walking the _dl_loaded list, it's
explicitly opened (via %DLOPEN-SHLIB).  I believe that the intent here
was to make it possible to pass the library pointer to #_dlsym (in
FOREIGN-SYMBOL-ENTRY, etc.);  if the library'd only been implicitly
opened, such calls would segfault.  There aren't any such calls
currently, and the explicit open has the unfortunate side-effect
of incrementing the library's reference count.  In the example
above, closing "libgdk.so" would leave "libX11.so.6" open (its
ref count would only be decremented to 1.)

If this were fixed (so that closing the GDK library closed the
X11 library as well), we'd need to notice whatever was transitively
closed and invalidate any eeps which refer to addresses in those
transitively-closed libraries.

It seems to be possible to close libraries that were transitively
opened (in the example case, it's possible to close libX11 without
closing libgdk first.)  That shouldn't be possible (it makes calling
into libgdk pretty dangerous).  I'm not sure what that says about
whether *shared-libraries* should be kept in synch with _dl_loaded
or whether it need only contain things that are explicitly opened
or found to contain "interesting" entrypoints; it may imply that
we need to keep track of how many times a library's been explicitly
#_dlopen()'ed, and refuse to #_dlclose() it more often than that.

> NSModuleForSymbol seems to be the closest thing
> to dladdr, and I'm not sure this is quite the right thing so this
> might need to be rethought as well. Dealing with .dylibs and versus
> modules seems to be another confusion.

NSModules seem to be contained in "libraries".

Given a defined name (c-string), one can get an NSSymbol via:

(#_NSLookupAndBindSymbol name) ; there may be other ways.

Given an NSSymbol, one can find the containing NSModule via:

(#_NSModuleForSymbol sym)

To top it all off, we can get the name of the containing library via:

(#_NSLibraryNameForModule mod)

I have no idea what good the "library name" does us.  Presumably, it's
the name of some dylib or bundle or module or something ...

-------- cut here -------
   cc -o syms syms.c
#include <mach-o/dyld.h>
#include <stdio.h>

main(int argc, char *argv[])
  NSSymbol sym;
  char *name = argv[1];
  void *addr;
  NSModule mod;

  if (NSIsSymbolNameDefined(name)) {
    fprintf(stderr, "win\n");
    sym = NSLookupAndBindSymbol(name);
    addr = NSAddressOfSymbol(sym);
    fprintf(stderr, "Addr = 0x%08x\n", (unsigned) addr);
    mod = NSModuleForSymbol(sym);
    fprintf(stderr, "Module name = %s\n", NSNameOfModule(mod));
    fprintf(stderr, "Library name = %s\n", NSLibraryNameForModule(mod));
  } else {
    fprintf(stderr, "lose\n");
------ cut here -------
If you compile this little program and run it, you'll find (for instance)
that the symbol "_fwrite" (note the leading underscore) is defined in
the module fwrite.o, which is contained in /usr/lib/libSystem.B.dylib.
(Since no other libraries are linked against the program, anything it
finds is presumably in libSystem.)

I -think- that the way to do dladdr in Darwin may be:

   ;; given name (a c-string) and address
   (dotimes (i (#__dyld_image_count))
     (let* ((header (#__dyld_get_image_header i))
            (symbol (#_NSLookUpSymbolInImage name options)))
	(unless (%null-ptr-p symbol)
          (let* ((thisaddr (#_NSAddressOfSymbol symbol)))
            (if (eql thisaddr addr)
              ;; Found it.  Finally.
              (return header))))))

It would make too much sense if that worked ...  If it does work, I
don't know whether it generalizes to things other than dylibs.

> >Note that it's currently possible to open a .dylib via (of all things)
> >CCL::OPEN-DYLIB.  (In cases where I've used this, I've gotten runtime
> >warnings from malloc() on attempts to open a .dylib that's already open.)
> I wonder if it is possible to avoid the runtime warnings by first
> calling NSAddImage with the flag
> then call NSAddImage again to actually load the dylib. Something like
> this (untested):
> (defun open-dylib (name)
>    (with-cstrs ((name name))
>      (let ((res (#_NSAddImage name (logior #$NSADDIMAGE_OPTION_RETURN_ON_ERROR
>                                            #$NSADDIMAGE_OPTION_WITH_SEARCHING
>        (if (%null-ptr-p res)
>          (#_NSAddImage name (logior #$NSADDIMAGE_OPTION_RETURN_ON_ERROR
>                                     #$NSADDIMAGE_OPTION_WITH_SEARCHING))))))

I tried your suggestion and it seems to work; thanks!

The "warning" from malloc() is along the lines of "free() was called on
something that wasn't allocated", indicating that things may be pretty
badly corrupted (or are about to get that way.)

Openmcl-devel mailing list
Openmcl-devel at clozure.com

More information about the Openmcl-devel mailing list