common/util/Util/Dll.hs (102 lines of code) (raw):

-- Copyright (c) Facebook, Inc. and its affiliates. module Util.Dll ( SOExport , DllException(..) -- "static" shared object API , rotateDLL , loadDll , unloadObj -- "real" shared object API , loadNativeDll , unloadNativeObj , DLHandle , setHighMemDynamic ) where import Control.Exception hiding (handle) import Control.Monad import Data.Text (Text) import qualified Data.Text as Text import Foreign import GHCi.ObjLink import Foreign.C.String import System.Posix.Internals (CFilePath, withFilePath) type SOExport a = IO (StablePtr a) data DllException = DllException FilePath Text deriving (Show, Eq) instance Exception DllException -- Loading of "static" shared objects -- | Given a previous dll and a new one: -- * load in the new one -- * pull out the exported data -- * dump out the old dll if it existed rotateDLL :: (Maybe FilePath, FilePath) -> Text -> IO a rotateDLL (old, newDLL) sym = loadDll newDLL sym <* forM_ old unloadObj loadDll :: FilePath -> Text -> IO a loadDll dllPath symbol = do loadObj dllPath resolved <- resolveObjs unless resolved $ throwIO $ DllException dllPath "Unable to resolve objects" c_sym <- lookupSymbol $ Text.unpack symbol h <- case c_sym of Nothing -> throwIO $ DllException dllPath $ "Could not find symbol: " <> symbol Just p_sym -> bracket (mkCallable $ castPtrToFunPtr p_sym) freeStablePtr deRefStablePtr purged <- withFilePath dllPath c_purgeObj when (purged == 0) $ throwIO $ DllException dllPath "Unable to purge object" return h -- Not exported by GHCI yet, so pull it in to dump the symbol table foreign import ccall unsafe "purgeObj" c_purgeObj :: CFilePath -> IO Int foreign import ccall "dynamic" mkCallable :: FunPtr (SOExport a) -> SOExport a -- Loading of "real" shared objects -- NB. if you're using this, you must add a dependency on -- common/hs/dll:hs_dynamic_main. See common/hs/dll/TARGETS. loadNativeDll :: FilePath -> Text -> IO (a, DLHandle) loadNativeDll dllPath symbol = do eHandle <- loadNativeObj dllPath handle <- case eHandle of Left msg -> throwIO $ DllException dllPath (Text.pack msg) Right h -> return h c_sym <- dlsym handle $ Text.unpack symbol h <- case c_sym of Nothing -> throwIO $ DllException dllPath $ "Could not find symbol: " <> symbol Just p_sym -> bracket (mkCallable $ castPtrToFunPtr p_sym) freeStablePtr deRefStablePtr return (h, handle) -- I will have to export these in https://phabricator.haskell.org/D4263 type DLHandle = Ptr () loadNativeObj :: String -> IO (Either String DLHandle) loadNativeObj str = withFilePath str $ \c_str -> alloca $ \perr -> do poke perr nullPtr bracket (c_loadNativeObj c_str perr) (\_ -> free =<< peek perr) $ \r -> do if r /= nullPtr then return $ Right r else do msg <- peekCString =<< peek perr return $ Left ("loadNativeObj " ++ show str ++ ": failed, reason: " ++ msg) unloadNativeObj :: DLHandle -> IO () unloadNativeObj handle = do r <- c_unloadNativeObj handle when (r == 0) $ error "unloadNativeObj" dlsym :: DLHandle -> String -> IO (Maybe (Ptr a)) dlsym handle str = withCAString str $ \c_str -> do addr <- c_dlsym handle c_str if addr == nullPtr then return Nothing else return (Just addr) foreign import ccall unsafe "loadNativeObj" c_loadNativeObj :: CFilePath -> Ptr CString -> IO DLHandle foreign import ccall unsafe "unloadNativeObj" c_unloadNativeObj :: DLHandle -> IO Int foreign import ccall unsafe "dlsym" c_dlsym :: DLHandle -> CString -> IO (Ptr a) foreign import ccall unsafe "setHighMemDynamic" setHighMemDynamic :: IO ()