Newbie questions about Pliant

Newbie questions about Pliant

loading a .dll

I am using MinGW to make a dll on windows.
Loading it with a test C program works.
Loading it with pliant's external meta doesn't
Message posted by maybe Boris Reitman on 2004/04/11 04:24:59
Here is the test program:

====================
#include <windows.h>
#include <stdio.h>


#define library "c:/pliant/pliantx/language/perl/lperl.dll"

int main() {
    HINSTANCE handle; void *fn;
    handle=LoadLibrary(library);
    if(handle==0) {
      fprintf(stderr, "error: can load library\n");
      return 1; }
    else {
      printf("library loaded ok: %d\n", (int)handle); }
#if 1
    fn=GetProcAddress(handle,"lperl_call_method");
    if(fn==0) {
      fprintf(stderr, "error: could not find function");
      return 0; }
    else {
      printf("got function address ok: %d\n", (int)fn); }
#endif
    return 0; }
==================================

Compiling it:

Administrator@sawwin ~/pliant-perl/pliantx/language/perl
$ /cygdrive/c/mingw/bin/gcc test_load_library.c  -o test_load_library


Running it: 

Administrator@sawwin ~/pliant-perl/pliantx/language/perl
$ ./test_load_library.exe 
library loaded ok: 1701052416
got function address ok: 1701057698


==================================

Here is the pliant code in which I am trying to use it,

=====================================
constant wrapper_library "c:/pliant/pliantx/language/perl/lperl.dll"
#...
function init_callback_xs name address filename
  arg CStr name
  arg Address address
  arg CStr filename
  external wrapper_library "init_callback_xs"

=====================================

Here's what happens when I try to run a test pliant program that loads
the module with above function

==============
$ !pliant
pliant examples/array.pli >& out

Administrator@sawwin ~/pliant-perl/pliantx/language/perl
$ head out
----------------------------------------------------------------
Failed to load Win32 DLL c:/pliant/pliantx/language/perl/lperl.dll
compile /pliantx/language/perl/types/../cperl.pli (internals) 95 3
compile /pliantx/language/perl/types/../cperl.pli (internals) 95 3
==============

Here is how I compile lperl.dll:

==============
make -f Makefile.win32                        
/cygdrive/c/mingw/bin/gcc -ggdb -shared -o lperl.dll lperl.c -g -L"c:/perl/5.8.3/lib/CORE" -L"C:/MinGW/lib"  C:/perl/5.8.3/lib
libperl58.a C:/MinGW/lib/libmsvcrt.a C:/MinGW/lib/libmoldname.a C:/MinGW/lib/libkernel32.a C:/MinGW/lib/libuser32.a C:/MinGW/l
gdi32.a C:/MinGW/lib/libwinspool.a C:/MinGW/lib/libcomdlg32.a C:/MinGW/lib/libadvapi32.a C:/MinGW/lib/libshell32.a C:/MinGW/li
le32.a C:/MinGW/lib/liboleaut32.a C:/MinGW/lib/libnetapi32.a C:/MinGW/lib/libuuid.a C:/MinGW/lib/libwsock32.a C:/MinGW/lib/lib
C:/MinGW/lib/libwinmm.a C:/MinGW/lib/libversion.a C:/MinGW/lib/libodbc32.a -g -DDEBUGGING -DWIN32 -DHAVE_DES_FCRYPT -DNO_HASH_
DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS -fno-strict-aliasing -DPERL_MSVCRT_READFIX  -I"C:/perl/5.8.3/lib/CORE" 
===============


Any ideas why it is happening ? Could it be happening because
my C compiler is MinGW and pliant was compiled in another way ? 
I am using pliant version 91.

Thanks
Boris

(PS. Can I post on this forum through email ?)



Message posted by hubert.tonneau on 2004/04/12 17:03:25
> constant wrapper_library "c:/pliant/pliantx/language/perl/lperl.dll"

Try using '\' instead of '/' and try to put your DLL in windows DLL directory
and remove the path.

> Can I post on this forum through email ?

No, because spamers could do the same, and I don't want to waste time moderating
the forum.
Message posted by maybe Boris Reitman on 2005/11/30 05:35:38
Tried it.  The test works, throught pliant doesn't.
I tried with placing the dll in c:\winnt  and using no path as prefix.
Still the same. the test works, pliant doesn't.
Message posted by hubert.tonneau on 2005/11/30 09:29:16
All the information I can provide you about Win32 DLLs is that they are not easy
to build.
As an example, I could not find on the net a pre compiled zlib DLL for Win32
usable for Pliant, because they had all been improperly compiled (compiler
specific calling convensions or external dependencies).

As a result Pliant archives provide i386-pc-cygwin.tgz which is a ready to
use cross compiler that I can run on Linux to compile for Win32,
and the /pliant/install/make-win32-i386-gcc contains sample usage.

What seems strange about your problem is that the DLL does not load at all
whereas I would expect it to load but not find the requested function name,
or crash when you call it.

The Pliant function loading an external DLL is 'external_function_address'
in module /pliant/language/os/dll.c
Message posted by maybe Boris Reitman on 2005/11/30 16:45:15
I have used GetLastError() in dll.c, and I am getting error code 193.

Error code 193 from what I can gather means invalid dll.

If I try to load a dll that doesn't exist on the computer I get error code 126.
So the difference in the error codes means that it is not the problem 
with finding the dll (I keep it in c:\winnt and refer to without a path prefix),
and indeed it has to do with the way my dll was compiled.

So now, suppose I was to compile my lperl.dll with the cygwin cross compiler 
that you supply. I need to compile my wrapper against the perl library 
that is installed on users system, which was compiled with a different 
compiler, possibly. I wonder if it is going to be a problem... 
Message posted by maybe Boris Reitman on 2005/12/03 09:23:12
So, same happened when I tried to load perl58.dll that I built
with mingw gcc. I went ahead and installed activestates' build of perl,
and pliant had no problem loading that version of perl58.dll and getting
a hold of a function in it.

ActiveState's Perl is compiled with VC compiler, so I downloaded free 
VC tools from microsoft.  I was able to build my perl wrapper module lperl.dll
against activestates perl58.dll, and now LoadLibrary returns 
a valid pointer.  However, the problem remains, since it can't get a hold of 
a function inside the dll.  It seems that the free version of 'cl' VC compiler
doesn't accept some options that were supposedly used by activestate to 
make the build. That would explain why working with perl58.dll works, but 
with mine doesn't.

I'm looking now at pliant cygwin cross-compiler gcc flags and trying to compare 
with the flags that are used to compile perl with mingw gcc. There is -U__stdcall
that pliant was compiled with, that i need to look into.
 
Message posted by maybe Boris Reitman on 2005/12/21 18:50:00
I have made some progress. I am able to compile the dll, 
have pliant load it and run functions inside it.  However there are 
some issues with memory cleanup, as the following short tests demonstrate.

Here is code in C, that uses my .dll and works fine:
---------8<-----------------------------------------
#include <windows.h>
#include <windef.h>
typedef int (*f_proto1)(void*);
typedef void (*f_void_void)(void);
typedef void (*f_void_address)(void*);
typedef void* (*f_address_void)(void);
typedef void* (*f_address_str)(char *);
int main(int argc, char **argv, char **env)
{
    HINSTANCE handle; 
    f_void_void lperl_prepare_old;
    f_void_void lperl_cleanup_old;
    f_address_str lperl_eval_pv;
  
    handle=LoadLibrary("c:/pliant/pliantx/language/perl/lperl.dll");
  
    lperl_prepare_old=(f_void_void) GetProcAddress(handle,"lperl_prepare_old");
    lperl_cleanup_old=(f_void_void) GetProcAddress(handle,"lperl_cleanup_old");
    lperl_eval_pv=(f_address_str) GetProcAddress(handle,"lperl_eval_pv");
  
    lperl_prepare_old();
    printf("ok\n");
  
    lperl_eval_pv("print qq(#I AM HERE\n);");
    printf("ok\n");
  
    lperl_eval_pv("print qq(#I AM THERE\n);");
    printf("ok\n");
  
    lperl_cleanup_old();
    printf("ok\n");
}
--------->8-----------------------------------------

The output from running it is:
---------8<-----------------------------------------
C:\pliant\pliantx\language\perl>t\000_test_dll.exe
ok
#I AM HERE
#lperl.dll: after eval_pv
ok
#I AM THERE
#lperl.dll: after eval_pv
ok  
in lperl_cleanup
  destructed: ok
  DONE
ok
--------->8-----------------------------------------

Here is an equivalent code in Pliant, that crashes,
---------8<-----------------------------------------
module "/pliant/language/unsafe.pli"
module "/pliant/language/compiler.pli"
module "/pliant/admin/file.pli"

constant wrapper_library "c:/pliant/pliantx/language/perl/lperl.dll"

function lperl_prepare_old
  external wrapper_library "lperl_prepare_old"

function lperl_cleanup_old
  external wrapper_library "lperl_cleanup_old"

function lperl_eval_pv code_string -> sv
  arg CStr code_string
  arg Address sv
  external wrapper_library "lperl_eval_pv"

function main
  lperl_prepare_old
  console "ok" eol

  lperl_eval_pv("print qq(#I AM HERE\n);");
  console "ok" eol

  lperl_eval_pv("print qq(#I AM THERE\n);");
  console "ok" eol

  lperl_cleanup_old
  console "ok" eol
main
--------->8-----------------------------------------

The output from running it is below.  It just hangs, it never exists:
---------8<-----------------------------------------
C:\pliant\pliantx\language\perl>pliant-debug1 module /pliantx/language/perl/t/001_test_dll.pli
ok
#I AM HERE
#lperl.dll: after eval_pv
ok
#I AM THERE
#lperl.dll: after eval_pv
ok
in lperl_cleanup
  destructed: ok
  DONE
ok
exception
----------------------------------------------------------------
actions stack is:
execute /pliantx/language/perl/t/001_test_dll.pli (internals) 32 1
parse /pliantx/language/perl/t/001_test_dll.pli (internals) 33 1
module /pliantx/language/perl/t/001_test_dll.pli
----------------------------------------------------------------
processor stack content is:
. free (MemoryPool Address) +
--------->8-----------------------------------------

Message posted by maybe Boris Reitman on 2005/12/21 18:51:39
Here is the relevant code in the lperl.dll that is being called,

---------8<-----------------------------------------
void lperl_prepare_old()
{
   char *embedding[] = { "", "-e", "0" };
   if ( my_perl != NULL )
     return;
   my_perl = perl_alloc();
   perl_construct(my_perl);
   perl_parse(my_perl, xs_init, 3, embedding, (char **)NULL);
}
void lperl_cleanup(PerlInterpreter *perl_interpreter)
{
   DEBUG_PRINT("in lperl_cleanup\n");
   perl_destruct(perl_interpreter);
   DEBUG_PRINT("  destructed: ok\n");
   perl_free(perl_interpreter);
   DEBUG_PRINT("  DONE\n");
}
void lperl_cleanup_old()
{
   lperl_cleanup(my_perl);
}
SV* lperl_eval_pv(char *str) /*, long int croak_on_error)*/
{
  SV *x = eval_pv(str, 0);
  printf("#lperl.dll: after eval_pv\n");
  return x;
}
------------->8--------------------------------------

What I don't understand why exactly the same usage of the .dll library
in the two programs works in the first program and fails in the second program.
I can think of two reasons, that you can perhaps expand on,

1. Could pliant be running the "external" functions and handling its exit
differently than C does ? Could Pliant sample be converting the types in the
signatures of the "external" functions not correctly ?

2. lperl.dll has a global variable 'my_perl' that is initialized by a
call to lperl_prepare_old().  Then lperl_cleanup_old() must delete it.
Could the different in how modules are ran under pliant and under .exe be different
for global variables ?

Any ideas ? Btw, the .pli test works on linux fine.
Message posted by maybe Boris Reitman on 2005/12/21 18:52:18
Below is the makefile to build the the lperl.dll and
t/000_test_dll.exe

-------8<------------------------------------------------
CC=cl

# ActivePerl + VC
PERL_CCOPTS=/nologo /GF /W3 /MD /Zi /DNDEBUG /DWIN32 /D_CONSOLE /DNO_STRICT /DHAVE_DES_FCRYPT /DNO_HASH_SEED /DUSE_SITECUSTOMIZE /DPERL_IMPLICIT_CONTEXT /DPERL_IMPLICIT_SYS /DUSE_PERLIO /DPERL_MSVCRT_READFIX  /I"C:\Perl\lib\CORE"
PERL_LDOPTS=/nologo /nodefaultlib /debug /libpath:"C:\Perl\lib\CORE"  /machine:x86  C:\Perl\lib\CORE\perl58.lib C:\msvc\VC\LIB\oldnames.lib C:\msvc\VC\LIB\kernel32.lib C:\msvc\VC\LIB\msvcrt.lib

all : lperl.dll t/000_test_dll.exe t/lib/test_mod.dll

t/000_test_dll.exe: t/src/000_test_dll.c
        $(CC) /ot/000_test_dll.exe t/src/000_test_dll.c $(PERL_CCOPTS) $(PERL_LDOPTS)

t/lib/test_mod.dll: t/src/test_mod.c
        $(CC) /Fot/lib/test_mod.o /Tpt/src/test_mod.c $(PERL_CCOPTS) $(PERL_LDOPTS)
        lib /out:test_mod.lib t/lib/test_mod.o
        link /out:t/lib/test_mod.dll /DLL /def:t/src/test_mod.def /implib:t/lib/test_mod.lib t/lib/test_mod.o

lperl.dll: lperl.c
        $(CC) /Folperl.o /Tplperl.c $(PERL_CCOPTS) $(PERL_LDOPTS)
        lib /out:lperl.lib lperl.o
        link /out:lperl.dll /DLL /def:lperl.def /implib:lperl.lib lperl.o $(PERL_LDOPTS)

clean:
        del t\000_test_dll*
        del t\lib\*
        del lperl.dll

------->8------------------------------------------------

Thanks,
Boris
Message posted by hubert.tonneau on 2005/12/21 19:45:07
One possible problem is that also C code is possibely clean and self contained,
the C compiler glue libraries tend to make it false.

As an example, if the perl library is allocating memory, it might rely on
a global variable that is initialised by the C compiler startup code.
If you call the library from Pliant, then the startup has not been
initialised. Boom.
Same with threads.
Same with environment.
Etc.
Message posted by maybe Boris Reitman on 2005/12/22 16:31:35
I was able to isolate the problem further.  
The problem is how the CStr -> char* parameter is passed.  
Whenever I try to access an external function that 
accepts a string, I get problems.

The makefile:
--8<------------------------------------
all : t/000_test_dll.exe t/lib/test_mod.dll

t/000_test_dll.exe: t/src/000_test_dll.c
        cl /ot/000_test_dll.exe t/src/000_test_dll.c

t/lib/test_mod.dll: t/src/test_mod.c t/src/test_mod.def
        gcc -c t/src/test_mod.c -o t/lib/test_mod.o
        dllwrap t/lib/test_mod.o --def t/src/test_mod.def -o t/lib/test_mod.dll -lws2_32

clean: 
        del t\000_test_dll*
        del t\lib\*
----------------------------------->8---
(Notice that I mixed the 'cl' and 'gcc' compilers.)  Supposedly the .dll
that is made is in win32 stdcall convention.

The source code for test_mod.dll:
--8<------------------------------------
#include <stdio.h>

void print_a_string(char *str)
{  
   puts("here1\n");
   puts(str);
}

int main() {
        return 0;
}
----------------------------------->8---

The test_mod.def file which is needed to convert .o into .dll:
--8<------------------------------------
LIBRARY test_mod
EXPORTS
        print_a_string
----------------------------------->8---

The program in C that uses the test_mod.dll. It works fine.
000_test_dll.c:
--8<------------------------------------
#include <windows.h>
#include <windef.h>
typedef void (*f_void_str)(char *);
int main(int argc, char **argv, char **env)
{
    HINSTANCE handle;
    f_void_str print_a_string;
    handle=LoadLibrary("c:/pliant/pliantx/language/perl/t/lib/test_mod.dll");
    print_a_string=(f_void_str) GetProcAddress(handle,"print_a_string");
    print_a_string("hello1\n");
}
----------------------------------->8---

The equivalent pliant program that crashes, 001_test_mod.pli:
--8<------------------------------------
module "/pliant/language/unsafe.pli"
module "/pliant/language/compiler.pli"

constant wrapper_library "c:/pliant/pliantx/language/perl/t/lib/test_mod.dll"

function print_a_string str
  arg CStr str 
  external wrapper_library "print_a_string"
    
function main
  print_a_string "hello1"
main
----------------------------------->8---

The output from running both programs. This time the pliant program
doesn't hang at the end, but still gives an exception.
--8<------------------------------------
C:\pliant\pliantx\language\perl>t\000_test_dll.exe
here1

hello1


C:\pliant\pliantx\language\perl>echo "t/001_test_dll.pli"
"t/001_test_dll.pli"
    
C:\pliant\pliantx\language\perl>pliant-debug1 module /pliantx/language/perl/t/001_test_dll.pli
here1

hello1
exception
----------------------------------------------------------------
actions stack is:
execute /pliantx/language/perl/t/001_test_dll.pli (internals) 12 1
parse /pliantx/language/perl/t/001_test_dll.pli (internals) 13 1
module /pliantx/language/perl/t/001_test_dll.pli
----------------------------------------------------------------
processor stack content is:
??? at 2292663
----------------------------------->8---
Message posted by hubert.tonneau on 2005/12/22 16:38:48
Please try all this at debugging level 2:
pliant debug 2 module /pliant/install/minimal.pli module /pliantx/language/perl/t/001_test_dll.pli
Message posted by maybe Boris Reitman on 2005/12/22 20:09:11
I ran it under debug 2. It took a very long time, but it finished in
exactly the same way with the same error message and line numbers.

Here is what I am thinking.  Why does calling with an integer argument not
produce an error, but with string does ? Also, why the crash happens
not right after the call to print_a_string, but at the destruction
of the program. I modified the pliant test to read,

--8<--------
...
function main
  print_a_string "hello1"
  console "Returned from function" eol
main
...
--->8-------

And when I run it, I see,

---8<-------
C:\pliant\pliantx\language\perl>pliant-debug1 module /pliantx/language/perl/t/001_test_dll.pli
here1

hello1
Returned from function
exception
----------------------------------------------------------------
actions stack is:
execute /pliantx/language/perl/t/001_test_dll.pli (internals) 13 1
parse /pliantx/language/perl/t/001_test_dll.pli (internals) 14 1
module /pliantx/language/perl/t/001_test_dll.pli
----------------------------------------------------------------
processor stack content is:
??? at 2292643
--->8------------

So could it be that we are writing the 
string into someone else's memory space? 

Could it be that we are trying to deallocate a space in memory
that was already freed by somebody else ? 

Thanks,
Boris
Message posted by hubert.tonneau on 2005/12/22 20:30:06
Your calling convension is probably bad. You should try something like:

void WINAPI lperl_cleanup(PerlInterpreter *perl_interpreter) {
Message posted by maybe Boris Reitman on 2005/12/24 04:33:15
Thanks Hubert, 
That solved this problem.  Now, I have another crash later on in the progam, 
which I am trying to isolate.


Boris