comp.lang.ada
 help / color / mirror / Atom feed
* StdCall and pragma Import_Function
@ 2011-08-19 16:24 Felix Krause
  2011-08-19 16:51 ` Dmitry A. Kazakov
  2011-08-19 18:10 ` Jeffrey Carter
  0 siblings, 2 replies; 16+ messages in thread
From: Felix Krause @ 2011-08-19 16:24 UTC (permalink / raw)


I'm writing a cross-platform Ada binding for a C library. It currently works on OSX and Linux, and I'm trying to get it to work on Windows. I have some code like this:

package P is
   type ULong is mod 2 ** 64;
   for ULong'Size use 64;

   type Bit_Vector is record
      First_Bit  : Boolean;
      Second_Bit : Boolean;
   end record;
   for Bit_Vector use record
      First_Bit  at 0 range 0 .. 0;
      Second_Bit at 0 range 1 .. 1;
   end record;
   for Bit_Vector'Size use ULong'Size;

   function C_Function (Param : Bit_Vector) return ULong;
   pragma Import (StdCall, C_Function, "cFunc");
   pragma Import_Function (Internal => C_Function, External => "cFunc", Mechanism => (Param => Value));
end P;

On Linux and OSX, this works without problems. On Windows however, I get this error: "undefined reference to 'cFunc@4'". The GNAT user manual tells me that for StdCall, the number behind the '@' is the total number of bytes used by the function parameters. Now I told the compiler quite explicitly that Param should be passed by value and therefore needs 8 bytes, not 4 bytes.

I'm not sure what goes wrong here. Is Import_Function ignored on Windows? As I compile for 32bit, the number of bytes used by Param would be 4 if it was passed by reference.

Of course, this example is simplified and the actual C function has more parameters that could be the cause of the wrong byte number, but if I use "Param : ULong" for the function definition, it works, so I'm pretty sure the problem lies in the usage of Bit_Vector for the parameter. I also used dumpbin.exe to extract the correct function names in the C library .lib file, and indeed, the symbol name is "_cFunc@8".

So, I do have a possibility to work around this problem; namely defining C_Function having a ULong parameter and then converting all Bit_Vector variables to ULong when calling C_Function. But I'm still curious what the problem is here, and if there is an easier workaround. Can anybody tell me?

Thanks,
Felix



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 16:24 StdCall and pragma Import_Function Felix Krause
@ 2011-08-19 16:51 ` Dmitry A. Kazakov
  2011-08-19 17:07   ` Felix Krause
  2011-08-19 17:26   ` Adam Beneschan
  2011-08-19 18:10 ` Jeffrey Carter
  1 sibling, 2 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2011-08-19 16:51 UTC (permalink / raw)


On Fri, 19 Aug 2011 09:24:33 -0700 (PDT), Felix Krause wrote:

>    pragma Import (StdCall, C_Function, "cFunc");
>    pragma Import_Function (Internal => C_Function, External => "cFunc", Mechanism => (Param => Value));
> end P;

Stdcall is the calling convention used by Windows API.  If you want to call
a C function then most likely it rather should be C, i.e.

   pragma Import (C, ...);

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 16:51 ` Dmitry A. Kazakov
@ 2011-08-19 17:07   ` Felix Krause
  2011-08-19 18:58     ` Dmitry A. Kazakov
  2011-08-19 17:26   ` Adam Beneschan
  1 sibling, 1 reply; 16+ messages in thread
From: Felix Krause @ 2011-08-19 17:07 UTC (permalink / raw)
  Cc: mailbox

What I'm wrapping is OpenCL, which does use StdCall (at least in AMD's Stream API distribution).



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 16:51 ` Dmitry A. Kazakov
  2011-08-19 17:07   ` Felix Krause
@ 2011-08-19 17:26   ` Adam Beneschan
  2011-08-19 17:38     ` Felix Krause
  1 sibling, 1 reply; 16+ messages in thread
From: Adam Beneschan @ 2011-08-19 17:26 UTC (permalink / raw)


On Aug 19, 9:51 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:
> On Fri, 19 Aug 2011 09:24:33 -0700 (PDT), Felix Krause wrote:
> >    pragma Import (StdCall, C_Function, "cFunc");
> >    pragma Import_Function (Internal => C_Function, External => "cFunc", Mechanism => (Param => Value));
> > end P;
>
> Stdcall is the calling convention used by Windows API.  If you want to call
> a C function then most likely it rather should be C, i.e.
>
>    pragma Import (C, ...);

The OP did say that the symbol name _cFunc@8 exists in the library,
which indicates that using StdCall appears to be correct, just that
GNAT is using the wrong size for the symbol.

Offhand, this looks like a bug, but there may be some other legitimate
reason why GNAT isn't getting the size right, and I'm not familiar
enough with GNAT-specific pragmas to be of any more help.  Out of
curiosity, Felix, what happens if you add a 62-bit "filler" field to
Bit_Vector, with 2..63 in the representation clause?  Does that make
things work?

                                   -- Adam



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 17:26   ` Adam Beneschan
@ 2011-08-19 17:38     ` Felix Krause
  0 siblings, 0 replies; 16+ messages in thread
From: Felix Krause @ 2011-08-19 17:38 UTC (permalink / raw)


On Friday, August 19, 2011 7:26:01 PM UTC+2, Adam Beneschan wrote:

>  Out of
> curiosity, Felix, what happens if you add a 62-bit "filler" field to
> Bit_Vector, with 2..63 in the representation clause?  Does that make
> things work?

No, it still gives a size of 4 bytes. Btw, I do get a warning that 56 bits are unused without the filler, so GNAT seems to get the size of the record right.



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 16:24 StdCall and pragma Import_Function Felix Krause
  2011-08-19 16:51 ` Dmitry A. Kazakov
@ 2011-08-19 18:10 ` Jeffrey Carter
  2011-08-19 18:37   ` Felix Krause
  1 sibling, 1 reply; 16+ messages in thread
From: Jeffrey Carter @ 2011-08-19 18:10 UTC (permalink / raw)


On 08/19/2011 09:24 AM, Felix Krause wrote:
> I'm writing a cross-platform Ada binding for a C library. It currently works on OSX and Linux, and I'm trying to get it to work on Windows. I have some code like this:

The first rule when interfacing to another language is to only use types 
declared as having the convention of that language.

> package P is
>     type ULong is mod 2 ** 64;
>     for ULong'Size use 64;

pragma Convention (C, Ulong);

>     type Bit_Vector is record
>        First_Bit  : Boolean;
>        Second_Bit : Boolean;
>     end record;
>     for Bit_Vector use record
>        First_Bit  at 0 range 0 .. 0;
>        Second_Bit at 0 range 1 .. 1;
>     end record;
>     for Bit_Vector'Size use ULong'Size;

pragma Convention (C_Pass_By_Copy, Bit_Vector);

>     function C_Function (Param : Bit_Vector) return ULong;
>     pragma Import (StdCall, C_Function, "cFunc");
>     pragma Import_Function (Internal =>  C_Function, External =>  "cFunc", Mechanism =>  (Param =>  Value));

Import_Function is not a standard pragma. C_Pass_By_Copy is standard, and seems 
to do the same thing.

> end P;

-- 
Jeff Carter
"Blessed is just about anyone with a vested interest in the status quo."
Monty Python's Life of Brian
73



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 18:10 ` Jeffrey Carter
@ 2011-08-19 18:37   ` Felix Krause
  0 siblings, 0 replies; 16+ messages in thread
From: Felix Krause @ 2011-08-19 18:37 UTC (permalink / raw)


Thanks for pointing this out, I didn't know about C_Pass_By_Copy. I did remove the Import_Function pragmas in my code and replaced them by adding the C_Pass_By_Copy convention to the Bit_Vector type. However, GNAT still wants to link against "cFunc@4", so while this is good advice, it doesn't solve the problem.



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 17:07   ` Felix Krause
@ 2011-08-19 18:58     ` Dmitry A. Kazakov
  2011-08-19 19:23       ` Felix Krause
  0 siblings, 1 reply; 16+ messages in thread
From: Dmitry A. Kazakov @ 2011-08-19 18:58 UTC (permalink / raw)


On Fri, 19 Aug 2011 10:07:21 -0700 (PDT), Felix Krause wrote:

> What I'm wrapping is OpenCL, which does use StdCall (at least in AMD's Stream API distribution).

1. How exactly is this function declared in the header file? The convention
must be specified there, e.g. as __stdcall, because it is not the native
C/C++ convention.

2. How is this function is defined in the def file of the import library.
You can see there its fully qualified name.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 18:58     ` Dmitry A. Kazakov
@ 2011-08-19 19:23       ` Felix Krause
  2011-08-19 19:43         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 16+ messages in thread
From: Felix Krause @ 2011-08-19 19:23 UTC (permalink / raw)
  Cc: mailbox

From the header file:

extern CL_API_ENTRY cl_int CL_API_CALL
clGetDeviceIDs(cl_platform_id   /* platform */,
               cl_device_type   /* device_type */,
               cl_uint          /* num_entries */,
               cl_device_id *   /* devices */,
               cl_uint *        /* num_devices */) CL_API_SUFFIX__VERSION_1_0;

Those macros are defined as:

#if defined(_WIN32) || defined(__CYGWIN__)
#define CL_API_ENTRY
#define CL_API_CALL __stdcall
    #define CL_CALLBACK     __stdcall
#else
#define CL_API_ENTRY
#define CL_API_CALL
    #define CL_CALLBACK
#endif

So it indeed uses StdCall convention. The corresponding output of dumpbin is:

  Version      : 0
  Machine      : 14C (x86)
  TimeDateStamp: 4E03B987 Fri Jun 24 00:09:11 2011
  SizeOfData   : 0000001E
  DLL name     : OpenCL.dll
  Symbol name  : _clGetDeviceIDs@24
  Type         : code
  Name type    : undecorate
  Hint         : 44
  Name         : clGetDeviceIDs

(I don't have a def file, but this shows the fully qualified name too.)

cl_device_type is the bitvector from my example, and GNAT searches for "clGetDeviceIDs@20".



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 19:23       ` Felix Krause
@ 2011-08-19 19:43         ` Dmitry A. Kazakov
  2011-08-19 20:07           ` Felix Krause
  2011-08-20  8:04           ` Georg Bauhaus
  0 siblings, 2 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2011-08-19 19:43 UTC (permalink / raw)


On Fri, 19 Aug 2011 12:23:49 -0700 (PDT), Felix Krause wrote:

> From the header file:
> 
> extern CL_API_ENTRY cl_int CL_API_CALL
> clGetDeviceIDs(cl_platform_id   /* platform */,
>                cl_device_type   /* device_type */,
>                cl_uint          /* num_entries */,
>                cl_device_id *   /* devices */,
>                cl_uint *        /* num_devices */) CL_API_SUFFIX__VERSION_1_0;
> 
> Those macros are defined as:
> 
> #if defined(_WIN32) || defined(__CYGWIN__)
> #define CL_API_ENTRY
> #define CL_API_CALL __stdcall
>     #define CL_CALLBACK     __stdcall
> #else
> #define CL_API_ENTRY
> #define CL_API_CALL
>     #define CL_CALLBACK
> #endif
> 
> So it indeed uses StdCall convention. The corresponding output of dumpbin is:
> 
>   Version      : 0
>   Machine      : 14C (x86)
>   TimeDateStamp: 4E03B987 Fri Jun 24 00:09:11 2011
>   SizeOfData   : 0000001E
>   DLL name     : OpenCL.dll
>   Symbol name  : _clGetDeviceIDs@24
>   Type         : code
>   Name type    : undecorate
>   Hint         : 44
>   Name         : clGetDeviceIDs
> 
> (I don't have a def file, but this shows the fully qualified name too.)
> 
> cl_device_type is the bitvector from my example, and GNAT searches for "clGetDeviceIDs@20".

How are the types of the arguments declared?

cl_int
cl_platform_id
cl_device_type
cl_device_id
cl_uint

BTW, you will have problems with making bindings to this mess portable. I
had similar issues with poorly designed Glib. They too have the linkage
names varying from platform to platform. I solved that by using GNU linker
weak references and wrappers. Not nice, but they refused to fix the
problem.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 19:43         ` Dmitry A. Kazakov
@ 2011-08-19 20:07           ` Felix Krause
  2011-08-19 20:37             ` Dmitry A. Kazakov
  2011-08-19 20:45             ` Jeffrey Carter
  2011-08-20  8:04           ` Georg Bauhaus
  1 sibling, 2 replies; 16+ messages in thread
From: Felix Krause @ 2011-08-19 20:07 UTC (permalink / raw)
  Cc: mailbox

typedef signed __int32 cl_int;

typedef struct _cl_platform_id *  cl_platform_id;

typedef unsigned __int64 cl_ulong;
typedef cl_ulong cl_bitfield;
typedef cl_bitfield cl_device_type;

typedef struct _cl_device_id * cl_device_id;

typedef unsigned __int32  cl_uint;

(For further details, the headers are available at http://www.khronos.org/registry/cl/ as cl.h and cl_platform.h)

As this problem definitely is fixable (by substituting the bit vector types with ULong in the function definitions), I'm confident I'll get my bindings working cross-platform. Unlike Glib, OpenCL is an open standard and there was much effort to make the API as clear and portable as possible. There also exist cross-platform bindings for the related OpenGL API in Ada, so it definitely is possible.



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 20:07           ` Felix Krause
@ 2011-08-19 20:37             ` Dmitry A. Kazakov
  2011-08-19 20:45             ` Jeffrey Carter
  1 sibling, 0 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2011-08-19 20:37 UTC (permalink / raw)


On Fri, 19 Aug 2011 13:07:16 -0700 (PDT), Felix Krause wrote:

> typedef signed __int32 cl_int;
> 
> typedef struct _cl_platform_id *  cl_platform_id;
> 
> typedef unsigned __int64 cl_ulong;
> typedef cl_ulong cl_bitfield;
> typedef cl_bitfield cl_device_type;
> 
> typedef struct _cl_device_id * cl_device_id;
> 
> typedef unsigned __int32  cl_uint;
> 
> (For further details, the headers are available at http://www.khronos.org/registry/cl/ as cl.h and cl_platform.h)
> 
> As this problem definitely is fixable (by substituting the bit vector
> types with ULong in the function definitions), I'm confident I'll get my
> bindings working cross-platform. Unlike Glib, OpenCL is an open standard
> and there was much effort to make the API as clear and portable as
> possible. There also exist cross-platform bindings for the related OpenGL
> API in Ada, so it definitely is possible.

   with Interfaces; use Interfaces;

   type cl_int  is new Integer_32;
   type cl_uint is new Unsigned_32;
   type cl_device_type is new Unsigned_64;
   
   function clGetDeviceIDs
            (  platform    : System.Address; -- access cl_platform_id
               device_type : cl_device_type;
               num_entries : cl_uint;
               devices     : System.Address; -- access cl_device_id
               num_devices : access cl_uint
            )  return cl_int;
   pragma Import (Stdcall, clGetDeviceIDs, "_clGetDeviceIDs@24");

Note:

1. I used System.Address twice, because you hadn't provided the
corresponding structures.

2. You have to use explicit external names everywhere. First you check that
the generated name has the correct @n suffix. It must be
"clGetDeviceIDs@24" in the above case. Then you add "_" prefix to it, and
specify the result as an external name.

3. It is not GNAT bug, it is crippled library interface.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 20:07           ` Felix Krause
  2011-08-19 20:37             ` Dmitry A. Kazakov
@ 2011-08-19 20:45             ` Jeffrey Carter
  2011-08-21 18:25               ` Felix Krause
  1 sibling, 1 reply; 16+ messages in thread
From: Jeffrey Carter @ 2011-08-19 20:45 UTC (permalink / raw)


On 08/19/2011 01:07 PM, Felix Krause wrote:
>
> typedef unsigned __int64 cl_ulong;
> typedef cl_ulong cl_bitfield;
> typedef cl_bitfield cl_device_type;

So what is expected by the C is an unsigned 64-bit integer, not a 64-bit record 
with 2 1-bit fields. Another rule I use when interfacing with C is, when it is 
more meaningful in Ada to use a different kind of type from that used by C, to 
use the Ada equivalent of the C type and convert to and from the more meaningful 
Ada type, (The conversions should, of course, be hidden from the client of your 
interface.) This seems to be your work around to passing the record, so I guess 
you're good to go.

-- 
Jeff Carter
"Your mother was a hamster and your father smelt of elderberries."
Monty Python & the Holy Grail
06



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 19:43         ` Dmitry A. Kazakov
  2011-08-19 20:07           ` Felix Krause
@ 2011-08-20  8:04           ` Georg Bauhaus
  2011-08-20  9:23             ` Dmitry A. Kazakov
  1 sibling, 1 reply; 16+ messages in thread
From: Georg Bauhaus @ 2011-08-20  8:04 UTC (permalink / raw)


On 19.08.11 21:43, Dmitry A. Kazakov wrote:
  it indeed uses StdCall convention. The corresponding output of dumpbin is:

> BTW, you will have problems with making bindings to this mess portable. I
> had similar issues with poorly designed Glib. They too have the linkage
> names varying from platform to platform. I solved that by using GNU linker
> weak references and wrappers. Not nice, but they refused to fix the
> problem.

Did you mention the text ".*Ada.*" when asking?  .->




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-20  8:04           ` Georg Bauhaus
@ 2011-08-20  9:23             ` Dmitry A. Kazakov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2011-08-20  9:23 UTC (permalink / raw)


On Sat, 20 Aug 2011 10:04:15 +0200, Georg Bauhaus wrote:

> On 19.08.11 21:43, Dmitry A. Kazakov wrote:
>   it indeed uses StdCall convention. The corresponding output of dumpbin is:
> 
>> BTW, you will have problems with making bindings to this mess portable. I
>> had similar issues with poorly designed Glib. They too have the linkage
>> names varying from platform to platform. I solved that by using GNU linker
>> weak references and wrappers. Not nice, but they refused to fix the
>> problem.
> 
> Did you mention the text ".*Ada.*" when asking?  .->

Of course not! (:-)) I just pointed out that some functions have one
external name under Win32 and another under Linux. They "wisely" used
#define to achieve this fantastic effect. I don't even asked them to fix
the mess, only to add an alias name to either distribution. The answer was
"won't do it".

Having said that, I used Glib in order to do things the Ada standard
library does wrong. Its directories (devices, file system) and OS calls
packages are designed OS-dependent, which is unacceptable. Glib by all its
drawbacks at least tries to make this right.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: StdCall and pragma Import_Function
  2011-08-19 20:45             ` Jeffrey Carter
@ 2011-08-21 18:25               ` Felix Krause
  0 siblings, 0 replies; 16+ messages in thread
From: Felix Krause @ 2011-08-21 18:25 UTC (permalink / raw)


Using a 64-bit Integer type instead of the record, I finally succeeded in linking to the library. Luckily, it works now without having to use explicit external names, so my code remains fully os-independent.

Thanks everyone.



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2011-08-21 18:25 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-19 16:24 StdCall and pragma Import_Function Felix Krause
2011-08-19 16:51 ` Dmitry A. Kazakov
2011-08-19 17:07   ` Felix Krause
2011-08-19 18:58     ` Dmitry A. Kazakov
2011-08-19 19:23       ` Felix Krause
2011-08-19 19:43         ` Dmitry A. Kazakov
2011-08-19 20:07           ` Felix Krause
2011-08-19 20:37             ` Dmitry A. Kazakov
2011-08-19 20:45             ` Jeffrey Carter
2011-08-21 18:25               ` Felix Krause
2011-08-20  8:04           ` Georg Bauhaus
2011-08-20  9:23             ` Dmitry A. Kazakov
2011-08-19 17:26   ` Adam Beneschan
2011-08-19 17:38     ` Felix Krause
2011-08-19 18:10 ` Jeffrey Carter
2011-08-19 18:37   ` Felix Krause

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox