    --
    --  (c) The University of Glasgow 2002-2006
    --

    -- ---------------------------------------------------------------------------
    --      The dynamic linker for object code (.o .so .dll files)
    -- ---------------------------------------------------------------------------

    -- | Primarily, this module consists of an interface to the C-land
    -- dynamic linker.
    module ObjLink (
       initObjLinker,        -- :: IO ()
       loadDLL,              -- :: String -> IO (Maybe String)
       loadArchive,          -- :: String -> IO ()
       loadObj,              -- :: String -> IO ()
       unloadObj,            -- :: String -> IO ()
       insertSymbol,         -- :: String -> String -> Ptr a -> IO ()
       lookupSymbol,         -- :: String -> IO (Maybe (Ptr a))
       resolveObjs           -- :: IO SuccessFlag
      )  where

    import Panic
    import BasicTypes       ( SuccessFlag, successIf )
    import Config           ( cLeadingUnderscore )
    import Util

    import Control.Monad    ( when )
    import Foreign.C
    import Foreign          ( nullPtr )
    import GHC.Exts         ( Ptr(..) )
    import System.Posix.Internals ( CFilePath, withFilePath )
    import System.FilePath  ( dropExtension )

    -- ---------------------------------------------------------------------------
    -- RTS Linker Interface
    -- ---------------------------------------------------------------------------

    insertSymbol :: String -> String -> Ptr a -> IO ()
    insertSymbol obj_name key symbol
        = let str = prefixUnderscore key
          in withFilePath obj_name $ \c_obj_name ->
             withCAString str $ \c_str ->
              c_insertSymbol c_obj_name c_str symbol

    lookupSymbol :: String -> IO (Maybe (Ptr a))
    lookupSymbol str_in = do
       let str = prefixUnderscore str_in
       withCAString str $ \c_str -> do
         addr <- c_lookupSymbol c_str
         if addr == nullPtr
            then return Nothing
            else return (Just addr)

    prefixUnderscore :: String -> String
    prefixUnderscore
     | cLeadingUnderscore == "YES" = ('_':)
     | otherwise                   = id

    -- | loadDLL loads a dynamic library using the OS's native linker
    -- (i.e. dlopen() on Unix, LoadLibrary() on Windows).  It takes either
    -- an absolute pathname to the file, or a relative filename
    -- (e.g. "libfoo.so" or "foo.dll").  In the latter case, loadDLL
    -- searches the standard locations for the appropriate library.
    --
    loadDLL :: String -> IO (Maybe String)
    -- Nothing      => success
    -- Just err_msg => failure
    loadDLL str0 = do
      let
         -- On Windows, addDLL takes a filename without an extension, because
         -- it tries adding both .dll and .drv.  To keep things uniform in the
         -- layers above, loadDLL always takes a filename with an extension, and
         -- we drop it here on Windows only.
         str | isWindowsHost = dropExtension str0
             | otherwise     = str0
      --
      maybe_errmsg <- withFilePath str $ \dll -> c_addDLL dll
      if maybe_errmsg == nullPtr
            then return Nothing
            else do str <- peekCString maybe_errmsg
                    return (Just str)

    loadArchive :: String -> IO ()
    loadArchive str = do
       withFilePath str $ \c_str -> do
         r <- c_loadArchive c_str
         when (r == 0) (panic ("loadArchive " ++ show str ++ ": failed"))

    loadObj :: String -> IO ()
    loadObj str = do
       withFilePath str $ \c_str -> do
         r <- c_loadObj c_str
         when (r == 0) (panic ("loadObj " ++ show str ++ ": failed"))

    unloadObj :: String -> IO ()
    unloadObj str =
       withFilePath str $ \c_str -> do
         r <- c_unloadObj c_str
         when (r == 0) (panic ("unloadObj " ++ show str ++ ": failed"))

    resolveObjs :: IO SuccessFlag
    resolveObjs = do
       r <- c_resolveObjs
       return (successIf (r /= 0))

    -- ---------------------------------------------------------------------------
    -- Foreign declarations to RTS entry points which does the real work;
    -- ---------------------------------------------------------------------------

    foreign import ccall unsafe "addDLL"       c_addDLL :: CFilePath -> IO CString
    foreign import ccall unsafe "initLinker"   initObjLinker :: IO ()
    foreign import ccall unsafe "insertSymbol" c_insertSymbol :: CFilePath -> CString -> Ptr a -> IO ()
    foreign import ccall unsafe "lookupSymbol" c_lookupSymbol :: CString -> IO (Ptr a)
    foreign import ccall unsafe "loadArchive"  c_loadArchive :: CFilePath -> IO Int
    foreign import ccall unsafe "loadObj"      c_loadObj :: CFilePath -> IO Int
    foreign import ccall unsafe "unloadObj"    c_unloadObj :: CFilePath -> IO Int
    foreign import ccall unsafe "resolveObjs"  c_resolveObjs :: IO Int