如何将Haskell编译成静态库?

发布于 2024-10-19 19:11:25 字数 2029 浏览 4 评论 0原文

嘿, 我正在学习 Haskell,我有兴趣使用它来制作静态库,以便在 Python 和 C 中使用。经过一番谷歌搜索后,我发现如何让 GHC 输出共享对象,但它动态地依赖于 GHC 的库。 在 GHC 中编译生成的 ELF 是动态依赖的,仅依赖于 C 库,并且大小略低于 1 MB - 它已与 GHC 的库静态链接。对于共享对象如何以及是否可以实现这一点?

当前状态的示例:

$ ghc --make -dynamic -shared -fPIC foo.hs -o libfoo.so
$ ldd libfoo.so
    linux-vdso.so.1 =>  (0x00007fff125ff000)
    libHSbase-4.2.0.2-ghc6.12.3.so => /usr/lib/ghc-6.12.3/base-4.2.0.2/libHSbase-4.2.0.2-ghc6.12.3.so (0x00007f7d5fcbe000)
    libHSinteger-gmp-0.2.0.1-ghc6.12.3.so => /usr/lib/ghc-6.12.3/integer-gmp-0.2.0.1/libHSinteger-gmp-0.2.0.1-ghc6.12.3.so (0x00007f7d5faac000)
    libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007f7d5f816000)
    libHSghc-prim-0.2.0.0-ghc6.12.3.so => /usr/lib/ghc-6.12.3/ghc-prim-0.2.0.0/libHSghc-prim-0.2.0.0-ghc6.12.3.so (0x00007f7d5f591000)
    libHSffi-ghc6.12.3.so => /usr/lib/ghc-6.12.3/libHSffi-ghc6.12.3.so (0x00007f7d5f383000)
    libc.so.6 => /lib/libc.so.6 (0x00007f7d5f022000)
    /lib/ld-linux-x86-64.so.2 (0x00007f7d60661000)

$ ghc foo.hs
$ ldd foo
    linux-vdso.so.1 =>  (0x00007fff2d3ff000)
    libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007f50014ec000)
    libm.so.6 => /lib/libm.so.6 (0x00007f5001269000)
    librt.so.1 => /lib/librt.so.1 (0x00007f5001061000)
    libdl.so.2 => /lib/libdl.so.2 (0x00007f5000e5d000)
    libc.so.6 => /lib/libc.so.6 (0x00007f5000afc000)
    libpthread.so.0 => /lib/libpthread.so.0 (0x00007f50008df000)
    /lib/ld-linux-x86-64.so.2 (0x00007f5001759000)

如果我尝试使用(不带“-dynamic”)编译它:

$ ghc --make -shared -fPIC foo.hs -o libfoo.so
Linking libfoo.so ...
/usr/bin/ld: foo.o: relocation R_X86_64_32S against `stg_CAF_BLACKHOLE_info' can not be used when making a shared object; recompile with -fPIC
foo.o: could not read symbols: Bad value
collect2: ld returned 1 exit status

当谷歌搜索时,我发现了有关整个问题的一些信息 - 它可能来自 GHC 以特定方式编译的事实(动态/静态? ),因此静态链接是不可能的。如果这是真的,ELF 二进制文件怎么可能是静态链接的?

不管怎样,我希望有人能对此有所了解,因为大量的谷歌搜索给我留下了比开始时更多的问题。

非常感谢。

Hey,
I'm learning Haskell and I'm interested in using it to make static libraries for using in Python and probably C. After some googling I found out how to get GHC to output a shared object, but it dynamically depends on GHC`s libraries.
The resulting ELF from compiling in GHC is dynamically dependand only on C libs and it's a bit under a MB in size - it has been statically linked with GHC`s libs. How and if can this be achieved for shared objects?

Example of current state:

$ ghc --make -dynamic -shared -fPIC foo.hs -o libfoo.so
$ ldd libfoo.so
    linux-vdso.so.1 =>  (0x00007fff125ff000)
    libHSbase-4.2.0.2-ghc6.12.3.so => /usr/lib/ghc-6.12.3/base-4.2.0.2/libHSbase-4.2.0.2-ghc6.12.3.so (0x00007f7d5fcbe000)
    libHSinteger-gmp-0.2.0.1-ghc6.12.3.so => /usr/lib/ghc-6.12.3/integer-gmp-0.2.0.1/libHSinteger-gmp-0.2.0.1-ghc6.12.3.so (0x00007f7d5faac000)
    libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007f7d5f816000)
    libHSghc-prim-0.2.0.0-ghc6.12.3.so => /usr/lib/ghc-6.12.3/ghc-prim-0.2.0.0/libHSghc-prim-0.2.0.0-ghc6.12.3.so (0x00007f7d5f591000)
    libHSffi-ghc6.12.3.so => /usr/lib/ghc-6.12.3/libHSffi-ghc6.12.3.so (0x00007f7d5f383000)
    libc.so.6 => /lib/libc.so.6 (0x00007f7d5f022000)
    /lib/ld-linux-x86-64.so.2 (0x00007f7d60661000)

$ ghc foo.hs
$ ldd foo
    linux-vdso.so.1 =>  (0x00007fff2d3ff000)
    libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007f50014ec000)
    libm.so.6 => /lib/libm.so.6 (0x00007f5001269000)
    librt.so.1 => /lib/librt.so.1 (0x00007f5001061000)
    libdl.so.2 => /lib/libdl.so.2 (0x00007f5000e5d000)
    libc.so.6 => /lib/libc.so.6 (0x00007f5000afc000)
    libpthread.so.0 => /lib/libpthread.so.0 (0x00007f50008df000)
    /lib/ld-linux-x86-64.so.2 (0x00007f5001759000)

If I try to compile it with(without '-dynamic'):

$ ghc --make -shared -fPIC foo.hs -o libfoo.so
Linking libfoo.so ...
/usr/bin/ld: foo.o: relocation R_X86_64_32S against `stg_CAF_BLACKHOLE_info' can not be used when making a shared object; recompile with -fPIC
foo.o: could not read symbols: Bad value
collect2: ld returned 1 exit status

When googling I found something about this whole issue - that it may come from the fact that GHC is compiled in a specific way(dynamic/static?) and so static linking is not possible. If this is true how is it possible that the ELF binary is statically linked?

Anyway, I am hoping someone can shed some light on this since a huge amount of googling left me with more questions than I started with.

Huge thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

白况 2024-10-26 19:11:25

规范的方法是这样的:

  1. 导出函数(通过 FFI)以通过外部程序初始化 RTS(运行时系统)
  2. 导出您想要在 Haskell 中实现的实际函数

手册的以下部分对此进行了描述: [1] [2]

另一方面,您可以尝试这篇博文中描述的技术(顺便说一下,这是我的):

http://mostlycode.wordpress.com/2010/01/03/shared-haskell-so-library-with-ghc-6-10-4- and-cabal/

它归结为创建一个小的 C 文件,该文件在加载库后立即自动调用。
它应该链接在一起进入库。

#define CAT(a,b) XCAT(a,b)
#define XCAT(a,b) a ## b
#define STR(a) XSTR(a)
#define XSTR(a) #a

#include

extern void CAT (__stginit_, MODULE) (void);

static void library_init (void) __attribute__ ((constructor));
static void
library_init (void)
{
      /* This seems to be a no-op, but it makes the GHCRTS envvar work. */
      static char *argv[] = { STR (MODULE) ".so", 0 }, **argv_ = argv;
      static int argc = 1;

      hs_init (&argc, &argv_);
      hs_add_root (CAT (__stginit_, MODULE));
}

static void library_exit (void) __attribute__ ((destructor));
static void
library_exit (void)
{
    hs_exit ();
}

编辑:描述此技术的原始博客文章是这样的: http:// /weblog.haskell.cz/pivnik/building-a-shared-library-in-haskell/

The canonical way of is this:

  1. Export the functions (via FFI) to initialise RTS (runtime system) by the foreign program
  2. Export actual functions you would like to implement in Haskell

The following sections of manual describe this: [1] [2]

On the other way, you can try technique described in this blog post (which mine, by the way):

http://mostlycode.wordpress.com/2010/01/03/shared-haskell-so-library-with-ghc-6-10-4-and-cabal/

It boils down to creating a small C file which is called automatically right after a library is loaded.
It should be linked together into the library.

#define CAT(a,b) XCAT(a,b)
#define XCAT(a,b) a ## b
#define STR(a) XSTR(a)
#define XSTR(a) #a

#include

extern void CAT (__stginit_, MODULE) (void);

static void library_init (void) __attribute__ ((constructor));
static void
library_init (void)
{
      /* This seems to be a no-op, but it makes the GHCRTS envvar work. */
      static char *argv[] = { STR (MODULE) ".so", 0 }, **argv_ = argv;
      static int argc = 1;

      hs_init (&argc, &argv_);
      hs_add_root (CAT (__stginit_, MODULE));
}

static void library_exit (void) __attribute__ ((destructor));
static void
library_exit (void)
{
    hs_exit ();
}

Edit: Original blog post describing this technique is this: http://weblog.haskell.cz/pivnik/building-a-shared-library-in-haskell/

煮茶煮酒煮时光 2024-10-26 19:11:25

这使得 ghc 静态编译(注意 pthread 在 optl-static 之前):
ghc --make -static -optl-pthread -optl-static test.hs

编辑:但是静态编译似乎有点冒险。大多数时候都会有一些错误。在我的 x64 fedora 上它根本不起作用。生成的二进制文件也相当大,main = putStrLn "hello world" 为 1.5M

This makes ghc compile statically (note that the pthread is before optl-static):
ghc --make -static -optl-pthread -optl-static test.hs

Edit: But the static compilation seems to be a bit risky. Most of the times there are some errors. And on my x64 fedora it doesn't work at all. The resulting binary is also quite large, 1.5M for main = putStrLn "hello world"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文