diff options
Diffstat (limited to 'System/Posix')
-rw-r--r-- | System/Posix/DynamicLinker.hsc | 95 | ||||
-rw-r--r-- | System/Posix/DynamicLinker/Module.hsc | 120 | ||||
-rw-r--r-- | System/Posix/DynamicLinker/Prim.hsc | 122 |
3 files changed, 337 insertions, 0 deletions
diff --git a/System/Posix/DynamicLinker.hsc b/System/Posix/DynamicLinker.hsc new file mode 100644 index 0000000..6d13de5 --- /dev/null +++ b/System/Posix/DynamicLinker.hsc @@ -0,0 +1,95 @@ +----------------------------------------------------------------------------- +-- | +-- Module : System.Posix.DynamicLinker +-- Copyright : (c) Volker Stolz <vs@foldr.org> 2003 +-- License : BSD-style (see the file libraries/base/LICENSE) +-- +-- Maintainer : vs@foldr.org +-- Stability : provisional +-- Portability : non-portable (requires POSIX) +-- +-- Dynamic linker support through dlopen() +----------------------------------------------------------------------------- + +module System.Posix.DynamicLinker ( + + module System.Posix.DynamicLinker.Prim, + dlopen, + dlsym, + dlerror, + dlclose, + withDL, withDL_, + undl, + ) + +-- Usage: +-- ****** +-- +-- Let's assume you want to open a local shared library \'foo\' (.\/libfoo.so) +-- offering a function +-- @char \* mogrify (char\*,int)@ +-- and invoke @str = mogrify("test",1)@: +-- +-- +-- type Fun = CString -> Int -> IO CString +-- foreign import dynamic unsafe fun__ :: FunPtr Fun -> Fun +-- +-- withDL "libfoo.so" [RTLD_NOW] \$ \\ mod -> do +-- funptr <- dlsym mod "mogrify" +-- let fun = fun__ funptr +-- withCString "test" \$ \\ str -> do +-- strptr <- fun str 1 +-- strstr <- peekCString strptr +-- ... +-- + +where + +#include "HsUnix.h" + +import System.Posix.DynamicLinker.Prim +import IO ( bracket ) +import Control.Monad ( liftM ) +import Foreign.Ptr ( Ptr, nullPtr, FunPtr, nullFunPtr ) +import Foreign.C.String ( withCString, peekCString ) + +dlopen :: String -> [RTLDFlags] -> IO DL +dlopen path flags = do + withCString path $ \ p -> do + liftM DLHandle $ throwDLErrorIf "dlopen" (== nullPtr) $ c_dlopen p (packRTLDFlags flags) + +dlclose :: DL -> IO () +dlclose (DLHandle h) = throwDLErrorIf_ "dlclose" (== 0) $ c_dlclose h +dlclose h = error $ "dlclose: invalid argument" ++ (show h) + +dlerror :: IO String +dlerror = c_dlerror >>= peekCString + +-- |'dlsym' returns the address binding of the symbol described in @symbol@, +-- as it occurs in the shared object identified by @source@. + +dlsym :: DL -> String -> IO (FunPtr a) +dlsym source symbol = do + withCString symbol $ \ s -> do + throwDLErrorIf "dlsym" (== nullFunPtr) $ c_dlsym (packDL source) s + +withDL :: String -> [RTLDFlags] -> (DL -> IO a) -> IO a +withDL mod flags f = bracket (dlopen mod flags) (dlclose) f + +withDL_ :: String -> [RTLDFlags] -> (DL -> IO a) -> IO () +withDL_ mod flags f = withDL mod flags f >> return () + +-- |'undl' obtains the raw handle. You mustn't do something like +-- @withDL mod flags $ liftM undl >>= \ p -> use p@ + +undl :: DL -> Ptr () +undl = packDL + +throwDLErrorIf :: String -> (a -> Bool) -> IO a -> IO a +throwDLErrorIf s p f = do + r <- f + if (p r) + then dlerror >>= \ err -> ioError (userError ( s ++ ": " ++ err)) + else return r + +throwDLErrorIf_ s p f = throwDLErrorIf s p f >> return () diff --git a/System/Posix/DynamicLinker/Module.hsc b/System/Posix/DynamicLinker/Module.hsc new file mode 100644 index 0000000..351429a --- /dev/null +++ b/System/Posix/DynamicLinker/Module.hsc @@ -0,0 +1,120 @@ +{-# OPTIONS -fffi #-} +----------------------------------------------------------------------------- +-- | +-- Module : System.Posix.DynamicLinker.Module +-- Copyright : (c) Volker Stolz <vs@foldr.org> 2003 +-- License : BSD-style (see the file libraries/base/LICENSE) +-- +-- Maintainer : vs@foldr.org +-- Stability : provisional +-- Portability : non-portable (requires POSIX) +-- +-- DLOpen support, old API +-- Derived from GModule.chs by M.Weber & M.Chakravarty which is part of c2hs +-- I left the API more or less the same, mostly the flags are different. +-- +----------------------------------------------------------------------------- + +module System.Posix.DynamicLinker.Module ( + +-- Usage: +-- ****** +-- +-- Let's assume you want to open a local shared library 'foo' (./libfoo.so) +-- offering a function +-- char * mogrify (char*,int) +-- and invoke str = mogrify("test",1): +-- +-- type Fun = CString -> Int -> IO CString +-- foreign import dynamic unsafe fun__ :: FunPtr Fun -> Fun +-- +-- withModule (Just ".") ("libfoo.so") [RTLD_NOW] $ \ mod -> do +-- funptr <- moduleSymbol mod "mogrify" +-- let fun = fun__ funptr +-- withCString "test" $ \ str -> do +-- strptr <- fun str 1 +-- strstr <- peekCString strptr +-- ... + + + moduleOpen -- :: String -> ModuleFlags -> IO Module + , moduleSymbol -- :: Source -> String -> IO (FunPtr a) + , moduleClose -- :: Module -> IO Bool + , moduleError -- :: IO String + , withModule -- :: Maybe String + -- -> String + -- -> [ModuleFlags ] + -- -> (Module -> IO a) + -- -> IO a + , withModule_ -- :: Maybe String + -- -> String + -- -> [ModuleFlags] + -- -> (Module -> IO a) + -- -> IO () + ) +where + +#include "HsUnix.h" + +import System.Posix.DynamicLinker +import Foreign.Ptr ( Ptr, nullPtr, FunPtr ) +import Foreign.C.String ( withCString ) + +-- abstract handle for dynamically loaded module (EXPORTED) +-- +newtype Module = Module (Ptr ()) + +unModule :: Module -> (Ptr ()) +unModule (Module adr) = adr + +-- Opens a module (EXPORTED) +-- + +moduleOpen :: String -> [RTLDFlags] -> IO Module +moduleOpen mod flags = do + modPtr <- withCString mod $ \ modAddr -> c_dlopen modAddr (packRTLDFlags flags) + if (modPtr == nullPtr) + then moduleError >>= \ err -> ioError (userError ("dlopen: " ++ err)) + else return $ Module modPtr + +-- Gets a symbol pointer from a module (EXPORTED) +-- +moduleSymbol :: Module -> String -> IO (FunPtr a) +moduleSymbol mod sym = dlsym (DLHandle (unModule mod)) sym + +-- Closes a module (EXPORTED) +-- +moduleClose :: Module -> IO () +moduleClose mod = dlclose (DLHandle (unModule mod)) + +-- Gets a string describing the last module error (EXPORTED) +-- +moduleError :: IO String +moduleError = dlerror + + +-- Convenience function, cares for module open- & closing +-- additionally returns status of `moduleClose' (EXPORTED) +-- +withModule :: Maybe String + -> String + -> [RTLDFlags] + -> (Module -> IO a) + -> IO a +withModule dir mod flags p = do + let modPath = case dir of + Nothing -> mod + Just p -> p ++ if ((head (reverse p)) == '/') + then mod + else ('/':mod) + mod <- moduleOpen modPath flags + result <- p mod + moduleClose mod + return result + +withModule_ :: Maybe String + -> String + -> [RTLDFlags] + -> (Module -> IO a) + -> IO () +withModule_ dir mod flags p = withModule dir mod flags p >>= \ _ -> return () diff --git a/System/Posix/DynamicLinker/Prim.hsc b/System/Posix/DynamicLinker/Prim.hsc new file mode 100644 index 0000000..00457f1 --- /dev/null +++ b/System/Posix/DynamicLinker/Prim.hsc @@ -0,0 +1,122 @@ +{-# OPTIONS -fffi #-} +----------------------------------------------------------------------------- +-- | +-- Module : System.Posix.DynamicLinker.Prim +-- Copyright : (c) Volker Stolz <vs@foldr.org> 2003 +-- License : BSD-style (see the file libraries/base/LICENSE) +-- +-- Maintainer : vs@foldr.org +-- Stability : provisional +-- Portability : non-portable (requires POSIX) +-- +-- DLOpen and friend +-- Derived from GModule.chs by M.Weber & M.Chakravarty which is part of c2hs +-- I left the API more or less the same, mostly the flags are different. +-- +----------------------------------------------------------------------------- + +module System.Posix.DynamicLinker.Prim ( + -- * low level API + c_dlopen, + c_dlsym, + c_dlerror, + c_dlclose, + -- dlAddr, -- XXX NYI + haveRtldNext, + haveRtldLocal, + packRTLDFlags, + RTLDFlags(..), + packDL, + DL(..) + ) + +where + +#include "HsUnix.h" + +import Data.Bits ( (.|.) ) +import Foreign.Ptr ( Ptr, FunPtr, nullPtr ) +import Foreign.C.Types ( CInt ) +import Foreign.C.String ( CString ) + +-- RTLD_NEXT madness +-- On some host (e.g. SuSe Linux 7.2) RTLD_NEXT is not visible +-- without setting _GNU_SOURCE. Since we don't want to set this +-- flag, here's a different solution: You can use the Haskell +-- function 'haveRtldNext' to check wether the flag is available +-- to you. Ideally, this will be optimized by the compiler so +-- that it should be as efficient as an #ifdef. +-- If you fail to test the flag and use it although it is +-- undefined, 'packOneModuleFlag' will bomb. +-- The same applies to RTLD_LOCAL which isn't available on +-- cygwin. + +haveRtldNext :: Bool + +#ifdef HAVE_RTLDNEXT +haveRtldNext = True + +foreign import ccall unsafe "__hsunix_rtldNext" rtldNext :: Ptr a + +#else /* HAVE_RTLDNEXT */ +haveRtldNext = False +#endif /* HAVE_RTLDNEXT */ + +haveRtldLocal :: Bool + +#ifdef HAVE_RTLDLOCAL +haveRtldLocal = True +#else /* HAVE_RTLDLOCAL */ +haveRtldLocal = False +#endif /* HAVE_RTLDLOCAL */ + +data RTLDFlags + = RTLD_LAZY + | RTLD_NOW + | RTLD_GLOBAL + | RTLD_LOCAL + deriving (Show, Read) + +foreign import ccall unsafe "dlopen" c_dlopen :: CString -> CInt -> IO (Ptr ()) +foreign import ccall unsafe "dlsym" c_dlsym :: Ptr () -> CString -> IO (FunPtr a) +foreign import ccall unsafe "dlerror" c_dlerror :: IO CString +foreign import ccall unsafe "dlclose" c_dlclose :: (Ptr ()) -> IO CInt + +packRTLDFlags :: [RTLDFlags] -> CInt +packRTLDFlags flags = foldl (\ s f -> (packRTLDFlag f) .|. s) 0 flags + +packRTLDFlag :: RTLDFlags -> CInt +packRTLDFlag RTLD_LAZY = #const RTLD_LAZY + +#ifdef HAVE_RTLDNOW +packRTLDFlag RTLD_NOW = #const RTLD_NOW +#else /* HAVE_RTLDNOW */ +packRTLDFlag RTLD_NOW = error "RTLD_NOW not available" +#endif /* HAVE_RTLDNOW */ + +#ifdef HAVE_RTLDGLOBAL +packRTLDFlag RTLD_GLOBAL = #const RTLD_GLOBAL +#else /* HAVE_RTLDGLOBAL */ +packRTLDFlag RTLD_GLOBAL = error "RTLD_GLOBAL not available" +#endif + +#ifdef HAVE_RTLDLOCAL +packRTLDFlag RTLD_LOCAL = #const RTLD_LOCAL +#else /* HAVE_RTLDLOCAL */ +packRTLDFlag RTLD_LOCAL = error "RTLD_LOCAL not available" +#endif /* HAVE_RTLDLOCAL */ + +-- |Flags for 'dlsym'. Notice that @Next@ might not be available on +-- your particular platform! + +data DL = Null | Next | Default | DLHandle (Ptr ()) deriving (Show) + +packDL :: DL -> Ptr () +packDL Null = nullPtr +#ifdef HAVE_RTLDNEXT +packDL Next = rtldNext +#else +packDL Next = error "RTLD_NEXT not available" +#endif +packDL Default = nullPtr +packDL (DLHandle h) = h |