packages feed

libffi 0.2 → 0.2.1

raw patch · 10 files changed

+664/−639 lines, 10 filessetup-changed

Files

CHANGELOG.md view
@@ -1,24 +1,28 @@-## 0.2 [2022.08.11]-* The `libffi` library now uses `bracket` internally and should now be-  exception-safe.-* There is a now a `ghc-bundled-libffi` `cabal` flag that makes this library-  statically link against GHC's bundled copy of `libffi` rather than attempt to-  link against the system `libffi`. On the vast majority of GHCs, this is the-  most reasonable option, as linking against the system `libffi` is inherently-  fragile. As a result, `+ghc-bundled-libffi` is now the defalut setting. See-  the [`README`](https://github.com/remiturk/libffi/blob/master/README.md#notes-on-ghcs-bundling-of-libffi)-  for more discussion on this point.-* The definition of `Arg` has changed:--  ```diff-  -newtype Arg = Arg { unArg :: IO (Ptr CType, Ptr CValue, IO ()) }-  +newtype Arg = Arg { unArg :: forall a. (Ptr CType -> Ptr CValue -> IO a) -> IO a }-  ```-* The definition of `RetType` has changed:-  ```diff-  -data RetType a = RetType (Ptr CType) ((Ptr CValue -> IO ()) -> IO a)-  +newtype RetType a = RetType { unRetType :: (Ptr CType -> Ptr CValue -> IO ()) -> IO a }-  ```--## 0.1 [2009.03.17]-* Initial release.+## 0.2.1 [2022.09.24]
+* Add `sizeAndAlignmentOfCType` to `Foreign.LibFFI.Base`, which can be used to
+  retrieve the size and alignment of a `CType`.
+
+## 0.2 [2022.08.11]
+* The `libffi` library now uses `bracket` internally and should now be
+  exception-safe.
+* There is a now a `ghc-bundled-libffi` `cabal` flag that makes this library
+  statically link against GHC's bundled copy of `libffi` rather than attempt to
+  link against the system `libffi`. On the vast majority of GHCs, this is the
+  most reasonable option, as linking against the system `libffi` is inherently
+  fragile. As a result, `+ghc-bundled-libffi` is now the defalut setting. See
+  the [`README`](https://github.com/remiturk/libffi/blob/master/README.md#notes-on-ghcs-bundling-of-libffi)
+  for more discussion on this point.
+* The definition of `Arg` has changed:
+
+  ```diff
+  -newtype Arg = Arg { unArg :: IO (Ptr CType, Ptr CValue, IO ()) }
+  +newtype Arg = Arg { unArg :: forall a. (Ptr CType -> Ptr CValue -> IO a) -> IO a }
+  ```
+* The definition of `RetType` has changed:
+  ```diff
+  -data RetType a = RetType (Ptr CType) ((Ptr CValue -> IO ()) -> IO a)
+  +newtype RetType a = RetType { unRetType :: (Ptr CType -> Ptr CValue -> IO ()) -> IO a }
+  ```
+
+## 0.1 [2009.03.17]
+* Initial release.
Foreign/LibFFI.hs view
@@ -1,28 +1,28 @@-{- |-This is the only module that normal users should need to import.--As an example, allocate 1GB of memory, zero it, and crash:--@-import System.Posix.DynamicLinker-import Foreign.Ptr-import Foreign.LibFFI--main = do-    malloc <- dlsym Default \"malloc\"-    memset <- dlsym Default \"memset\"-    p <- callFFI malloc (retPtr retVoid) [argCSize (2^30)]-    callFFI memset (retPtr retVoid) [argPtr p, argCInt 0, argCSize (2^30)]-    callFFI memset (retPtr retVoid) [argPtr nullPtr, argCInt 0, argCSize 1]-@--}-module Foreign.LibFFI-    (Arg-    ,RetType-    ,callFFI-    ,withRetType-    ,module Foreign.LibFFI.Types-    ) where--import Foreign.LibFFI.Base-import Foreign.LibFFI.Types+{- |
+This is the only module that normal users should need to import.
+
+As an example, allocate 1GB of memory, zero it, and crash:
+
+@
+import System.Posix.DynamicLinker
+import Foreign.Ptr
+import Foreign.LibFFI
+
+main = do
+    malloc <- dlsym Default \"malloc\"
+    memset <- dlsym Default \"memset\"
+    p <- callFFI malloc (retPtr retVoid) [argCSize (2^30)]
+    callFFI memset (retPtr retVoid) [argPtr p, argCInt 0, argCSize (2^30)]
+    callFFI memset (retPtr retVoid) [argPtr nullPtr, argCInt 0, argCSize 1]
+@
+-}
+module Foreign.LibFFI
+    (Arg
+    ,RetType
+    ,callFFI
+    ,withRetType
+    ,module Foreign.LibFFI.Types
+    ) where
+
+import Foreign.LibFFI.Base
+import Foreign.LibFFI.Types
Foreign/LibFFI/Base.hs view
@@ -1,71 +1,86 @@-{-# LANGUAGE Rank2Types #-}-{- | This module defines the basic libffi machinery.-    You will need this to create support for new ffi types. -}-module Foreign.LibFFI.Base where--import Control.Monad-import Control.Exception-import Foreign.Ptr-import Foreign.Storable-import Foreign.Marshal--import Foreign.LibFFI.Internal-import Foreign.LibFFI.FFITypes--newtype Arg = Arg { unArg :: forall a. (Ptr CType -> Ptr CValue -> IO a) -> IO a }--customPointerArg :: (a -> IO (Ptr b)) -> (Ptr b -> IO ()) -> a -> Arg-customPointerArg newA freeA a = Arg $ \withArg ->-    bracket (newA a) freeA $ \p ->-        with p $ \pp ->-            withArg ffi_type_pointer (castPtr pp)--mkStorableArg :: Storable a => Ptr CType -> a -> Arg-mkStorableArg cType a = Arg $ \withArg ->-    with a $ \p ->-        withArg cType (castPtr p)--newtype RetType a = RetType { unRetType :: (Ptr CType -> Ptr CValue -> IO ()) -> IO a }--instance Functor RetType where-    fmap f  = withRetType (return . f)--withRetType :: (a -> IO b) -> RetType a -> RetType b-withRetType f (RetType withPoke) = RetType $ withPoke >=> f--mkStorableRetType :: Storable a => Ptr CType -> RetType a-mkStorableRetType cType-    = RetType $ \write -> alloca $ \cValue -> write cType (castPtr cValue) >> peek cValue--newStorableStructArgRet :: Storable a => [Ptr CType] -> IO (a -> Arg, RetType a, IO ())-newStorableStructArgRet cTypes = do-    (cType, freeit) <- newStructCType cTypes-    return (mkStorableArg cType, mkStorableRetType cType, freeit)--newStructCType  :: [Ptr CType] -> IO (Ptr CType, IO ())-newStructCType cTypes = do-    ffi_type <- mallocBytes sizeOf_ffi_type-    elements <- newArray0 nullPtr cTypes-    init_ffi_type ffi_type elements-    return (ffi_type, free ffi_type >> free elements)--callFFI :: FunPtr a -> RetType b -> [Arg] -> IO b-callFFI funPtr (RetType actRet) args-    = allocaBytes sizeOf_cif $ \cif ->-        allocaArray n $ \cTypesPtr ->-            allocaArray n $ \cValuesPtr ->-                let-                    doCall  = actRet $ \cRetType cRetValue -> do-                                status <- ffi_prep_cif cif ffi_default_abi (fromIntegral n) cRetType cTypesPtr-                                unless (status == ffi_ok) $-                                    error "callFFI: ffi_prep_cif failed"-                                ffi_call cif funPtr cRetValue cValuesPtr-                    addArg (i, Arg actArg) goArgs-                            = actArg $ \cType cValue -> do-                                pokeElemOff cTypesPtr i cType-                                pokeElemOff cValuesPtr i cValue-                                goArgs-                in-                    foldr addArg doCall $ zip [0..] args-    where-        n = length args+{-# LANGUAGE Rank2Types #-}
+{- | This module defines the basic libffi machinery.
+    You will need this to create support for new ffi types. -}
+module Foreign.LibFFI.Base where
+
+import Control.Monad
+import Control.Exception
+import Foreign.Ptr
+import Foreign.Storable
+import Foreign.Marshal
+
+import Foreign.LibFFI.Internal
+import Foreign.LibFFI.FFITypes
+
+newtype Arg = Arg { unArg :: forall a. (Ptr CType -> Ptr CValue -> IO a) -> IO a }
+
+customPointerArg :: (a -> IO (Ptr b)) -> (Ptr b -> IO ()) -> a -> Arg
+customPointerArg newA freeA a = Arg $ \withArg ->
+    bracket (newA a) freeA $ \p ->
+        with p $ \pp ->
+            withArg ffi_type_pointer (castPtr pp)
+
+mkStorableArg :: Storable a => Ptr CType -> a -> Arg
+mkStorableArg cType a = Arg $ \withArg ->
+    with a $ \p ->
+        withArg cType (castPtr p)
+
+newtype RetType a = RetType { unRetType :: (Ptr CType -> Ptr CValue -> IO ()) -> IO a }
+
+instance Functor RetType where
+    fmap f  = withRetType (return . f)
+
+withRetType :: (a -> IO b) -> RetType a -> RetType b
+withRetType f (RetType withPoke) = RetType $ withPoke >=> f
+
+mkStorableRetType :: Storable a => Ptr CType -> RetType a
+mkStorableRetType cType
+    = RetType $ \write -> alloca $ \cValue -> write cType (castPtr cValue) >> peek cValue
+
+newStorableStructArgRet :: Storable a => [Ptr CType] -> IO (a -> Arg, RetType a, IO ())
+newStorableStructArgRet cTypes = do
+    (cType, freeit) <- newStructCType cTypes
+    return (mkStorableArg cType, mkStorableRetType cType, freeit)
+
+newStructCType  :: [Ptr CType] -> IO (Ptr CType, IO ())
+newStructCType cTypes = do
+    ffi_type <- mallocBytes sizeOf_ffi_type
+    elements <- newArray0 nullPtr cTypes
+    init_ffi_type ffi_type elements
+    return (ffi_type, free ffi_type >> free elements)
+
+sizeAndAlignmentOfCType :: Ptr CType -> IO (Int, Int)
+sizeAndAlignmentOfCType cType = do
+  (size, alignment) <- ffi_type_size_and_alignment cType
+  if size /= 0 && alignment /= 0
+  then return (fromIntegral size, fromIntegral alignment)
+  else do
+    -- The type's size and alignment haven't been initialized
+    -- so we force it with a call to `ffi_prep_cif`.
+    status <- allocaBytes sizeOf_cif $ \cif ->
+                ffi_prep_cif cif ffi_default_abi 0 cType nullPtr
+    unless (status == ffi_ok) $
+      error "sizeAndAlignmentOfCType: ffi_prep_cif failed"
+    (size, alignment) <- ffi_type_size_and_alignment cType
+    return (fromIntegral size, fromIntegral alignment)
+
+callFFI :: FunPtr a -> RetType b -> [Arg] -> IO b
+callFFI funPtr (RetType actRet) args
+    = allocaBytes sizeOf_cif $ \cif ->
+        allocaArray n $ \cTypesPtr ->
+            allocaArray n $ \cValuesPtr ->
+                let
+                    doCall  = actRet $ \cRetType cRetValue -> do
+                                status <- ffi_prep_cif cif ffi_default_abi (fromIntegral n) cRetType cTypesPtr
+                                unless (status == ffi_ok) $
+                                    error "callFFI: ffi_prep_cif failed"
+                                ffi_call cif funPtr cRetValue cValuesPtr
+                    addArg (i, Arg actArg) goArgs
+                            = actArg $ \cType cValue -> do
+                                pokeElemOff cTypesPtr i cType
+                                pokeElemOff cValuesPtr i cValue
+                                goArgs
+                in
+                    foldr addArg doCall $ zip [0..] args
+    where
+        n = length args
Foreign/LibFFI/FFITypes.hs view
@@ -1,85 +1,85 @@-{-# LANGUAGE ForeignFunctionInterface #-}-{- | The pointers exported and used by the C libffi describing basic ffi types. -}-module Foreign.LibFFI.FFITypes where--import Data.Int-import Data.Word-import Foreign.C.Types-import Foreign.Ptr-import Foreign.Storable--import Foreign.LibFFI.Internal--foreign import ccall unsafe "&" ffi_type_void :: Ptr CType-foreign import ccall unsafe "&" ffi_type_sint8 :: Ptr CType-foreign import ccall unsafe "&" ffi_type_uint8 :: Ptr CType-foreign import ccall unsafe "&" ffi_type_uint16 :: Ptr CType-foreign import ccall unsafe "&" ffi_type_sint16 :: Ptr CType-foreign import ccall unsafe "&" ffi_type_uint32 :: Ptr CType-foreign import ccall unsafe "&" ffi_type_sint32 :: Ptr CType-foreign import ccall unsafe "&" ffi_type_uint64 :: Ptr CType-foreign import ccall unsafe "&" ffi_type_sint64 :: Ptr CType-foreign import ccall unsafe "&" ffi_type_float  :: Ptr CType-foreign import ccall unsafe "&" ffi_type_double :: Ptr CType-foreign import ccall unsafe "&" ffi_type_pointer :: Ptr CType--ffi_type_uchar  :: Ptr CType-ffi_type_uchar  = ffi_type_uint8--ffi_type_schar  :: Ptr CType-ffi_type_schar  = ffi_type_sint8--ffi_type_wchar  :: Ptr CType-ffi_type_wchar  = case sizeOf (undefined :: CWchar) of-                    2   -> ffi_type_sint16-                    4   -> ffi_type_sint32-                    8   -> ffi_type_sint64-                    _   -> error "ffi_type_wchar of unsupported size"--ffi_type_size   :: Ptr CType-ffi_type_size   = case sizeOf (undefined :: CSize) of-                    4   -> ffi_type_uint32-                    8   -> ffi_type_uint64-                    _   -> error "ffi_type_size of unsupported size"--ffi_type_time   :: Ptr CType-ffi_type_time   = case sizeOf (undefined :: CTime) of-                    4   -> ffi_type_sint32-                    8   -> ffi_type_sint64-                    _   -> error "ffi_type_time of unsupported size"--ffi_type_uint   :: Ptr CType-ffi_type_uint   = case sizeOf (undefined :: CUInt) of-                    4   -> ffi_type_uint32-                    8   -> ffi_type_uint64-                    _   -> error "ffi_type_uint of unsupported size"--ffi_type_sint   :: Ptr CType-ffi_type_sint   = case sizeOf (undefined :: CInt) of-                    4   -> ffi_type_sint32-                    8   -> ffi_type_sint64-                    _   -> error "ffi_type_sint of unsupported size"--ffi_type_ulong  :: Ptr CType-ffi_type_ulong  = case sizeOf (undefined :: CULong) of-                    4   -> ffi_type_uint32-                    8   -> ffi_type_uint64-                    _   -> error "ffi_type_ulong of unsupported size"--ffi_type_slong  :: Ptr CType-ffi_type_slong  = case sizeOf (undefined :: CLong) of-                    4   -> ffi_type_sint32-                    8   -> ffi_type_sint64-                    _   -> error "ffi_type_slong of unsupported size"--ffi_type_hs_int :: Ptr CType-ffi_type_hs_int = case sizeOf (undefined :: Int) of-                    4   -> ffi_type_sint32-                    8   -> ffi_type_sint64-                    _   -> error "ffi_type_hs_int: unsupported sizeOf (_ :: Int)"--ffi_type_hs_word :: Ptr CType-ffi_type_hs_word = case sizeOf (undefined :: Word) of-                    4   -> ffi_type_uint32-                    8   -> ffi_type_uint64-                    _   -> error "ffi_type_hs_word: unsupported sizeOf (_ :: Word)"+{-# LANGUAGE ForeignFunctionInterface #-}
+{- | The pointers exported and used by the C libffi describing basic ffi types. -}
+module Foreign.LibFFI.FFITypes where
+
+import Data.Int
+import Data.Word
+import Foreign.C.Types
+import Foreign.Ptr
+import Foreign.Storable
+
+import Foreign.LibFFI.Internal
+
+foreign import ccall unsafe "&" ffi_type_void :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_sint8 :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_uint8 :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_uint16 :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_sint16 :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_uint32 :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_sint32 :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_uint64 :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_sint64 :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_float  :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_double :: Ptr CType
+foreign import ccall unsafe "&" ffi_type_pointer :: Ptr CType
+
+ffi_type_uchar  :: Ptr CType
+ffi_type_uchar  = ffi_type_uint8
+
+ffi_type_schar  :: Ptr CType
+ffi_type_schar  = ffi_type_sint8
+
+ffi_type_wchar  :: Ptr CType
+ffi_type_wchar  = case sizeOf (undefined :: CWchar) of
+                    2   -> ffi_type_sint16
+                    4   -> ffi_type_sint32
+                    8   -> ffi_type_sint64
+                    _   -> error "ffi_type_wchar of unsupported size"
+
+ffi_type_size   :: Ptr CType
+ffi_type_size   = case sizeOf (undefined :: CSize) of
+                    4   -> ffi_type_uint32
+                    8   -> ffi_type_uint64
+                    _   -> error "ffi_type_size of unsupported size"
+
+ffi_type_time   :: Ptr CType
+ffi_type_time   = case sizeOf (undefined :: CTime) of
+                    4   -> ffi_type_sint32
+                    8   -> ffi_type_sint64
+                    _   -> error "ffi_type_time of unsupported size"
+
+ffi_type_uint   :: Ptr CType
+ffi_type_uint   = case sizeOf (undefined :: CUInt) of
+                    4   -> ffi_type_uint32
+                    8   -> ffi_type_uint64
+                    _   -> error "ffi_type_uint of unsupported size"
+
+ffi_type_sint   :: Ptr CType
+ffi_type_sint   = case sizeOf (undefined :: CInt) of
+                    4   -> ffi_type_sint32
+                    8   -> ffi_type_sint64
+                    _   -> error "ffi_type_sint of unsupported size"
+
+ffi_type_ulong  :: Ptr CType
+ffi_type_ulong  = case sizeOf (undefined :: CULong) of
+                    4   -> ffi_type_uint32
+                    8   -> ffi_type_uint64
+                    _   -> error "ffi_type_ulong of unsupported size"
+
+ffi_type_slong  :: Ptr CType
+ffi_type_slong  = case sizeOf (undefined :: CLong) of
+                    4   -> ffi_type_sint32
+                    8   -> ffi_type_sint64
+                    _   -> error "ffi_type_slong of unsupported size"
+
+ffi_type_hs_int :: Ptr CType
+ffi_type_hs_int = case sizeOf (undefined :: Int) of
+                    4   -> ffi_type_sint32
+                    8   -> ffi_type_sint64
+                    _   -> error "ffi_type_hs_int: unsupported sizeOf (_ :: Int)"
+
+ffi_type_hs_word :: Ptr CType
+ffi_type_hs_word = case sizeOf (undefined :: Word) of
+                    4   -> ffi_type_uint32
+                    8   -> ffi_type_uint64
+                    _   -> error "ffi_type_hs_word: unsupported sizeOf (_ :: Word)"
Foreign/LibFFI/Internal.hsc view
@@ -1,43 +1,49 @@-{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}-{- | The internals of the C library libffi -}-module Foreign.LibFFI.Internal where--#include <ffi.h>--import Data.Int-import Data.Word-import Foreign.C.Types-import Foreign.Ptr-import Foreign.Storable--data CValue-data CType-data CIF--type C_ffi_status   = (#type ffi_status)-type C_ffi_abi      = (#type ffi_abi)--ffi_default_abi :: C_ffi_abi-ffi_default_abi = #const FFI_DEFAULT_ABI--ffi_ok          :: C_ffi_status-ffi_ok          = #const FFI_OK--sizeOf_cif :: Int-sizeOf_cif = #size ffi_cif--sizeOf_ffi_type :: Int-sizeOf_ffi_type = #size ffi_type--init_ffi_type   :: Ptr CType -> Ptr (Ptr CType) -> IO ()-init_ffi_type cType cTypes = do-    (#poke ffi_type, size) cType (0 :: CSize)-    (#poke ffi_type, alignment) cType (0 :: CUShort)-    (#poke ffi_type, type) cType ((#const FFI_TYPE_STRUCT) :: CUShort)-    (#poke ffi_type, elements) cType cTypes--foreign import ccall safe ffi_prep_cif-    :: Ptr CIF -> C_ffi_abi -> CUInt -> Ptr CType -> Ptr (Ptr CType) -> IO C_ffi_status--foreign import ccall safe ffi_call-    :: Ptr CIF -> FunPtr a -> Ptr CValue -> Ptr (Ptr CValue) -> IO ()+{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
+{- | The internals of the C library libffi -}
+module Foreign.LibFFI.Internal where
+
+#include <ffi.h>
+
+import Data.Int
+import Data.Word
+import Foreign.C.Types
+import Foreign.Ptr
+import Foreign.Storable
+
+data CValue
+data CType
+data CIF
+
+type C_ffi_status   = (#type ffi_status)
+type C_ffi_abi      = (#type ffi_abi)
+
+ffi_default_abi :: C_ffi_abi
+ffi_default_abi = #const FFI_DEFAULT_ABI
+
+ffi_ok          :: C_ffi_status
+ffi_ok          = #const FFI_OK
+
+sizeOf_cif :: Int
+sizeOf_cif = #size ffi_cif
+
+sizeOf_ffi_type :: Int
+sizeOf_ffi_type = #size ffi_type
+
+init_ffi_type   :: Ptr CType -> Ptr (Ptr CType) -> IO ()
+init_ffi_type cType cTypes = do
+    (#poke ffi_type, size) cType (0 :: CSize)
+    (#poke ffi_type, alignment) cType (0 :: CUShort)
+    (#poke ffi_type, type) cType ((#const FFI_TYPE_STRUCT) :: CUShort)
+    (#poke ffi_type, elements) cType cTypes
+
+ffi_type_size_and_alignment :: Ptr CType -> IO (CSize, CUShort)
+ffi_type_size_and_alignment cType = do
+  size <- (#peek ffi_type, size) cType
+  alignment <- (#peek ffi_type, alignment) cType
+  return (size, alignment)
+
+foreign import ccall safe ffi_prep_cif
+    :: Ptr CIF -> C_ffi_abi -> CUInt -> Ptr CType -> Ptr (Ptr CType) -> IO C_ffi_status
+
+foreign import ccall safe ffi_call
+    :: Ptr CIF -> FunPtr a -> Ptr CValue -> Ptr (Ptr CValue) -> IO ()
Foreign/LibFFI/Types.hs view
@@ -1,215 +1,215 @@--- | Arguments and return types-module Foreign.LibFFI.Types (-    -- * Arguments-    -- ** Integral types-    argCInt,-    argCUInt,-    argCLong,-    argCULong,-    argInt8,-    argInt16,-    argInt32,-    argInt64,-    argWord8,-    argWord16,-    argWord32,-    argWord64,-    -- ** Floating point types-    argCFloat,-    argCDouble,-    -- ** Various other C types-    argCSize,-    argCTime,-    argCChar,-    argCUChar,-    argCWchar,-    argPtr,-    argFunPtr,-    -- ** Strings-    argString,-    argByteString,-    argConstByteString,-    -- * Return types-    -- ** Integral types-    retVoid,-    retCInt,-    retCUInt,-    retCLong,-    retCULong,-    retInt8,-    retInt16,-    retInt32,-    retInt64,-    retWord8,-    retWord16,-    retWord32,-    retWord64,-    -- ** Floating point types-    retCFloat,-    retCDouble,-    -- ** Various other C types-    retCSize,-    retCTime,-    retCChar,-    retCUChar,-    retCWchar,-    retPtr,-    retFunPtr,-    -- ** Strings-    retCString,-    retString,-    retByteString,-    retMallocByteString-    ) where--import Control.Monad-import Data.List-import Data.Char-import Data.Int-import Data.Word--import Foreign.C.Types-import Foreign.Ptr-import Foreign.Storable-import Foreign.C.String-import Foreign.Marshal-import qualified Data.ByteString as BS-import qualified Data.ByteString.Unsafe as BSU--import Foreign.LibFFI.Base-import Foreign.LibFFI.FFITypes--argCInt     :: CInt -> Arg-argCInt     = mkStorableArg ffi_type_sint-argCUInt    :: CUInt -> Arg-argCUInt    = mkStorableArg ffi_type_uint-argCLong    :: CLong -> Arg-argCLong    = mkStorableArg ffi_type_slong-argCULong   :: CULong -> Arg-argCULong   = mkStorableArg ffi_type_ulong---- | Note that on e.g. x86_64, Int \/= CInt-argInt8     :: Int8 -> Arg-argInt8     = mkStorableArg ffi_type_sint8-argInt16    :: Int16 -> Arg-argInt16    = mkStorableArg ffi_type_sint16-argInt32    :: Int32 -> Arg-argInt32    = mkStorableArg ffi_type_sint32-argInt64    :: Int64 -> Arg-argInt64    = mkStorableArg ffi_type_sint64--argWord8    :: Word8 -> Arg-argWord8    = mkStorableArg ffi_type_uint8-argWord16   :: Word16 -> Arg-argWord16   = mkStorableArg ffi_type_uint16-argWord32   :: Word32 -> Arg-argWord32   = mkStorableArg ffi_type_uint32-argWord64   :: Word64 -> Arg-argWord64   = mkStorableArg ffi_type_uint64--argCFloat   :: CFloat -> Arg-argCFloat   = mkStorableArg ffi_type_float-argCDouble  :: CDouble -> Arg-argCDouble  = mkStorableArg ffi_type_double--argCSize    :: CSize -> Arg-argCSize    = mkStorableArg ffi_type_size-argCTime    :: CTime -> Arg-argCTime    = mkStorableArg ffi_type_size--argCChar    :: CChar -> Arg-argCChar    = mkStorableArg ffi_type_schar-argCUChar   :: CUChar -> Arg-argCUChar   = mkStorableArg ffi_type_uchar--argCWchar   :: CWchar -> Arg-argCWchar   = mkStorableArg ffi_type_wchar--argPtr      :: Ptr a -> Arg-argPtr      = mkStorableArg ffi_type_pointer--argFunPtr   :: FunPtr a -> Arg-argFunPtr   = mkStorableArg ffi_type_pointer--{- | The string argument is passed to C as a char * pointer, which is freed afterwards.-     The argument should not contain zero-bytes. -}-argString   :: String -> Arg-argString   = customPointerArg newCString free---- | Like argString, but for ByteString's.-argByteString  :: BS.ByteString -> Arg-argByteString  = customPointerArg (flip BS.useAsCString return) (const $ return ())---- | Like argByteString, but changing the string from C breaks referential transparency.-argConstByteString  :: BS.ByteString -> Arg-argConstByteString  = customPointerArg (flip BSU.unsafeUseAsCString return) (const $ return ())--retVoid     :: RetType ()-retVoid     = RetType (\write -> write ffi_type_void nullPtr >> return ())--retCInt     :: RetType CInt-retCInt     = mkStorableRetType ffi_type_sint-retCUInt    :: RetType CUInt-retCUInt    = mkStorableRetType ffi_type_uint-retCLong    :: RetType CLong-retCLong    = mkStorableRetType ffi_type_slong-retCULong   :: RetType CULong-retCULong   = mkStorableRetType ffi_type_ulong--retInt8     :: RetType Int8-retInt8     = mkStorableRetType ffi_type_sint8-retInt16    :: RetType Int16-retInt16    = mkStorableRetType ffi_type_sint16-retInt32    :: RetType Int32-retInt32    = mkStorableRetType ffi_type_sint32-retInt64    :: RetType Int64-retInt64    = mkStorableRetType ffi_type_sint64--retWord8    :: RetType Word8-retWord8    = mkStorableRetType ffi_type_uint8-retWord16   :: RetType Word16-retWord16   = mkStorableRetType ffi_type_uint16-retWord32   :: RetType Word32-retWord32   = mkStorableRetType ffi_type_uint32-retWord64   :: RetType Word64-retWord64   = mkStorableRetType ffi_type_uint64--retCFloat   :: RetType CFloat-retCFloat   = mkStorableRetType ffi_type_float-retCDouble  :: RetType CDouble-retCDouble  = mkStorableRetType ffi_type_double--retCSize    :: RetType CSize-retCSize    = mkStorableRetType ffi_type_size-retCTime    :: RetType CTime-retCTime    = mkStorableRetType ffi_type_time--retCChar    :: RetType CChar-retCChar    = mkStorableRetType ffi_type_schar-retCUChar   :: RetType CUChar-retCUChar   = mkStorableRetType ffi_type_uchar--retCWchar   :: RetType CWchar-retCWchar   = mkStorableRetType ffi_type_wchar--retFunPtr   :: RetType a -> RetType (FunPtr a)-retFunPtr _ = mkStorableRetType ffi_type_pointer--retPtr      :: RetType a -> RetType (Ptr a)-retPtr _    = mkStorableRetType ffi_type_pointer--retCString          :: RetType CString-retCString          = retPtr retCChar--{- | Peek a String out of the returned char *. The char * is not freed. -}-retString           :: RetType String-retString           = withRetType peekCString (retPtr retCChar)--{- | Like retString, but for ByteString's -}-retByteString       :: RetType BS.ByteString-retByteString       = withRetType BS.packCString (retPtr retCChar)--{- | Make a ByteString out of the returned char *.-     The char * will be free(3)ed when the ByteString is garbage collected. -}-retMallocByteString :: RetType BS.ByteString-retMallocByteString = withRetType BSU.unsafePackMallocCString (retPtr retCChar)+-- | Arguments and return types
+module Foreign.LibFFI.Types (
+    -- * Arguments
+    -- ** Integral types
+    argCInt,
+    argCUInt,
+    argCLong,
+    argCULong,
+    argInt8,
+    argInt16,
+    argInt32,
+    argInt64,
+    argWord8,
+    argWord16,
+    argWord32,
+    argWord64,
+    -- ** Floating point types
+    argCFloat,
+    argCDouble,
+    -- ** Various other C types
+    argCSize,
+    argCTime,
+    argCChar,
+    argCUChar,
+    argCWchar,
+    argPtr,
+    argFunPtr,
+    -- ** Strings
+    argString,
+    argByteString,
+    argConstByteString,
+    -- * Return types
+    -- ** Integral types
+    retVoid,
+    retCInt,
+    retCUInt,
+    retCLong,
+    retCULong,
+    retInt8,
+    retInt16,
+    retInt32,
+    retInt64,
+    retWord8,
+    retWord16,
+    retWord32,
+    retWord64,
+    -- ** Floating point types
+    retCFloat,
+    retCDouble,
+    -- ** Various other C types
+    retCSize,
+    retCTime,
+    retCChar,
+    retCUChar,
+    retCWchar,
+    retPtr,
+    retFunPtr,
+    -- ** Strings
+    retCString,
+    retString,
+    retByteString,
+    retMallocByteString
+    ) where
+
+import Control.Monad
+import Data.List
+import Data.Char
+import Data.Int
+import Data.Word
+
+import Foreign.C.Types
+import Foreign.Ptr
+import Foreign.Storable
+import Foreign.C.String
+import Foreign.Marshal
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Unsafe as BSU
+
+import Foreign.LibFFI.Base
+import Foreign.LibFFI.FFITypes
+
+argCInt     :: CInt -> Arg
+argCInt     = mkStorableArg ffi_type_sint
+argCUInt    :: CUInt -> Arg
+argCUInt    = mkStorableArg ffi_type_uint
+argCLong    :: CLong -> Arg
+argCLong    = mkStorableArg ffi_type_slong
+argCULong   :: CULong -> Arg
+argCULong   = mkStorableArg ffi_type_ulong
+
+-- | Note that on e.g. x86_64, Int \/= CInt
+argInt8     :: Int8 -> Arg
+argInt8     = mkStorableArg ffi_type_sint8
+argInt16    :: Int16 -> Arg
+argInt16    = mkStorableArg ffi_type_sint16
+argInt32    :: Int32 -> Arg
+argInt32    = mkStorableArg ffi_type_sint32
+argInt64    :: Int64 -> Arg
+argInt64    = mkStorableArg ffi_type_sint64
+
+argWord8    :: Word8 -> Arg
+argWord8    = mkStorableArg ffi_type_uint8
+argWord16   :: Word16 -> Arg
+argWord16   = mkStorableArg ffi_type_uint16
+argWord32   :: Word32 -> Arg
+argWord32   = mkStorableArg ffi_type_uint32
+argWord64   :: Word64 -> Arg
+argWord64   = mkStorableArg ffi_type_uint64
+
+argCFloat   :: CFloat -> Arg
+argCFloat   = mkStorableArg ffi_type_float
+argCDouble  :: CDouble -> Arg
+argCDouble  = mkStorableArg ffi_type_double
+
+argCSize    :: CSize -> Arg
+argCSize    = mkStorableArg ffi_type_size
+argCTime    :: CTime -> Arg
+argCTime    = mkStorableArg ffi_type_size
+
+argCChar    :: CChar -> Arg
+argCChar    = mkStorableArg ffi_type_schar
+argCUChar   :: CUChar -> Arg
+argCUChar   = mkStorableArg ffi_type_uchar
+
+argCWchar   :: CWchar -> Arg
+argCWchar   = mkStorableArg ffi_type_wchar
+
+argPtr      :: Ptr a -> Arg
+argPtr      = mkStorableArg ffi_type_pointer
+
+argFunPtr   :: FunPtr a -> Arg
+argFunPtr   = mkStorableArg ffi_type_pointer
+
+{- | The string argument is passed to C as a char * pointer, which is freed afterwards.
+     The argument should not contain zero-bytes. -}
+argString   :: String -> Arg
+argString   = customPointerArg newCString free
+
+-- | Like argString, but for ByteString's.
+argByteString  :: BS.ByteString -> Arg
+argByteString  = customPointerArg (flip BS.useAsCString return) (const $ return ())
+
+-- | Like argByteString, but changing the string from C breaks referential transparency.
+argConstByteString  :: BS.ByteString -> Arg
+argConstByteString  = customPointerArg (flip BSU.unsafeUseAsCString return) (const $ return ())
+
+retVoid     :: RetType ()
+retVoid     = RetType (\write -> write ffi_type_void nullPtr >> return ())
+
+retCInt     :: RetType CInt
+retCInt     = mkStorableRetType ffi_type_sint
+retCUInt    :: RetType CUInt
+retCUInt    = mkStorableRetType ffi_type_uint
+retCLong    :: RetType CLong
+retCLong    = mkStorableRetType ffi_type_slong
+retCULong   :: RetType CULong
+retCULong   = mkStorableRetType ffi_type_ulong
+
+retInt8     :: RetType Int8
+retInt8     = mkStorableRetType ffi_type_sint8
+retInt16    :: RetType Int16
+retInt16    = mkStorableRetType ffi_type_sint16
+retInt32    :: RetType Int32
+retInt32    = mkStorableRetType ffi_type_sint32
+retInt64    :: RetType Int64
+retInt64    = mkStorableRetType ffi_type_sint64
+
+retWord8    :: RetType Word8
+retWord8    = mkStorableRetType ffi_type_uint8
+retWord16   :: RetType Word16
+retWord16   = mkStorableRetType ffi_type_uint16
+retWord32   :: RetType Word32
+retWord32   = mkStorableRetType ffi_type_uint32
+retWord64   :: RetType Word64
+retWord64   = mkStorableRetType ffi_type_uint64
+
+retCFloat   :: RetType CFloat
+retCFloat   = mkStorableRetType ffi_type_float
+retCDouble  :: RetType CDouble
+retCDouble  = mkStorableRetType ffi_type_double
+
+retCSize    :: RetType CSize
+retCSize    = mkStorableRetType ffi_type_size
+retCTime    :: RetType CTime
+retCTime    = mkStorableRetType ffi_type_time
+
+retCChar    :: RetType CChar
+retCChar    = mkStorableRetType ffi_type_schar
+retCUChar   :: RetType CUChar
+retCUChar   = mkStorableRetType ffi_type_uchar
+
+retCWchar   :: RetType CWchar
+retCWchar   = mkStorableRetType ffi_type_wchar
+
+retFunPtr   :: RetType a -> RetType (FunPtr a)
+retFunPtr _ = mkStorableRetType ffi_type_pointer
+
+retPtr      :: RetType a -> RetType (Ptr a)
+retPtr _    = mkStorableRetType ffi_type_pointer
+
+retCString          :: RetType CString
+retCString          = retPtr retCChar
+
+{- | Peek a String out of the returned char *. The char * is not freed. -}
+retString           :: RetType String
+retString           = withRetType peekCString (retPtr retCChar)
+
+{- | Like retString, but for ByteString's -}
+retByteString       :: RetType BS.ByteString
+retByteString       = withRetType BS.packCString (retPtr retCChar)
+
+{- | Make a ByteString out of the returned char *.
+     The char * will be free(3)ed when the ByteString is garbage collected. -}
+retMallocByteString :: RetType BS.ByteString
+retMallocByteString = withRetType BSU.unsafePackMallocCString (retPtr retCChar)
LICENSE view
@@ -1,23 +1,23 @@-Copyright (c) 2008, Remi Turk-All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:--1. Redistributions of source code must retain the above copyright notice,-   this list of conditions and the following disclaimer.-2. Redistributions in binary form must reproduce the above copyright-   notice, this list of conditions and the following disclaimer in the-   documentation and/or other materials provided with the distribution.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE-POSSIBILITY OF SUCH DAMAGE.+Copyright (c) 2008, Remi Turk
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
README.md view
@@ -1,85 +1,85 @@-# `libffi`-[![Hackage](https://img.shields.io/hackage/v/libffi.svg)][Hackage: libffi]-[![Hackage Dependencies](https://img.shields.io/hackage-deps/v/libffi.svg)](http://packdeps.haskellers.com/reverse/libffi)-[![Haskell Programming Language](https://img.shields.io/badge/language-Haskell-blue.svg)][Haskell.org]-[![BSD3 License](http://img.shields.io/badge/license-BSD3-brightgreen.svg)][tl;dr Legal: BSD3]-[![Linux build](https://github.com/remiturk/libffi/workflows/Haskell-CI/badge.svg)](https://github.com/remiturk/libffi/actions?query=workflow%3AHaskell-CI)--[Hackage: libffi]:-  http://hackage.haskell.org/package/libffi-  "libffi package on Hackage"-[Haskell.org]:-  http://www.haskell.org-  "The Haskell Programming Language"-[tl;dr Legal: BSD3]:-  https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29-  "BSD 3-Clause License (Revised)"--A binding to `libffi`, allowing C functions of types only known at runtime to be called from Haskell.--# Notes on GHC's bundling of `libffi`--This library makes a somewhat unusual choice: by default, it does not-explicitly declare a dependency against the `libffi` C library. This is because-most binary distributions of GHC—that is, GHCs configured without the-`--with-system-libffi` option—bundle their own copies of `libffi`. One of these-copies is statically linked, and another of these copies is dynamically linked.-Moreover, whenever GHC compiles an executable, it will always pass the-necessary flags to link against its static copy of `libffi`, as the GHC runtime-system depends on it.--When GHC bundles its own copies of `libffi`, if you were to declare, say, an-`extra-libraries: ffi` dependency, then it would not behave in the way that you-would expect. This is because:--1. The linker flags to link against GHC's static copy of `libffi` always come-   first in the final linking step when compiling an executable. As a result,-   declaring an `extra-libraries: ffi` dependency won't make much of a-   difference, since GHC will always statically link against its own copy of-   `libffi` anyway due to the order of linker flags.--2. Moreover, declaring an `extra-libraries: ffi` dependency can have the-   unfortunate side effect of declaring an _unused_ dynamic dependency against-   `libffi`. Even worse is the fact that the version of dynamically linked-   `libffi` that comes with your operating system may differ from the version-   of dynamiclly linked `libffi` that GHC bundles. When the version numbers-   differ, this can lead to the compiled executable failing at runtime with-   mysterious errors such as:--   ```-   error while loading shared libraries: libffi.so.7: cannot open shared object file: No such file or directory-   ```--   For more information on this point, see-   [GHC#15397](https://gitlab.haskell.org/ghc/ghc/-/issues/15397).--Observation (2) means that when GHC is configured with `--with-system-libffi`,-it is inherently fragile to use `extra-libraries: ffi`. This is an unfortunate-situation, but there is not much that one can do about this short of fixing-GHC#15397 upstream. A workaround would be to configure GHC with-`--with-system-libffi`, but practically speaking, the vast majority of GHC-binary distributions do not configure this way. This includes all versions of-GHC that `ghcup` distributes, so unless we want to exclude most GHC users, we-need some kind of workaround for this issue.--Our workaround is to rely on observation (1). That is, because GHC always-passes flags to the linker to link against its own static copy of `libffi`, we-can always assume that GHC will handle the `libffi` dependency for us. As a-result, the default behavior for this library is to enable the-`+ghc-bundled-libffi` flag, which means that the library will not declare an-external dependency on `libffi` at all. This is rather unusual, but then again,-GHC bundling its own copies of `libffi` is also unusual. (To our knowledge,-this is the _only_ C library that GHC bundles in this fashion.)--We have tested out `+ghc-bundled-libffi` on Windows, macOS, and Linux, and it-works as expected. If you encounter any linking oddities with-`+ghc-bundled-libffi`, please file an issue.--It is worth re-emphasizing that `+ghc-bundled-libffi` will only work if you are-using a binary distribution of GHC that was not configured with the-`--with-system-libffi` option. If you _are_ using such a GHC, then you will-need to use `-ghc-bundle-libffi` (note the minus sign) to disable the flag and-link against your operating system's copy of `libffi`. Unfortunately, `cabal`-does not provide a way to detect whether GHC was configured with-`--with-system-libffi` or not, so the burden is on users to enable or disable-`ghc-bundle-libffi` as appropriate.+# `libffi`
+[![Hackage](https://img.shields.io/hackage/v/libffi.svg)][Hackage: libffi]
+[![Hackage Dependencies](https://img.shields.io/hackage-deps/v/libffi.svg)](http://packdeps.haskellers.com/reverse/libffi)
+[![Haskell Programming Language](https://img.shields.io/badge/language-Haskell-blue.svg)][Haskell.org]
+[![BSD3 License](http://img.shields.io/badge/license-BSD3-brightgreen.svg)][tl;dr Legal: BSD3]
+[![Linux build](https://github.com/remiturk/libffi/workflows/Haskell-CI/badge.svg)](https://github.com/remiturk/libffi/actions?query=workflow%3AHaskell-CI)
+
+[Hackage: libffi]:
+  http://hackage.haskell.org/package/libffi
+  "libffi package on Hackage"
+[Haskell.org]:
+  http://www.haskell.org
+  "The Haskell Programming Language"
+[tl;dr Legal: BSD3]:
+  https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29
+  "BSD 3-Clause License (Revised)"
+
+A binding to `libffi`, allowing C functions of types only known at runtime to be called from Haskell.
+
+# Notes on GHC's bundling of `libffi`
+
+This library makes a somewhat unusual choice: by default, it does not
+explicitly declare a dependency against the `libffi` C library. This is because
+most binary distributions of GHC—that is, GHCs configured without the
+`--with-system-libffi` option—bundle their own copies of `libffi`. One of these
+copies is statically linked, and another of these copies is dynamically linked.
+Moreover, whenever GHC compiles an executable, it will always pass the
+necessary flags to link against its static copy of `libffi`, as the GHC runtime
+system depends on it.
+
+When GHC bundles its own copies of `libffi`, if you were to declare, say, an
+`extra-libraries: ffi` dependency, then it would not behave in the way that you
+would expect. This is because:
+
+1. The linker flags to link against GHC's static copy of `libffi` always come
+   first in the final linking step when compiling an executable. As a result,
+   declaring an `extra-libraries: ffi` dependency won't make much of a
+   difference, since GHC will always statically link against its own copy of
+   `libffi` anyway due to the order of linker flags.
+
+2. Moreover, declaring an `extra-libraries: ffi` dependency can have the
+   unfortunate side effect of declaring an _unused_ dynamic dependency against
+   `libffi`. Even worse is the fact that the version of dynamically linked
+   `libffi` that comes with your operating system may differ from the version
+   of dynamically linked `libffi` that GHC bundles. When the version numbers
+   differ, this can lead to the compiled executable failing at runtime with
+   mysterious errors such as:
+
+   ```
+   error while loading shared libraries: libffi.so.7: cannot open shared object file: No such file or directory
+   ```
+
+   For more information on this point, see
+   [GHC#15397](https://gitlab.haskell.org/ghc/ghc/-/issues/15397).
+
+Observation (2) means that when GHC is configured with `--with-system-libffi`,
+it is inherently fragile to use `extra-libraries: ffi`. This is an unfortunate
+situation, but there is not much that one can do about this short of fixing
+GHC#15397 upstream. A workaround would be to configure GHC with
+`--with-system-libffi`, but practically speaking, the vast majority of GHC
+binary distributions do not configure this way. This includes all versions of
+GHC that `ghcup` distributes, so unless we want to exclude most GHC users, we
+need some kind of workaround for this issue.
+
+Our workaround is to rely on observation (1). That is, because GHC always
+passes flags to the linker to link against its own static copy of `libffi`, we
+can always assume that GHC will handle the `libffi` dependency for us. As a
+result, the default behavior for this library is to enable the
+`+ghc-bundled-libffi` flag, which means that the library will not declare an
+external dependency on `libffi` at all. This is rather unusual, but then again,
+GHC bundling its own copies of `libffi` is also unusual. (To our knowledge,
+this is the _only_ C library that GHC bundles in this fashion.)
+
+We have tested out `+ghc-bundled-libffi` on Windows, macOS, and Linux, and it
+works as expected. If you encounter any linking oddities with
+`+ghc-bundled-libffi`, please file an issue.
+
+It is worth re-emphasizing that `+ghc-bundled-libffi` will only work if you are
+using a binary distribution of GHC that was not configured with the
+`--with-system-libffi` option. If you _are_ using such a GHC, then you will
+need to use `-ghc-bundle-libffi` (note the minus sign) to disable the flag and
+link against your operating system's copy of `libffi`. Unfortunately, `cabal`
+does not provide a way to detect whether GHC was configured with
+`--with-system-libffi` or not, so the burden is on users to enable or disable
+`ghc-bundle-libffi` as appropriate.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple
+main = defaultMain
libffi.cabal view
@@ -1,63 +1,63 @@-Name:               libffi-cabal-version:      >= 1.10-Version:            0.2-Description:        A binding to libffi, allowing C functions of types only known at runtime to be called from Haskell.-License:            BSD3-License-file:       LICENSE-Copyright:          Remi Turk 2008-2009-Author:             Remi Turk-Maintainer:         remi.turk@gmail.com-Homepage:           http://haskell.org/haskellwiki/Library/libffi-Stability:          alpha-Synopsis:           A binding to libffi-Tested-With:        GHC == 7.0.4-                  , GHC == 7.2.2-                  , GHC == 7.4.2-                  , GHC == 7.6.3-                  , GHC == 7.8.4-                  , GHC == 7.10.3-                  , GHC == 8.0.2-                  , GHC == 8.2.2-                  , GHC == 8.4.4-                  , GHC == 8.6.5-                  , GHC == 8.8.4-                  , GHC == 8.10.7-                  , GHC == 9.0.2-                  , GHC == 9.2.2-extra-source-files: CHANGELOG.md, README.md-Build-Type:         Simple-Category:           Foreign--flag ghc-bundled-libffi-  description:      When GHC is configured without @--with-system-libffi@, it-                    will bundle its own copies of @libffi@, one of them-                    statically linked and the other dynamically linked. This-                    flag will force linking against the static copy of @libffi@-                    that GHC bundles. This avoids a GHC bug-                    (https://gitlab.haskell.org/ghc/ghc/-/issues/15397) that-                    can arise when the linker confuses the system's dynamic-                    @libffi@ with GHC's own dynamic @libffi@.--                    Note that this flag only works when GHC is configured-                    without the @--with-system-libffi@ option. This is the case-                    for most GHC binary distributions, such as those provided-                    by @ghcup@. If you are using a GHC that was configured with-                    @--with-system-libffi@, however, you will need to disable-                    this option and link against the system's version of-                    @libffi@ instead.-  default:          True--source-repository head-  type:                git-  location:            https://github.com/remiturk/libffi--library-  build-depends:       base >= 3 && < 5, bytestring-  exposed-modules:     Foreign.LibFFI,-                       Foreign.LibFFI.Base,-                       Foreign.LibFFI.Types,-                       Foreign.LibFFI.FFITypes,-                       Foreign.LibFFI.Internal-  if !flag(ghc-bundled-libffi)-    pkgconfig-depends: libffi-  default-language:    Haskell2010+Name:               libffi
+cabal-version:      >= 1.10
+Version:            0.2.1
+Description:        A binding to libffi, allowing C functions of types only known at runtime to be called from Haskell.
+License:            BSD3
+License-file:       LICENSE
+Copyright:          Remi Turk 2008-2009
+Author:             Remi Turk
+Maintainer:         remi.turk@gmail.com
+Homepage:           http://haskell.org/haskellwiki/Library/libffi
+Stability:          alpha
+Synopsis:           A binding to libffi
+Tested-With:        GHC == 7.0.4
+                  , GHC == 7.2.2
+                  , GHC == 7.4.2
+                  , GHC == 7.6.3
+                  , GHC == 7.8.4
+                  , GHC == 7.10.3
+                  , GHC == 8.0.2
+                  , GHC == 8.2.2
+                  , GHC == 8.4.4
+                  , GHC == 8.6.5
+                  , GHC == 8.8.4
+                  , GHC == 8.10.7
+                  , GHC == 9.0.2
+                  , GHC == 9.2.2
+extra-source-files: CHANGELOG.md, README.md
+Build-Type:         Simple
+Category:           Foreign
+
+flag ghc-bundled-libffi
+  description:      When GHC is configured without @--with-system-libffi@, it
+                    will bundle its own copies of @libffi@, one of them
+                    statically linked and the other dynamically linked. This
+                    flag will force linking against the static copy of @libffi@
+                    that GHC bundles. This avoids a GHC bug
+                    (https://gitlab.haskell.org/ghc/ghc/-/issues/15397) that
+                    can arise when the linker confuses the system's dynamic
+                    @libffi@ with GHC's own dynamic @libffi@.
+
+                    Note that this flag only works when GHC is configured
+                    without the @--with-system-libffi@ option. This is the case
+                    for most GHC binary distributions, such as those provided
+                    by @ghcup@. If you are using a GHC that was configured with
+                    @--with-system-libffi@, however, you will need to disable
+                    this option and link against the system's version of
+                    @libffi@ instead.
+  default:          True
+
+source-repository head
+  type:                git
+  location:            https://github.com/remiturk/libffi
+
+library
+  build-depends:       base >= 3 && < 5, bytestring
+  exposed-modules:     Foreign.LibFFI,
+                       Foreign.LibFFI.Base,
+                       Foreign.LibFFI.Types,
+                       Foreign.LibFFI.FFITypes,
+                       Foreign.LibFFI.Internal
+  if !flag(ghc-bundled-libffi)
+    pkgconfig-depends: libffi
+  default-language:    Haskell2010