[prev in list] [next in list] [prev in thread] [next in thread] 

List:       wine-devel
Subject:    Re: DLL functions returning structs-Info+Suggestions
From:       Jon Griffiths <jon_p_griffiths () yahoo ! com>
Date:       2000-11-23 4:01:14
[Download RAW message or body]

OK, I've been investigating (couldn't wait), heres what I've got so
far:

Recap: Using GetProcAddress to get a pointer (p_ldiv)to CRTDLL's
"ldiv" function, and calling through that pointer produces
errors in some cases. This is because ldiv returns a struct.
The problem occurs for any dll function that returns a struct when
one side (the process or the dll) is not the same as the other.

There are four situations to consider:

1. unix  application calls unix  .so
2. unix  application calls win32 .dll
3. win32 application calls unix  .so
4. win32 application calls win32 .dll

Case 1. unix calls unix (winelib apps)
 This works fine. function pointer is defined as:
    ldiv_t (__cdecl *p_ldiv)() = 0;
 and the returned struct is assigned without problem:
    ldiv_t ldt = p_ldiv(20L,3L);

Case 2. unix calls win32 (winelib, wine)
 Doesn't work using the above code. ldiv_t is filled with crap.
 The following works however:

    void (__cdecl *p_ldiv2)() = 0; /* Note void return! */
    ldiv_t ldt;
    p_ldiv2(20L,3L);
    __asm__ __volatile__( "movl %%eax, %0;" : "=m" (ldt.quot) : );
    __asm__ __volatile__( "movl %%edx, %0;" : "=m" (ldt.rem) : );

Because in this case VC returns structs < 8 bytes in registers.
Note that this code will crash in case 1 (i.e. if unix .so is
called).

Case 3. win32 calls unix (wine)

Crashes into debugger (see last post for trace). I can fix this by
modifying the .so, so the .spec calls my function:

@ cdecl ldiv(long long) CRTDLL_ldiv

which I implement as:

void __cdecl CRTDLL_ldiv(long x, long y)
{
  ldiv_t ldt = ldiv(x,y);
  __asm__ __volatile__( "movl %0, %%eax;" : "=m" (ldt.quot) : );
  __asm__ __volatile__( "movl %0, %%edx;" : "=m" (ldt.rem) : );
}

i.e. I load the struct into registers before returning. Of course,
this wont work with a unix caller.

Case 4. win32 calls win32 (wine)
 This works fine, as you'd expect. The pointer and call
 are exactly as in case 1.

Summary/Suggestions:

struct return conventions are different between gcc on linux and
win32 (and probably solaris etc as well). The method of gcc's returns
hasn't been investigated, but we cant change it anyway, unless we
want the Wine installation to require a recompile of every binary
on the box (including kernel), i.e *no way*.

The problem is that we must dynamically decide how to return
structs from .so based dlls based on whether the calling process
is a native or win32 binary. I'll start the ball rolling with
3 suggested solutions:

1. Allow .spec files to specify struct return values.
(I am not convinced this is a good idea anymore).

.spec files could be enhanced as per my previous mail.
The generated .spec.c file could hold a table of extra
info about structs being returned and generate two stubs,
one for host o/s calling programs, one for win32. The win32
stub would call the native version, load the returned value
into registers, and return. winelib progs would just link
direct to the native stub. GetProcAddress would determine
the callers type and return the appropriate stub.
Alternately, the base function could return its values in
registers, and a stub would be generated for native calls.
Theres a few possibilities around this area.

PROS: DLLs dont have to know about the mechanism.
CONS: Lots of work/overhead for a rarely used feature.
      More work to implement structs > 8 bytes.

2. Make the DLL's do it.
Do something like:

.h :
#if defined(__GNUC__) && defined(__i386__)
void __cdecl CRTDLL_ldiv(long x, long y)
#else
ldiv_t  __cdecl CRTDLL_ldiv(long x, long y)
#endif

.c:
#if defined(__GNUC__) && defined(__i386__)
void __cdecl CRTDLL_ldiv(long x, long y)
{
  ldiv_t ldt = ldiv(x,y);
  if (win32_caller())
  {
     __asm__ __volatile__( "movl %0, %%eax;" : "=m" (ldt.quot) : );
     __asm__ __volatile__( "movl %0, %%edx;" : "=m" (ldt.rem) :
     return;
   }
   _some_asm_here_to_push_the_return_value_the_way_gcc_does_
);
#else
/* All other callers/platforms must be native (winelib) */
ldiv_t __cdecl CRTDLL_ldiv(long x, long y)
{
  return ldiv(x,y);
}
#endif

PROS: Dont have to touch spec files for this rare occurence
      Can handle returning structs > 8 bytes when (if) they come up.
CONS: Must expose internal call to get process type for linux/x86
dlls

Note: I have to make sure this works!

3. Add a new function attribute to gcc

No Thanks. This would take forever...


So there you have it. If someone knows how to determine whether the
current process is native or win32, please let me know so I can test
#2 out. Other than that, feedback please!!

Cheers,
Jon

=====
"May their negative actions ripen upon me. And may all my virtues ripen upon \
them."-Nagarjuna, on Compassion

"If it could be talked about, everybody would have told their brother."-Chuang Tzu, \
on Tao

tntjpgriff@tsnxt.co.uk , jon_p_griffiths@yahoo.com

__________________________________________________
Do You Yahoo!?
Yahoo! Shopping - Thousands of Stores. Millions of Products.
http://shopping.yahoo.com/


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic