Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Foreign Function Interface with GHCI in Windows

First of all, I specify that I use Windows 10 64bit and Haskell Platform 8.0.1.

I try to use FFI of Haskell in Windows using following code.

import Control.Monad
import Data.Char
import Foreign.C

getCh :: IO Char
getCh = liftM (chr . fromEnum) c_getch
foreign import ccall unsafe "conio.h getch" c_getch :: IO CInt

main :: IO ()
main = getCh >>= \x -> print x

After this, I can compile this well with ghc

> ghc Examples.hs
[1 of 1] Compiling Main             ( Examples.hs, Examples.o )
Linking Examples.exe ...

and it runs completely.

> Examples.exe
'1'

(When I type 1 after running it)

However, the problem occurs with GHCI. When I load it to ghci, I got these messages.

> ghci Examples.hs
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( Examples.hs, interpreted )
Ok, modules loaded: Main.
*Main> main

ByteCodeLink: can't find label
During interactive linking, GHCi couldn't find the following symbol:
  getch
This may be due to you not asking GHCi to load extra object files,
archives or DLLs needed by your current session.  Restart GHCi, specifying
the missing library using the -L/path/to/object/dir and -lmissinglibname
flags, or simply by naming the relevant files on the GHCi command line.
Alternatively, this link failure might indicate a bug in GHCi.
If you suspect the latter, please send a bug report to:
  [email protected]

*Main>

I try to load "missing library", such as "-lmsvcrt" which needs to use conio.h, but result is pessimistically same.

> ghci -lmsvcrt Examples.hs
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( Examples.hs, interpreted )
Ok, modules loaded: Main.
*Main> main

ByteCodeLink: can't find label
During interactive linking, GHCi couldn't find the following symbol:
  getch
This may be due to you not asking GHCi to load extra object files,
archives or DLLs needed by your current session.  Restart GHCi, specifying
the missing library using the -L/path/to/object/dir and -lmissinglibname
flags, or simply by naming the relevant files on the GHCi command line.
Alternatively, this link failure might indicate a bug in GHCi.
If you suspect the latter, please send a bug report to:
  [email protected]

*Main>

GHCI probably loads the library, since when I try to load a wrong library, ghci prints errors about that.

I try several other things, like using ghci Examples.hs -fobject-code, ghci -lmsvcrt Examples.hs -fobject-code, and even

ghci Examples.hs "-luser32" "-lgdi32" "-lwinmm" "-ladvapi32" "-lshell32"
"-lshfolder" "-lwsock32" "-luser32" "-lshell32" "-lmsvcrt" "-lmingw32" 
"-lmingwex" "-luser32" "-lmingw32" "-lmingwex" "-lm" "-lwsock32" "-lgdi32" "-lwinmm"

Which was found in ghc Examples.hs -v5.

Sadly, Nothing works for my main, and I can't find any otherway for this.

P.S. Is there anyone knowing how to use hSetBuffering in Windows (It was posted at 8 years ago in ghc ticket #2189. Isn't it fixed?)

like image 494
Junyoung Clare Jang Avatar asked Dec 08 '25 05:12

Junyoung Clare Jang


1 Answers

This is because there is no getch on Windows. getch is POSIX and POSIX has been deprecated on Windows. It's still around but the functions have been moved to a different namespace (to free up the root namespace to user programs). As you can see MSDN says getch is deprecated https://msdn.microsoft.com/en-us/library/ms235446.aspx and to use _getch instead.

import Control.Monad
import Data.Char
import Foreign.C

getCh :: IO Char
getCh = liftM (chr . fromEnum) c_getch
foreign import ccall unsafe "conio.h _getch" c_getch :: IO CInt

main :: IO ()
main = getCh >>= \x -> print x

Will work both compiled and interpreted.

As to the why it works compiled and not interpreted when using getch:

The MingW-w64 project has never removed deprecated functions as Microsoft has

As such

$ objdump -t /home/Tamar/ghc2/inplace/mingw/x86_64-w64-mingw32/lib/libmsvcr120.a | grep getch
[  7](sec  1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 getch
[  8](sec  5)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 __imp_getch
[  7](sec  1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 _getch
[  8](sec  5)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x0000000000000000 __imp__getch

getch is being redirected to _getch and so they have both versions. This is a source of incompatibility between MSVC++ and GCC.

Microsoft however has removed them

>dumpbin /exports C:\Windows\System32\msvcr120.dll | findstr getch
        699  2BA 0006B8B4 _getch = _getch

So what happens, when you link against msvcrt:

  1. In compiled mode both GCC and GHC will pick the static library first which happens to be an import lib libmsvcrt.dll.a. This is due to the link order of the linker (ld).

  2. In interpreted mode, GHCi will always prefer the dynamic version of the library over the static one. The reason is during re-linking (which has to happen when you introduce a new scope or reload) dynamic libraries are much faster as we don't have to internally do relocations and symbol resolving. There are also things we still don't support properly like weak symbols or common symbols, so for these reasons we just prefer the dynamic one.

  3. GHCi 8.0.1 does not support import libraries. So while you can force GHCi to use the static library (just give the full name to -l, e.g. -llibmsvcr.a) It won't work because the runtime loader doesn't know what to do with it. This is however supported in the current GIT master and will likely be in 8.0.2

like image 150
Phyx Avatar answered Dec 10 '25 19:12

Phyx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!