使用 FFI 声明时,GHCi 运行时链接器出现问题

50

我有一个关于Haskell中FFI和GHC交互模式的问题,请看这里

考虑FFISo.hs

{-# LANGUAGE OverloadedStrings #-}
module Main where

import qualified Data.ByteString.Char8 as B

import FFIFun.Foo

main :: IO ()
main = do
  B.putStrLn "main"
  callMeFromC
  callMeFromHaskell
  return ()

c.c:

#include <stdio.h>

void callMeFromC(void);

void callMeFromHaskell(void)
{
    printf("callMeFromHaskell\n");
    callMeFromC();
}

FFIFun/Foo.hs:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module FFIFun.Foo where

import qualified Data.ByteString.Char8 as B

foreign import ccall "callMeFromHaskell"
  callMeFromHaskell :: IO ()

foreign export ccall callMeFromC :: IO ()
callMeFromC :: IO ()
callMeFromC = B.putStrLn "callMeFromC"

还需要一个 Makefile

SHELL := bash

GHC_OPT := -Wall -O2 -fno-warn-unused-do-bind


all: ffiso

test: ffiso
    ./$<

ffiso: FFISo.hs c.c
    ghc --make $(GHC_OPT) $^ -o $@

clean:
    rm -rf *{.hi,o,_stub.*} ffiso FFIFun/*{.hi,.o,_stub.*}

ghci: ffiso
    ghci -package bytestring FFIFun/Foo.o c.o FFISo.hs

你也可以在这里找到它。

那么,我的问题是:

$ make ghci
[...]
Ok, modules loaded: Main, FFIFun.Foo.
Prelude Main> -- fine, it's loading.
Prelude Main> :t callMeFromC

<interactive>:1:1: Not in scope: `callMeFromC'
Prelude Main> -- uhm, why?
Prelude Main> :t main
main :: IO ()
Prelude Main> main


GHCi runtime linker: fatal error: I found a duplicate definition for symbol
   FFIFunziFoo_callMeFromC_info
whilst processing object file
   ./FFIFun/Foo.o
This could be caused by:
   * Loading two different object files which export the same symbol
   * Specifying the same object file twice on the GHCi command line
   * An incorrect `package.conf' entry, causing some object to be
     loaded twice.
GHCi cannot safely continue in this situation.  Exiting now.  Sorry.

哎呀,这里出了什么问题?有趣的是,在i686上我得到了一个不同的错误(上面是一个x86_64系统,但两者都是GHC 7.4.1):

GHCi runtime linker: fatal error: I found a duplicate definition for symbol
   __stginit_FFIFunziFoo
whilst processing object file
   ./FFIFun/Foo.o
This could be caused by:
   * Loading two different object files which export the same symbol
   * Specifying the same object file twice on the GHCi command line
   * An incorrect `package.conf' entry, causing some object to be
     loaded twice.
GHCi cannot safely continue in this situation.  Exiting now.  Sorry.

此外,有没有相关的文档资料可供参考?我感觉自己是唯一一个在使用FFI和GHCi时遇到困难的人。
编辑: 请注意,make test 运行正常。
$ ghc --make -Wall -O2 -fno-warn-unused-do-bind FFISo.hs c.c -o ffiso
[1 of 2] Compiling FFIFun.Foo       ( FFIFun/Foo.hs, FFIFun/Foo.o )
[2 of 2] Compiling Main             ( FFISo.hs, FFISo.o )
Linking ffiso ...
./ffiso
main
callMeFromC
callMeFromHaskell
callMeFromC

如果您尝试将一个模块进行静态链接并且又动态加载它,可能会导致一些问题。 - Don Stewart
你能详细说明一下吗?或者有什么方法可以解决这个问题吗? - h_s
3
我稍微尝试了一下,似乎是GHCi中的一个BUG。该符号被查看了两次:因为它通过.o文件在命令行上加载,以及因为.hs文件被加载。也许当启动ghci时不应该链接它,只有执行东西时才链接它? - Joachim Breitner
感谢您的评论。与此同时,我已经从7.4.1升级到7.4.2,现在收到了不同的错误信息,在x86_64上甚至没有提示(但在x86上有)。叹气。奇怪的是,我在我的项目中解决了这个问题(http://wien.tomnetworks.com/gitweb/?p=mate.git;a=summary 仅限于x86,请参见makefile),但我无法在上面的gist中解决它。这真的很奇怪,可能我应该报告一个GHC错误或其他什么东西。 - lewurm
请参考以下链接:https://dev59.com/wGrWa4cB1Zd3GeqP91ny,https://dev59.com/nk_Ta4cB1Zd3GeqPBYEj和http://stackoverflow.com/questions/5388894/runhaskell-error-on-ghc7-02。 - Don Stewart
1个回答

3
这是GHCi字节码解释器中动态链接目标文件的已知限制
如果您加载编译代码,该代码是静态链接到给定C对象的,然后还通过FFI在现场解释一些Haskell,也引用了同一个C对象,则运行时链接器将被迫动态加载C对象。
现在您的地址空间中有两个版本的C符号,并且会出现故障。
您必须在GHCi模式下解释所有内容,或放弃使用GHCi进行此过程。对于某些操作系统链接器,您可以通过动态表公开静态链接的符号表( -x 标志)。

这是你对类似问题的另一个回答。很好的回答。虽然我无法弄清楚在使用GHC api和dynCompileExpr时设置-x标志会涉及什么。我猜在调用setSessionDynFlags时为ghcLink设置一些值可能会做到这一点,但迄今为止没有成功。也许需要为“父”会话设置标志。 - worldsayshi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接