Old 10-22-2011, 02:19 PM   #1
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default building reaper sdk plugins with newer mingw

since the reaper sdk is using an abstract c++ class interface, there is the potential problem with calling conversations of member functions. explained here:

http://www.reaper.fm/sdk/plugin/plugin.php
http://forum.cockos.com/showthread.php?t=34447

pre gcc(mingw) 4.6.0, thiscall differs between gcc and msvc.
http://en.wikipedia.org/wiki/X86_cal...tions#thiscall

but versions post 4.6.0 have added support for the same format as msvc (explicitly or by default in 4.7).

http://gcc.gnu.org/gcc-4.6/changes.html
http://lists-archives.org/mingw-user...ith-mingw.html

a test:
Code:
/*
  c.h

  this header describes the interface
*/

#ifndef _H_
#define _H_

/*
  gcc 4.6.x supports explicit declaration of virtual members with __thiscall.
  conversation defaults for such in 4.7.0. the new __thiscall conversation
  is the same as in MSVC.
*/
#define THISCALL
#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6)
  #undef THISCALL
  #define THISCALL __thiscall
#elif defined __GNUC__
  #error __thiscall not supported for GCC VER < 4.6.0
#endif

#define VTABLE_NULL   = 0
#define EXPORT        __declspec(dllexport)

// some func pointer
extern void (*somef_ptr)(void);

// abstract base class with a couple of pure virtual methods
class c
{
  public:
    virtual void        THISCALL ptxt     (const char *)      VTABLE_NULL;
    virtual void*       THISCALL getsf    (void)              VTABLE_NULL;
    virtual const char* THISCALL getstr   (void)              VTABLE_NULL;
    virtual void                 tprintf  (const char*, ...)  VTABLE_NULL;
};

#endif // _H_
Code:
/*
  dll.cpp

  test library

  g++ -W -Wall -shared dll.cpp -o dll.dll -s
  cl /nologo /W3 /LD /MD dll.cpp /Fedll.dll
*/

#include "stdio.h"
#include "c.h"

// func pointer
void (*somef_ptr)();

// dll entry point receives pointer to class instance
extern "C"
{
  void EXPORT dllcall(c *cptr)
  {
    cptr->ptxt("c::ptxt");

    puts(cptr->getstr());
    
    cptr->tprintf("tprintf:0");
    cptr->tprintf("%s:%.0f", (const char*)"tprintf", 1.f);

    *(void**)&somef_ptr = cptr->getsf();
    somef_ptr();
  }
}
Code:
/*
  test.cpp

  abstract interface test for mingw (dll) and msvc (exe)

  g++ -W -Wall test.cpp -o test.exe -s
  cl /nologo /W3 /MD test.cpp /Fetest.exe

  out:
    start
    c::ptxt
    c::getstr
    tprintf:0
    tprintf:1
    somef
    end
*/

#include "windows.h"
#include "stdio.h"
#include "stddef.h"
#include "stdarg.h"

#include "c.h"

typedef void (__stdcall *ptr_fn)(void*);

// address of to be returned via c::getf
void somef(void)
{
  puts("somef");
}

// c_impl inherits from c
class c_impl : public c
{
  public:
    char str[1024];

    c_impl(void)  { strcat((char*)str, (const char*)"c::getstr\0"); }
    ~c_impl(void) { return; }

    virtual void        THISCALL ptxt     (const char *);
    virtual void*       THISCALL getsf    (void);
    virtual const char* THISCALL getstr   (void);
    virtual void                 tprintf  (const char*, ...);
};

// ptxt: prints some text from argument
void THISCALL c_impl::ptxt(const char *txt)
{
  puts(txt);
}

// getsf: returns a pointer to 'somef'
void* THISCALL c_impl::getsf(void)
{
  return (void*)(ptrdiff_t)&somef; // or (INT_PTR)&somef
}

// getstr: returns value of a data member
const char* THISCALL c_impl::getstr(void)
{
  return str;
}

// getstr: variadic member that sets data member str and prints it
void c_impl::tprintf(const char* format, ...)
{
  char buf[1024];
  
  va_list args;
  va_start(args, format);
  vsprintf(buf, format, args);
  va_end(args);
  
  memset(str, 0, sizeof(str)*sizeof(char));
  strcat((char*)str, (char*)buf);
  puts(str);
}

// exe entry
int main(void)
{
  HINSTANCE   hinst;
  FARPROC     id;
  ptr_fn      fn;
  c_impl      cinst; // instantiate inherited

  // load lib; get address of entry
  puts("start");
  if (!(hinst = LoadLibrary("dll.dll")))
    goto err_lib;
  if (!(id = GetProcAddress((HMODULE)hinst, "dllcall")))
    goto err_id;

  // set pointer; pass address of instance to entry
  fn = (ptr_fn)id;
  fn(&cinst);
  goto free_lib;

  // err
  err_lib:
  puts("err_lib");
  goto end;
  err_id:
  puts("err_id");
  free_lib:
  FreeLibrary(hinst);
  end:
  puts("end");

  return 0;
}
files:
http://dl.dropbox.com/u/1627980/c/test_dll.zip

mingw > 4.6.x:
ftp://ftp.equation.com/gcc/

if i'm not missing something, the same should work for reaper extensions.
with the cc not being compatible, introducing member function with a parameter and calling it, causes undefined behaviour between a mingw dll and msvc exe (e.g. not passing ecx as the first parameter).

--

Last edited by liteon; 10-22-2011 at 05:49 PM. Reason: vararg
liteon is offline   Reply With Quote
Old 10-22-2011, 05:48 PM   #2
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

reaper_m3u.dll - example from sdk compiled with mingw is crashing (v 4.10) with the first call to ProjectStateContext::AddLine.

Code:
(434.cd4): Unknown exception - code 000006ba (first chance)
ModLoad: 76980000 76988000   C:\WINDOWS\system32\LINKINFO.dll
ModLoad: 76990000 769b5000   C:\WINDOWS\system32\ntshrui.dll
ModLoad: 76b20000 76b31000   C:\WINDOWS\system32\ATL.DLL
(434.c4c): Unknown exception - code 000006ba (first chance)
(434.cd4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ccc359ff ebx=00000000 ecx=77c3f000 edx=010961c8 esi=ffc938e8 edi=0162b948
eip=006910e3 esp=00125274 ebp=010961c8 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010286
*** WARNING: Unable to verify checksum for image00400000
*** ERROR: Module load completed but symbols could not be loaded for image00400000
image00400000+0x2910e3:
006910e3 8b5010          mov     edx,dword ptr [eax+10h] ds:0023:ccc35a0f=????????
the above example now includes a variadic virtual method (same as the ProjectStateContext interface), but it cannot reproduce the crash, which suggests that varargs are working. also variadic members should be using __cdecl in general, since the caller needs to clear the stack before the arguments are passed:
http://msdn.microsoft.com/en-us/libr...bw(VS.71).aspx

i'm assuming that that ProjectStateContext::AddLine is resizing a char buffer on each call to update the project format string, but cannot exactly determine what is the issue here between compilers.

other tested functions seem to work from reaper_m3u.dll:
ProjectStateContext::GetLine
PCM_source::GetLength
...


--

Last edited by liteon; 10-22-2011 at 06:16 PM. Reason: 4.10
liteon is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 08:18 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.