Haskell 动态库

Haskell dynamic library

http://www.vex.net/~trebla/haskell/so.xhtml描述了如何编译共享库。

关于编译命令:

ghc -O2 -dynamic -shared -fPIC -o libEval.so Eval.hs hsbracket.c -lHSrts-ghc7.6.3

它说:

(Could you omit -dynamic to request static libraries of other packages? Not really, they were not generated with -fPIC. In particular it is illegal on x86_64.)

为什么会这样?在没有 libHS* 依赖项的情况下编译共享库应该怎么做?

既然Kaiko私下联系我解答,不妨post这里...

简短版

通过省略 -dynamic,您将尝试将所有静态 .a 库和 link 它们放入一个巨大的 .so 文件中。那些 .a 库本身是在没有 -fPIC 的情况下构建的。所有以 .so 文件结束的代码都必须使用 -fPIC 构建(至少在 ELF x86-64 上)。因此在这种情况下 linking 会失败,因为需要 -fPIC 但库不是用 -fPIC 构建的。

长版

不同的构建方式之间存在一些差异 静态和动态库:

  1. 它是构建为 .a 存档还是构建为 .so(或 .dll/.dynlib)对象?
  2. 它是否使用-fPIC 构建,位置无关代码?
  3. 外部符号是希望在同一个 DSO 中还是在外部 DSO 中?

原则上,这些东西的许多不同组合是有意义的 但实际上只有少数被使用。

在 Linux (ELF) 上,有两种构建库的标准方法, 全静态和全动态。在完全静态的方法中答案 上面的问题 1、2、3 是:.a 存档,没有 -fPIC,相同的 DSO。在里面 完全动态的方法答案是:.so lib、-fPIC、外部 DSO。

现在你想做的不一样了。您希望构建所有库 作为 .a 文件,但带有 -fPIC 和预期位于 同一个 DSO。这会让你 link 所有这些库一起进入 一个巨大的共享库。所以关键的区别是使用-fPIC, 因为在 ELF(特别是 x86_64)上,代码最终出现在共享库中 必须使用 -fPIC 构建。

相比之下,在 Windows 上,GHC 可以做你想做的,link 所有 Haskell 库(包括 RTS 等)到一个巨大的共享库中 (.dll)。这是因为在 Windows 上(不像 ELF),位置无关 代码无关紧要。所以在 Windows 上,可以采取静态 图书馆和link他们进入一个大的共享图书馆。

原则上这在 Linux 上也应该是可能的,如果所有 Haskell 库是静态构建的,但带有 -fPIC。这不是 默认值,这是您不能省略 -dynamic 的直接原因 在这种情况下 Linux.

所以原则上,可以尝试重建 ghc 和核心库 使用 -fPIC 标志从源代码开始,然后查看它是否可以忽略 -dynamic 和 link 一切都在一个巨大的共享库中。

是的,使用 -fPIC 进行编译会有所帮助。这是如何做到这一点。

ghc-7.8.4/mk/build.mk:

 SRC_HC_OPTS     = -H64m -O 
 EXTRA_HC_OPTS   = -fPIC    
 SRC_CC_OPTS     = -fPIC -O 
 GhcStage1HcOpts = -fasm -O0
 GhcStage2HcOpts = -fasm -O0
 GhcLibHcOpts    = -fasm -O2
 GhcLibWays      = v dyn    
 DYNAMIC_GHC_PROGRAMS = YES 
 DYNAMIC_BY_DEFAULT   = NO  
 SplitObjs            = NO  
 HADDOCK_DOCS         = NO  
 BUILD_DOCBOOK_HTML   = NO  
 BUILD_DOCBOOK_PS     = NO  
 BUILD_DOCBOOK_PDF    = NO  

编译ghc时:

export EXTRA_CONFIGURE_OPTS="--disable-library-profiling --enable-shared"

要使用 -fPIC 构建 cabal 包,请使用:

cabal install --enable-shared --ghc-option=-fPIC text

测试文件foo.hs(Data.Text 用于查看 cabal 包是否也有效):

import Foreign.C as C 
import Data.Text as T
import Data.Text.Foreign as T 
foreign export ccall   len :: CString -> IO CInt 
len t = C.peekCString t >>= return . CInt . fromIntegral . T.length . T.pack  
main = return ()

动态构建:

ghc -dynamic --make foo.hs

动态混合静态构建(不确定是否需要 pthread 但它说明了如何添加动态链接):

ghc -fPIC -shared --make -o libfoo.so \
 -optl-Wl,-Bstatic -lHSrts -lCffi \
   -lHSbase-4.7.0.2 -lHSinteger-gmp-0.5.1.0 -lHSghc-prim-0.3.1.0 \
-optl-Wl,-Bdynamic -lpthread foo.hs