3

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:
  glasgow-haskell-bugs@haskell.org

*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:
  glasgow-haskell-bugs@haskell.org

*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?)

  • I can only tell you two unhelpful things: 1. This works in Linux just fine using `stdio.h getchar`, without needing to specify a library, and 2. your approach looks approximately correct. – crockeea Jul 10 '16 at 01:34
  • @Eric In Linux, there is no need for FFI in this case, since hSetBuffering function works fine, and by using that function, I can make _Bufferless Input_. However, this approach wasn't work for Windows. – Junyoung Clare Jang Jul 10 '16 at 13:02
  • I was referring only to your main question of trying to link with `getChar`. I can't help you on the buffering problem. – crockeea Jul 10 '16 at 18:28
  • 1
    w.r.t ticket #2189, there is ongoing work to slowly replace things like the IO manager in GHC with native Windows ones. But it's slow since there are not a lot of Windows maintainers. I currently have to prioritize to tickets which affect the most people. – Phyx Aug 06 '16 at 08:11

1 Answers1

1

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

Phyx
  • 2,697
  • 1
  • 20
  • 35
  • Then what is the '-l' option in `ghci --show-options`? And GHCi user guide said "Extra libraries may be specified on the command line using the normal -llib option." [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#extra-libraries] – Junyoung Clare Jang Aug 06 '16 at 14:01
  • @JunyoungClareJang `-l` is used to indicate to the linker that you have a dependency on a library. Libraries come in different forms, You have static ones commonly with `.a` extensions, dynamic `.dll` or import libraries `.dll.a`, `.a` and .`.lib`. when you specify `-lfoo` you're telling the linker you need library `foo`. If you have the library in multiple versions e.g. libfoo.dll, libfoo.a, libfoo.lib, which one the linker uses depends on the various flags and search priorities. As I said before, GHCi currently will always prefer the dynamic version over any and all static ones. – Phyx Aug 06 '16 at 14:28
  • 1
    These don't magically work, we have to actively write code to support them in GHCi. So some features are not supported yet as the Haskell runtime loader (RTS) doesn't support them yet. As I said, support for more is actively being added, such as import library support in 8.0.2 https://phabricator.haskell.org/D1696 – Phyx Aug 06 '16 at 14:33
  • Also I should mention, symbol resolution is lazy, e.g we pick the first one we encounter (well, it's slightly more complicated than that but not important). GHCi has a dependency on the C runtime. On Windows this is msvcrt.dll, so it's already there by default, specifying it again will do nothing :) – Phyx Aug 06 '16 at 14:45