ghci 6.6 foreign import stdcall broken, panic, panic!!!!

john lask jvlask at hotmail.com
Sat Apr 21 05:09:46 EDT 2007


ghci 6.6 foreign import stdcall broken, panic, panic!!!!

Hello

I would like to report what I think is a problem with GHCI foreign function 
imports of c functions
declared with stdcall calling convention (1) and also with loading objects 
containing dll imports (2).

I will attempt to log this in ghc trac.

What is the problem ?
----------------------
(1)
Prelude> :load htest3
[1 of 1] Compiling Main             ( htest3.hs, interpreted )

During interactive linking, GHCi couldn't find the following symbol:
  test
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 at haskell.org

(2)
>ghci -fglasgow-exts test_proxy5a.o -ltest3b

test_proxy5a.o: unknown symbol `__imp__test'
final link ... : linking extra libraries/objects failed


detail follows ...

CONCLUSSION REACHED
--------------------
(1) ghci handling of mangling of stdcall function names broken.
(2) ghci loading of c object files dll imports name resolution broken.

(1) ghci dose not correctly look for symbols decorated in the stdcall manner 
in the object files
ie. it looks for _test when it should (according to stdcall convention) look 
for _test at 4
stdcall symbols in object files are decorated with the number of bytes that 
should be popped from the
stack before the function returns.

(2) ghci cannot properly resolve dllimports in c object files i.e. if an 
object loaded by ghci
   (eg test_proxy, with c function test_proxy) calls functions from a dll 
(eg test) then
   that function in the object file will be decorated as

   this in normal course linked agains a .lib or a .a file will be resolved 
to _test function in the dll
   but ghci does not do this.

ofcourse these problems go away when using ghc ... but well what's the point 
of ghci then?

SUMMARY OF TESTS and METHODOLOGY
--------------------------------

1) REFERENCE CASE
   verify that ccall works ..
   Create c file test1a.c and function test(int) in the file
   create haskell file htest1.hs with function ctest calling test
   check this works ok with
     (a) c object loaded (test1a.c, htest1.hs )-- OK, RIGHT!
     (b) windows dll, c is function dll export  (test1b.c, htest1.hs ) -- 
OK, RIGHT!

3) change calling convention to stdcall
     (a) check loading of function from object (test3a.c, htest3.hs) -- 
FAIL, WRONG
     (b) check loading of stdcall function from dll -- FAIL, WRONG

4) try what shouldn't work
   (a) htest3 against object test1a,
       i.e. what hapens when you try loading a haskell module with foreign 
import stdcall
       against c function declared with ccall convention
       ghci loaded -- OK!!, WRONG, it should not have resolved the symbol 
and not loaded
       ghc called function and crashed -- given that it resolved the symbol
       and called the function this is expected

   (b) htest3 against dll htest1b.dll
       same result as 4a -- expected,

    behaviour consistent in both cases with resolving stdcall
    callouts (symbols test) in a manner consistent with ccalls
    i.e. resolving the call to c function test as a call to symbol _test 
rather than
    _test at 4 as it should be according to stdcall convention
    yet the function is called as stdcall c function consequently ghci 
crashes when
    the ccall function returns and the stack has not been cleared of the 
function arguments
    as is required by the stdcall convention

5) Hmm, lets see what happens when we have a ccall c proxy function calling 
stdcall c function in dll etc?

   (a) test_proxy5a.o with test3b.dll
       ccall function calling stdcall function in dll
       unknown symbol __imp__test -- FAILED, WRONG dllimports are decorated 
with __imp__
       so a stdcall dllimport to a c function test will appear as 
__imp__test at 4
       the dll will export symbol _test at 4, usually it is the function of the 
import library .a or .lib
       depending upon which linker mingw or msvc to resolve __imp_test at 4 to 
reference _test at 4 in dll

   (b) test_proxy5b.o with test3b.dll, htest5.hs
       ccall function calling stdcall function (not declared as dllimport)
       LOADS OK, RUNS OK
       test_proxy5b imports symbol _test at 4 the test3b.dll exports symbol 
_test at 4
       arguably this is correct behaviour as __imp__ decoration is an MS 
innovation and case (a)
       should be handled together with case (b).

    (c) whell what about test_proxy5b.o with test1b.dll
        i.e. c stdcall to a c function declared with ccall convention from 
.o object loaded by ghci
        should fail to resolve symbols, should crash program
        does niether ???? hey this has me stumped
        test_proxy5b.o imports symbol _test at 4
        test1b.dll exports symbol _test
        hmmm how is the symbol _test at 4 resolved ???
        why isn't the stack corrupted when test is called ???
        MYSTERY ???

=========================================================================
DETAIL
=========================================================================
compiler: ghci version 6.6
platform: windows XP

NOTES-
test1[a,b].c
test3[a,b].c
differnece in these files between a,b version is that the b version declares 
dllexport,
required when building a dll with msvc (and not using def file), not 
relevant with mingw
and using "dllwrap --export-all-symbols"

TEST 1 ===================================================================
-- test1a.c ----------------------------------------------------------------
#include <stdio.h>

void test(int arg)
{
   printf("The argument passed was %i\n", arg );
}
-- test1b.c ----------------------------------------------------------------
#include <stdio.h>

__declspec(dllexport) void test(int arg)
{
   printf("The argument passed was %i\n", arg );
}

-- htest1.hs ---------------------------------------------------------------

import Foreign
import Foreign.C


foreign import ccall "test" ctest :: CInt -> IO ()
---------------------------------------------------------------------------

COMPILATION OF C DLL/OBJECTS
=============================
gcc -c test1a.c
gcc -c test1b.c
ar -rv test1b.a test1b.o
dllwrap --export-all-symbols --output-lib test1b.dll.a -o test1b.dll 
test1b.a

TEST 1A results ------------------------------------------------------------
ghci -fglasgow-exts test1a.o
Loading package base ... linking ... done.
Loading object (static) test1a.o ... done
final link ... done
Prelude> :load htest1
[1 of 1] Compiling Main             ( htest1.hs, interpreted )
Ok, modules loaded: Main.
*Main> ctest 5
The argument passed was 5
*Main>

TEST 1B results ------------------------------------------------------------
ghci -fglasgow-exts -ltest1b
Loading package base ... linking ... done.
Loading object (dynamic) test1b ... done
final link ... done

Prelude> :load htest1
[1 of 1] Compiling Main             ( htest1.hs, interpreted )
Ok, modules loaded: Main.
*Main> ctest 5
The argument passed was 5
*Main>


TEST 3 =====================================================================
-- test3a.c ----------------------------------------------------------------
#include <stdio.h>

void _stdcall test(int arg)
{
   printf("The argument passed was %i\n", arg );
}
-- test3b.c ----------------------------------------------------------------
#include <stdio.h>

__declspec(dllexport) void _stdcall test(int arg)
{
   printf("The argument passed was %i\n", arg );
}
-- htest3.hs ---------------------------------------------------------------

import Foreign
import Foreign.C


foreign import stdcall "test" ctest :: CInt -> IO ()
----------------------------------------------------------------------------

COMPILATION OF C DLL/OBJECTS
=============================

compilation (mingw)
gcc -c test3a.c
gcc -c test3b.c
ar -rv test3b.a test3b.o
E:\vvv\WP03\S00\ffi>dllwrap --export-all-symbols --output-lib test3b.dll.a 
-o test3b.dll test3b.a

compilation msvc (same result with both mingw and msvc dll!!)
cl -c test3.c
link etc ...


TEST 3A results ------------------------------------------------------------
ghci -fglasgow-exts test3a.o
Loading package base ... linking ... done.
Loading object (static) test3a.o ... done
final link ... done

Prelude> :load htest3.hs
[1 of 1] Compiling Main             ( htest3.hs, interpreted )

During interactive linking, GHCi couldn't find the following symbol:
  test

TEST 3B results ------------------------------------------------------------

ghci -fglasgow-exts -ltest3b
Loading package base ... linking ... done.
Loading object (dynamic) test3b ... done
final link ... done

Prelude> :load htest3.hs
[1 of 1] Compiling Main             ( htest3.hs, interpreted )

During interactive linking, GHCi couldn't find the following symbol:
  test

the actual symbol in the object file is _test at 4

TEST 4A 
---------------------------------------------------------------------
ghci -fglasgow-exts test1a.o
Prelude> :load htest3
[1 of 1] Compiling Main             ( htest3.hs, interpreted )
Ok, modules loaded: Main.
*Main> ctest 5
The argument passed was 5

... ghci then crashes .... as expected
after all ccall function was called using the stdcall calling convention !
but the module should not have loaded as ghci should have been looking for a 
_test at 4 symbol

TEST 5 =====================================================================
-- test_proxy5a.c 
----------------------------------------------------------------

#include <stdio.h>

__declspec(dllimport) void _stdcall test(int arg);

void test_proxy(int arg)
{
   test(arg);
}
-- test_proxy5a.c 
----------------------------------------------------------------
#include <stdio.h>

void _stdcall test(int arg);

void test_proxy(int arg)
{
   test(arg);
}
-- htest5.hs 
----------------------------------------------------------------------------
module Main where

import Foreign
import Foreign.C


foreign import ccall "test_proxy" ctest :: CInt -> IO ()
-----------------------------------------------------------------------------------------

COMPILATION OF C OBJECTS
=============================
gcc -c test_proxy5a.c
gcc -c test_proxy5b.c

TEST 5A results ------------------------------------------------------------

E:\vvv\WP03\S00\ffi>ghci -fglasgow-exts test_proxy5a.o -ltest3b
   ___         ___ _
  / _ \ /\  /\/ __(_)
/ /_\// /_/ / /  | |      GHC Interactive, version 6.6, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base ... linking ... done.
Loading object (static) test_proxy5a.o ... done
Loading object (dynamic) test3b ... done
:
test_proxy5a.o: unknown symbol `__imp__test'
final link ... : linking extra libraries/objects failed

TEST 5B results ------------------------------------------------------------

E:\vvv\WP03\S00\ffi>ghci -fglasgow-exts test_proxy5b.o -ltest3b
   ___         ___ _
  / _ \ /\  /\/ __(_)
/ /_\// /_/ / /  | |      GHC Interactive, version 6.6, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base ... linking ... done.
Loading object (static) test_proxy5b.o ... done
Loading object (dynamic) test3b ... done
final link ... done
Prelude> :load htest5
[1 of 1] Compiling Main             ( htest5.hs, interpreted )
Ok, modules loaded: Main.
*Main> ctest 5
The argument passed was 5
*Main>

TEST 5C results 
-------------------------------------------------------------
E:\vvv\WP03\S00\ffi>ghci -fglasgow-exts test_proxy5b.o -ltest1b
   ___         ___ _
  / _ \ /\  /\/ __(_)
/ /_\// /_/ / /  | |      GHC Interactive, version 6.6, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base ... linking ... done.
Loading object (static) test_proxy5b.o ... done
Loading object (dynamic) test1b ... done
final link ... done
Prelude> :load htest5
[1 of 1] Compiling Main             ( htest5.hs, interpreted )
Ok, modules loaded: Main.
*Main> ctest 5
The argument passed was 5
*Main> ctest 5
The argument passed was 5

_________________________________________________________________
Advertisement: Visit LetsShop.com to WIN Fabulous Books Weekly 
http://a.ninemsn.com.au/b.aspx?URL=http%3A%2F%2Fwww%2Eletsshop%2Ecom%2FLetsShopBookClub%2Ftabid%2F866%2FDefault%2Easpx&_t=751480117&_r=HM_Tagline_books&_m=EXT

_______________________________________________
Glasgow-haskell-bugs mailing list
Glasgow-haskell-bugs at haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs



More information about the Glasgow-haskell-bugs mailing list