Подтвердить что ты не робот

Вызов динамически связанного кода Haskell с Rust

Я пытаюсь скомпилировать код Rust с некоторым кодом Haskell. У меня есть тестовая система с файлом Fibonacci.hs с функцией, которая вычисляет числа фибоначчи в Haskell и экспортирует функцию как fibonacci_hs через Haskell FFI (как здесь: https://github.com/nh2/haskell-from-python, хотя я скопирую и вставляю внизу), а в wrapper.c определили функции экспорта для вызова для инициализации и выхода из Haskell RTS.

Код выглядит следующим образом:

{- Fibonacci.hs -}
{-# LANGUAGE ForeignFunctionInterface #-}

module Fibonacci where

import Foreign.C.Types

fibonacci :: Int -> Int
fibonacci n = fibs !! n
    where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

fibonacci_hs :: CInt -> CInt
fibonacci_hs = fromIntegral . fibonacci . fromIntegral

foreign export ccall fibonacci_hs :: CInt -> CInt

// wrapper.c

#include <stdlib.h>
#include "HsFFI.h"

void
example_init (void)
{
  hs_init (NULL, NULL);
}

void
example_exit (void)
{
  hs_exit ();
}

Я скомпилирую их через:

ghc -c -dynamic -fPIC Fibonacci.hs

ghc -c -dynamic -fPIC wrapper.c

и я связываю объекты в общую/динамическую библиотеку (подробнее об этом через секунду) через:

ghc -o libfibonacci.so -shared -dynamic -fPIC Fibonacci.o wrapper.o -lHSrts

При запуске кода примера Python из связанного репозитория он отлично работает на моем mac, но я не могу связать его с Rust.

В Rust мой код выглядит примерно так:

//main.rs
#[link(name = "fibonacci")]
extern {
    fn fibonacci_hs (n : i32); // c_int = i32
    fn fib_init (); // start hs rts
    fn fib_exit (); // kill hs rts
}

fn main () {
    unsafe {
        fib_init();
        for i in 0..100 {
            println!("{:?}th fibonacci : {:?}", i, fibonacci_hs(i));
        }
        fib_exit();
    }
}

И я компилирую с rustc main.rs -L . (так как файл общей библиотеки является локальным).

Ошибка, которую я генерирую на Mac, при компиляции с динамической библиотекой (ghc -o libfibonacci.so -shared -static haskell/Fibonacci.o haskell/wrapper.o -lHSrts, затем rustc main.rs -L.) находится во время выполнения:

dyld: Symbol not found: _ffi_call
  Referenced from: ./libfibonacci.so
  Expected in: flat namespace
 in ./libfibonacci.so
Trace/BPT trap: 5

Спасибо за любую помощь заранее.

4b9b3361

Ответ 1

Когда вы компилируете свою общую библиотеку, похоже, вам нужно также установить ссылку на libffi:

ghc -o libfibonacci.dylib -shared -dynamic -fPIC \
  Fibonacci.hs wrapper.c -lHSrts -lffi

Я вывел это, перейдя в мой каталог библиотеки GHC (/usr/local/lib/ghc-7.10.1/rts), а затем grepping для символа ffi_call:

$ grep -lRa ffi_call .
./include/ffi.h
./rts/libHSrts-ghc7.10.1.dylib
...

Затем я использовал nm, чтобы найти, какая именно библиотека имела его:

for i in *dylib; do
   if nm $i | grep -q 'T.*ffi_call'; then
       echo "== $i";
   fi;
done

Тогда мне удалось запустить:

DYLD_LIBRARY_PATH='.' ./main

К сожалению, кажется, что ваш код не совсем прав, поскольку я просто получаю кучу пустых кортежей. Вы забыли использовать возвращаемый тип функции, а затем вы столкнулись с проблемой, что 46-й или около того Фибоначчи слишком велик для u32.

Кроме того, вы должны использовать типы из пакета libc, и может быть безопаснее использовать u64 здесь.

Я установил GHC 7.10.1 с помощью Homebrew, но, надеюсь, такая же модель будет работать в другом месте.

Ответ 2

Вы упомянули две разные команды конечной ссылки,

ghc -o libfibonacci.so -shared -dynamic -fPIC Fibonacci.o wrapper.o -lHSrts

и

ghc -o libfibonacci.so -shared -static haskell/Fibonacci.o haskell/wrapper.o -lHSrts

Возможно, стоит явно описать, что означают некоторые из этих флагов.

  • -shared указывает ghc создать общий объект (а не исполняемый файл).

  • -dynamic указывает ghc связать вывод с динамическими библиотечными версиями своих зависимостей Haskell (base, ghc-prim и т.д.)

  • -static - это противоположность -dynamic, он сообщает ghc ссылаться на версии статической библиотеки зависимостей Haskell.

  • -lHSrts означает ссылку на (статическую или общую) библиотеку libHSrts. Но в GHC, только статическая библиотека фактически имеет basename libHSrts (так что имя файла библиотеки libHSrts.a). Версия общей библиотеки имеет имя файла libHSrts-ghc7.8.4.so (скорректируйте вашу версию GHC). Таким образом, -lHSrts действительно означает ссылку на статическую библиотечную версию RTS.

Таким образом, вторая команда связывается со статическими версиями всех зависимостей Haskell, включая RTS. Это может работать на OS X, где весь код должен быть сгенерирован как PIC, но он не будет работать на обычном бинарном дистрибутиве GHC Linux, поскольку разделяемая библиотека должна быть PIC-кодом, но статические библиотеки Haskell, поставляемые с GHC, -PIC (они должны быть связаны с неперемещаемыми исполняемыми файлами). Я не совсем понимаю, почему GHC недостаточно умен, чтобы добавить здесь -lffi, возможно, на самом деле он не ожидает этой комбинации опций, поскольку он не будет работать с нормальной настройкой Linux.

Первая команда нечетна, потому что вы статически ставите ссылку на RTS, но динамически против всех других зависимостей Haskell. Если вы измените имя библиотеки в опции -l на -lHSrts-ghc7.8.4, тогда все будет работать только в Linux и, возможно, везде (кроме Windows).