bug-libtool
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

lt_dlsym() Doesn't Allow for a Symbol with Address of 0.


From: Ralph Corderoy
Subject: lt_dlsym() Doesn't Allow for a Symbol with Address of 0.
Date: Sun, 22 Jul 2007 11:01:54 +0100

Hi,

I investigated a problem on the llvmdev mailing list where someone was
trying to find the value of a symbol that has an address of 0.

    $ nm /System/Library/Frameworks/Foundation.framework/Foundation |
    > grep .objc_class_name_NSAutoreleasePool
    00000000 A .objc_class_name_NSAutoreleasePool
    $

LLVM uses lt_dlsym().  This led me to look at libtool-1.5.24's
sys_dl_sym():

    static lt_ptr
    sys_dl_sym (loader_data, module, symbol)
         lt_user_data loader_data;
         lt_module module;
         const char *symbol;
    {
      lt_ptr address = dlsym (module, symbol);

      if (!address)
        {
          LT_DLMUTEX_SETERROR (DLERROR (SYMBOL_NOT_FOUND));
        }

      return address;
    }

This is flawed.  dlsym()'s return value doesn't indicate whether an
error occurred or whether the symbol was not found.  dlsym(3) here says

    Since the value of the symbol could actually be NULL (so that a NULL
    return from dlsym() need not indicate an error), the correct way to
    test for an error is to call dlerror() to clear any old error
    conditions, then call dlsym(), and then call dlerror() again, saving
    its return value into a variable, and check whether this saved value
    is not NULL.

So it should be

    {
        lt_ptr address;

        dlerror();    /* Discard any earlier errors. */
        address = dlsym(module, symbol);
        if (dlerror()) {
            /* We assume symbol not found. */
            LT_DLMUTEX_SETERROR(DLERROR(SYMBOL_NOT_FOUND));
        }

        return address;
    }

Even then, lt_dlsym()'s documentation doesn't mention the symbol's
address may be 0 in the same way as dlsym(3);  I think this could be
improved.

I looked at some of the other (*find_sym)() implementations...

    static lt_ptr
    sys_shl_sym (loader_data, module, symbol)
         lt_user_data loader_data;
         lt_module module;
         const char *symbol;
    {
      lt_ptr address = 0;

      /* sys_shl_open should never return a NULL module handle */
      if (module == (lt_module) 0)
      {
        LT_DLMUTEX_SETERROR (LT_DLSTRERROR (INVALID_HANDLE));
      }
      else if (!shl_findsym((shl_t*) &module, symbol, TYPE_UNDEFINED, &address))
        {
          if (!address)
            {
              LT_DLMUTEX_SETERROR (LT_DLSTRERROR (SYMBOL_NOT_FOUND));
            }
        }

      return address;
    }

http://docs.hp.com/en/B2355-90654/ch05s04.html says sys_shl_sym()
returns 0 on success.  So sys_shl_sym() is then insisting that even if
symbol has been found, if it's 0 then lt_dlsym() will wrongly say it was
not found.  Instead, if shl_findsym() doesn't return 0 then errno should
be checked to determine the error.

    static lt_ptr
    sys_wll_sym (loader_data, module, symbol)
         lt_user_data loader_data;
         lt_module module;
         const char *symbol;
    {
      lt_ptr      address  = GetProcAddress (module, symbol);

      if (!address)
        {
          LT_DLMUTEX_SETERROR (LT_DLSTRERROR (SYMBOL_NOT_FOUND));
        }

      return address;
    }

GetProcAddress() overloads the return value.
http://msdn2.microsoft.com/en-us/library/ms683212.aspx says
GetLastError() should be called to "get extended error information".

    static lt_ptr
    sys_bedl_sym (loader_data, module, symbol)
         lt_user_data loader_data;
         lt_module module;
         const char *symbol;
    {
      lt_ptr address = 0;
      image_id image = (image_id) module;

      if (get_image_symbol (image, symbol, B_SYMBOL_TYPE_ANY, address) != B_OK)
        {
          LT_DLMUTEX_SETERROR (LT_DLSTRERROR (SYMBOL_NOT_FOUND));
          address = 0;
        }

      return address;
    }

Firstly, I think &address should be passed to get_image_symbol() since
it's used to return the result.  This one does work when address == 0.
However, any non-OK return value from get_image_symbol() is turned into
symbol not found.
http://www.beunited.org/bebook/The%20Kernel%20Kit/Images.html was one
bit of documentation I found.

    static lt_ptr
    sys_dld_sym (loader_data, module, symbol)
         lt_user_data loader_data;
         lt_module module;
         const char *symbol;
    {
      lt_ptr address = dld_get_func (symbol);

      if (!address)
        {
          LT_DLMUTEX_SETERROR (LT_DLSTRERROR (SYMBOL_NOT_FOUND));
        }

      return address;
    }

http://sunsite.ualberta.ca/Documentation/Gnu/dld-3.3/html_node/dld_6.html
says dld_get_func() returns 0 when the symbol is not found.  GNU dld is
unmaintained now and old hat, but perhaps dld_function_executable_p()
might be useful to really determine if the symbol exists.  I found
http://ftp.gnu.org/old-gnu/dld/dld-3.3.tar.gz as the latest source.

    static lt_ptr
    sys_dyld_sym (loader_data, module, symbol)
         lt_user_data loader_data;
         lt_module module;
         const char *symbol;
    {
        lt_ptr address = 0;
        NSSymbol *nssym = 0;
        void *unused;
        const struct mach_header *mh=NULL;
        char saveError[256] = "Symbol not found";
        if (module == (lt_module)-1)
        {
            _dyld_lookup_and_bind(symbol,(unsigned long*)&address,&unused);
            return address;
        }
    #ifdef __BIG_ENDIAN__
        if (((struct mach_header *)module)->magic == MH_MAGIC)
    #else
        if (((struct mach_header *)module)->magic == MH_CIGAM)
    #endif
        {
            if (ltdl_NSIsSymbolNameDefinedInImage && ltdl_NSLookupSymbolInImage)
            {
                mh=module;
                if (ltdl_NSIsSymbolNameDefinedInImage((struct 
mach_header*)module,symbol))
                {
                    nssym = ltdl_NSLookupSymbolInImage((struct 
mach_header*)module,
                                                symbol,
                                                
NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW
                                                | 
NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
                                                );
                }
            }
        }
        else {
            nssym = NSLookupSymbolInModule(module, symbol);
        }
        if (!nssym)
        {
            strncpy(saveError, 
lt_int_dyld_error(LT_DLSTRERROR(SYMBOL_NOT_FOUND)), 255);
            saveError[255] = 0;
            if (!mh) mh=lt_int_dyld_get_mach_header_from_nsmodule(module);
            nssym = lt_int_dyld_NSlookupSymbolInLinkedLibs(symbol,mh);
        }
        if (!nssym)
        {
            LT_DLMUTEX_SETERROR (saveError);
            return NULL;
        }
        return NSAddressOfSymbol(nssym);
    }

I think _dyld_lookup_and_bind()'s module parameter being 0 on return is
an indication the symbol was not found, but I don't know Mac OS X.
Unfortunately, it's &unused above.

(BTW, I find the style of one return at the end of the function makes
some of the logic more tortuous to read than necessary, and thereby
easier to make mistakes.)

In general, it would be nice if libtool documented that the address
could validly be 0 on return and say how to code to cope with that, e.g.
doing the double-shuffle with lt_dlerror() around the call to
lt_dlsym().  However, given many people don't do this, e.g. LLVM, how
about a second interface that returns 0 on symbol found, or a variety
of error ids, with the address being a parameter.  If you have to write

    lt_dlfindsym(h, sym, &addr);

then it's a bit more obvious you're not checking the return value.

(Apologies for the length of the email.)

Cheers,


Ralph.





reply via email to

[Prev in Thread] Current Thread [Next in Thread]