Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XS INTERFACE: token doesn't support calling conventions #14327

Open
p5pRT opened this issue Dec 12, 2014 · 6 comments
Open

XS INTERFACE: token doesn't support calling conventions #14327

p5pRT opened this issue Dec 12, 2014 · 6 comments

Comments

@p5pRT
Copy link

p5pRT commented Dec 12, 2014

Migrated from rt.perl.org#123415 (status was 'open')

Searchable as RT123415$

@p5pRT
Copy link
Author

p5pRT commented Dec 12, 2014

From @bulk88

I tried using INTERFACE: keyword with __stdcall C functions (all MS
libraries/MS syscalls are __stdcall, Win32 Perl uses __cdecl, not
writing any CC C compiler token means implying __cdecl on Win32
compilers, Perl doesn't change that default with compiler flags) and I
got a SEGV from C stack pointer corruption. The fix I figured out is to
redefine XSINTERFACE_CVT. On CPAN grep nobody has ever redefined
XSINTERFACE_CVT before even though it (and INTERFACE:) was added in
1998, which means I am the first.

Also as far as I can tell, I can't rename a XSUB in Perl namespace to be
different in C namespace with INTERFACE: keyword. The only way is make a
Perl XSUB have a different name than C function without PPCODE/CODE is
"PREFIX =", correct?

"fixed" .xs chunk below for example

const void * reset = ResetEvent;
const void * set = SetEvent;

/* otherwise INTERFACE: crashes below since the default is __cdecl */
#undef XSINTERFACE_CVT
#undef XSINTERFACE_CVT_ANON
#define XSINTERFACE_CVT(ret,name) ret (__stdcall *name)()
#define XSINTERFACE_CVT_ANON(ret) ret (__stdcall *)()

MODULE = Win32::Event PACKAGE = Win32::Event

BOOL
handleInBoolOut(event)
HANDLE event
INTERFACE:
PulseEvent reset set

The standard call functions are declared as

#line 2967 "C:\\Program Files\\Microsoft Visual Studio .NET
2003\\VC7\\PlatformSDK\\include\\winbase.h"

__declspec(dllimport)
void
__stdcall DeleteCriticalSection(LPCRITICAL_SECTION
lpCriticalSection);

__declspec(dllimport)
BOOL __stdcall
SetEvent(HANDLE hEvent);

__declspec(dllimport)
BOOL __stdcall
ResetEvent(HANDLE hEvent);

in MS's headers after CPP.

Should I add note to perlxs.pod about INTERFACE's lack of calling
convention support which can cause crashes on Win32 and tell them to
redefine XSINTERFACE_CVT like I did? Or should PXS have some kind of API
for supporting calling conventions? Can anyone think of what API should
look like?

Perl Info

Flags:
    category=library
    severity=low
    module=ExtUtils::ParseXS

Site configuration information for perl 5.21.6:

Configured by Owner at Thu Nov 13 18:20:06 2014.

Summary of my perl5 (revision 5 version 21 subversion 6) configuration:
  Derived from: 5e1acb3ae8d10f0a0404ec4d30afb925b08b53fd
  Ancestor: b255a112693312907754193cdbc1648d9182cb0d
  Platform:
    osname=MSWin32, osvers=5.1, archname=MSWin32-x86-multi-thread
    uname=''
    config_args='undef'
    hint=recommended, useposix=true, d_sigaction=undef
    useithreads=define, usemultiplicity=define
    use64bitint=undef, use64bitall=undef, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cl', ccflags ='-nologo -GF -W3 -Od -MD -Zi -DDEBUGGING -G7 -GL 
-DWIN32 -D_CONSOLE -DNO_STRICT  -DPERL_TEXTMODE_SCRIPTS 
-DPERL_HASH_FUNC_ONE_AT_A_TIME -DNO_MATHOMS -DPERL_IMPLICIT_CONTEXT 
-DPERL_IMPLICIT_SYS -DUSE_PERLIO -D_USE_32BIT_TIME_T',
    optimize='-Od -MD -Zi -DDEBUGGING -G7 -GL',
    cppflags='-DWIN32'
    ccversion='13.10.6030', gccversion='', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234, 
doublekind=3
    d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=8, 
longdblkind=0
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='__int64', 
lseeksize=8
    alignbytes=8, prototype=define
  Linker and Libraries:
    ld='link', ldflags ='-nologo -nodefaultlib -debug  -opt:ref,icf  
-libpath:"c:\perl521\lib\CORE"  -machine:x86'
    libpth="C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\lib"
    libs=oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib  
comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib  
netapi32.lib uuid.lib ws2_32.lib mpr.lib winmm.lib  version.lib 
odbc32.lib odbccp32.lib comctl32.lib msvcrt.lib
    perllibs=oldnames.lib kernel32.lib user32.lib gdi32.lib 
winspool.lib  comdlg32.lib advapi32.lib shell32.lib ole32.lib 
oleaut32.lib  netapi32.lib uuid.lib ws2_32.lib mpr.lib winmm.lib  
version.lib odbc32.lib odbccp32.lib comctl32.lib msvcrt.lib
    libc=msvcrt.lib, so=dll, useshrplib=true, libperl=perl521.lib
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' '
    cccdlflags=' ', lddlflags='-dll -nologo -nodefaultlib -debug  
-opt:ref,icf  -libpath:"c:\perl521\lib\CORE"  -machine:x86'

Locally applied patches:
    uncommitted-changes
    02b4b73f6c4578c66ae7106592964b8853d4db30
    45e05eceb42cd889a80ef0875887fcb62181cbb6
    225b72a0c1e8cf621f10ed445e0260410082bfc6
    7f871da7cac3993a56343b032f9d7eff91422224
    0b68901531d0a01506365798f883b5f08b1adb89
    a87204c1df9892bf05451407ab107a76ece4810b
    5e1acb3ae8d10f0a0404ec4d30afb925b08b53fd


@INC for perl 5.21.6:
    C:/perl521/site/lib
    C:/perl521/lib
    .


Environment for perl 5.21.6:
    HOME (unset)
    LANG (unset)
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=C:\perl521\bin;C:\Program Files\Microsoft Visual Studio .NET 
2003\Common7\IDE;C:\Program Files\Microsoft Visual Studio .NET 
2003\VC7\BIN;C:\Program Files\Microsoft Visual Studio .NET 
2003\Common7\Tools;C:\Program Files\Microsoft Visual Studio .NET 
2003\Common7\Tools\bin\prerelease;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\system32\wbem;
    PERL_BADLANG (unset)
    SHELL (unset)


@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2014

From @tonycoz

Can it be done using INTERFACE_MACRO?

#define XSINTERFACE_FUNC_STDCALL(ret,cv,f) ((ret (__stdcall *)())(f))

...

BOOL handleInBoolOut(event)
HANDLE event
INTERFACE:
PulseEvent reset set
INTERFACE_MACRO:
XSINTERFACE_FUNC_STDCALL
XSINTERFACE_FUNC_SET

Tony

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2014

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Dec 15, 2014

From @bulk88

I am not sure if you are proposing a new API or using INTERFACE_MACRO keyword as is.

If as is, not according to the docs which say

This keyword allows one to define an INTERFACE using a different way to extract a function pointer from an XSUB. The text which follows this keyword should give the name of macros which would extract/set a function pointer. The extractor macro is given return type, CV* , and XSANY.any_dptr for this CV* . The setter macro is given cv, and the function pointer.

The default value is XSINTERFACE_FUNC and XSINTERFACE_FUNC_SET . An INTERFACE keyword with an empty list of functions can be omitted if INTERFACE_MACRO keyword is used.

These seems to be written for C++/faux-C++ vtables, MS COM/OLE perhaps (see http://www.codeproject.com/Articles/14117/COM-in-plain-C-Part#premain4 where QueryInterface would be the offset into the vtable, the actual function pointer can never be stored in the CV * because each may/probably has a different QueryInterface func ptr), where the function pointer of what to call isn't store in CV's ANY member, but an offset into a table of function pointers, so the function pointers in the RW vtable can get swaped out after the CV is registered/newXSed. This does NOT affect how the function pointer is invoked, only how the CV's ANY member is converted to a function pointer.

XS_EUPXS(XS_Win32__Event_handleInBoolOut); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Win32__Event_handleInBoolOut)
{
dVAR; dXSARGS;
dXSFUNCTION(BOOL);
if (items != 1)
croak_xs_usage(cv, "event");
{
HANDLE event;
BOOL RETVAL;

{
SV * tsv = ST(0);
if (sv_derived_from(tsv, "Win32::Event")) {
IV tmp = SvIV((SV*)SvRV(tsv));
event = INT2PTR(HANDLE, tmp);
}
else
croak("event is not of type Win32::Event");
}
;
XSFUNCTION = XSINTERFACE_FUNC(BOOL,cv,XSANY.any_dptr);

RETVAL = XSFUNCTION(event);
ST(0) = boolSV(RETVAL);
}
XSRETURN(1);
}

After CPP.

static void XS_Win32__Event_handleInBoolOut(PerlInterpreter * my_perl, CV * cv);
static void
XS_Win32__Event_handleInBoolOut(PerlInterpreter * my_perl, CV * cv)
{
extern int Perl___notused(void);
SV **sp = (my_perl->Istack_sp);
I32 ax = (*(my_perl->Imarkstack_ptr)--);
SV **mark = (my_perl->Istack_base) + ax++;
I32 items = (I32) (sp - mark);
>>>>>>>>>>>>>>THIS WONT CHANGE<<<<<<<<<<<<<<<<<<<
BOOL(__stdcall * XSFUNCTION) ();
if (items != 1)
Perl_croak_xs_usage(cv, "event");
{
HANDLE event;
BOOL RETVAL;

{
SV *tsv = (my_perl->Istack_base)[ax + (0)];
if (Perl_sv_derived_from(my_perl, tsv, "Win32::Event")) {
IV tmp =
(((((SV *) ((tsv)->sv_u.svu_rv))->sv_flags & (0x00000100 | 0x00200000)) ==
0x00000100) ? ((XPVIV *) ((SV *) ((tsv)->sv_u.svu_rv))->sv_any)->xiv_u.
xivu_iv : Perl_sv_2iv_flags(my_perl, (SV *) ((tsv)->sv_u.svu_rv), 2));
event = (HANDLE) (tmp);
} else
Perl_croak_nocontext("event is not of type Win32::Event");
}
;
>>>>>>>>>>IF I USE INTERFACE_MACRO KEYWORD I'D CHANGE ONLY THE LINE DIRECTLY BELOW<<<<<<<<<<<
XSFUNCTION = ((BOOL(__stdcall *) ()) (((XPVCV *) ((void *) ((cv)->sv_any)))->xcv_start_u.xcv_xsubany.any_dptr));
>>>>>>>>>>>>>>THIS LINE BELOW WONT CHANGE<<<<<<<<<<<<<<<<<<<
RETVAL = XSFUNCTION(event);
(my_perl->Istack_base)[ax + (0)] = ((RETVAL) ? &(my_perl->Isv_yes) : &(my_perl->Isv_no));
}
do {
const IV tmpXSoff = (1);
(my_perl->Istack_sp) = (my_perl->Istack_base) + ax + (tmpXSoff - 1);
return;
} while (0);
}

Now lets look at macro for declaration of function pointer.

"dXSFUNCTION(BOOL);"

#define dXSFUNCTION(ret) XSINTERFACE_CVT(ret,XSFUNCTION)

XSINTERFACE_CVT(BOOL,XSFUNCTION);

#define XSINTERFACE_CVT(ret,name) ret (*name)()

BOOL (*XSFUNCTION)();

I tried your suggestion, it still crashed as c stack pointer corruption.

#define MYXSINTERFACE_FUNC(ret,cv,ptr) (ret(__stdcall *)())ptr
.....................
BOOL
handleInBoolOut(event)
HANDLE event
INTERFACE_MACRO:
MYXSINTERFACE_FUNC
XSINTERFACE_FUNC_SET
INTERFACE:
PulseEvent reset set

CPP output of above XS

static void XS_Win32__Event_handleInBoolOut(PerlInterpreter* my_perl , CV* cv);
static void XS_Win32__Event_handleInBoolOut(PerlInterpreter* my_perl , CV* cv)
{
extern int Perl___notused(void); SV **sp = (my_perl->Istack_sp); I32 ax = (*(my_perl->Imarkstack_ptr)--); SV **mark = (my_perl->Istack_base) + ax++; I32 items = (I32)(sp - mark);
>>>>>>>>>>>>>>>>no __stdcall here<<<<<<<<<<<<<<<<<<<<<
BOOL (*XSFUNCTION)();
if (items != 1)
Perl_croak_xs_usage(cv, "event");
{
HANDLE event;
BOOL RETVAL;

{
SV * tsv = (my_perl->Istack_base)[ax + (0)];
if (Perl_sv_derived_from(my_perl, tsv,"Win32::Event")) {
IV tmp = (((((SV*)((tsv)->sv_u.svu_rv))->sv_flags & (0x00000100|0x00200000)) == 0x00000100) ? ((XPVIV*) ((SV*)((tsv)->sv_u.svu_rv))->sv_any)->xiv_u.xivu_iv : Perl_sv_2iv_flags(my_perl, (SV*)((tsv)->sv_u.svu_rv),2));
event = (HANDLE)(tmp);
}
else
Perl_croak_nocontext("event is not of type Win32::Event");
}
;
XSFUNCTION = ((BOOL (*)())(((XPVCV*)((void *) ((cv)->sv_any)))->xcv_start_u.xcv_xsubany.any_dptr));

RETVAL = XSFUNCTION(event);
(my_perl->Istack_base)[ax + (0)] = ((RETVAL) ? &(my_perl->Isv_yes) : &(my_perl->Isv_no));
}
do { const IV tmpXSoff = (1); (my_perl->Istack_sp) = (my_perl->Istack_base) + ax + (tmpXSoff - 1); return; } while (0);
}

The result is a syntax error since the func ptrs are of different type.

lib\Win32\Event.c(293) : error C2440: '=' : cannot convert from 'BOOL (__stdcall
*)()' to 'BOOL (__cdecl *)()'

If you are talking about new API, are you suggesting adding 3rd line for INTERFACE_MACRO? Currently it only takes in 2 lines, here is the chunk from ParseXS.

sub INTERFACE_MACRO_handler {
my $self = shift;
$_ = shift;
my $in = $self->merge_section();

trim_whitespace($in);
if ($in =~ /\s/) { # two
($self->{interface_macro}, $self->{interface_macro_set}) = split ' ', $in;
}
else {
$self->{interface_macro} = $in;
$self->{interface_macro_set} = 'UNKNOWN_CVT'; # catch later
}
$self->{interface} = 1; # local
$self->{interfaces} = 1; # global
}

I also noticed like this the special interface macros are ignored with no warning.

BOOL
handleInBoolOut(event)
HANDLE event
INTERFACE:
PulseEvent reset set
INTERFACE_MACRO:
MYXSINTERFACE_FUNC
XSINTERFACE_FUNC_SET

but like this they take effect

BOOL
handleInBoolOut(event)
HANDLE event
INTERFACE_MACRO:
MYXSINTERFACE_FUNC
XSINTERFACE_FUNC_SET
INTERFACE:
PulseEvent reset set

I think that this 2nd bug is a very low priority over not supporting calling conventions

@p5pRT
Copy link
Author

p5pRT commented Dec 15, 2014

From @tonycoz

I was suggesting using them as is. I didn't test it.

I hadn't looked at the generated code, so I didn't see the assignment.

Maybe there could be a third line for INTERFACE_MACRO that lets you supply a macro name for the function type, though that would be messy with the way other macros use the XSINTERFACE_CVT macro.

If you supply a reasonable patch for some approach (not necessarily my approach) for this I'll apply it.

Tony

@toddr
Copy link
Member

toddr commented Feb 13, 2020

@bulk88 how do you want to proceed with this case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants