Skip to content

Commit f077dc5

Browse files
committed
util.c: Perl_xs_handshake print API ver mismatch before interp mismatch
-this fatal error is much more common by general users than I (orig author) anticipated when I added this check in 5.21.6/2014. I assumed Unix land never had ABI/SEGVing or upgrade problems previous. I wrote the code for my dev style, and my personal setup as test cases, and test cases with Win32-isms. If other OSes get bad-ABI caught, its a plus, but I thought they wouldn't. -the hexadecimal handshake keys were intended to be a debug tool for core devs hacking on something and for XS authors with very complicated Makefile.PL s. To catch -D CCFLAGS arg dropouts on the way to the final cmd line invocation of the CC. -I say the handshake keys are a terrible UI for general "power users" and non-coder sys admins -the Perl API version strings ARE available, even with mismatched interp struct sizes, and those are much more user friendly to print as a error. It should be obvious that from now on, non-power users can figure out on their own (no community help) that a way to "fix" XS boot handshake is to force "reinstall" the "left side perl" or "right side perl" through the OS Pkg Manager. -after this commit, much more often! but not always, users will see a "Perl API 5.X.Y against 5.X+1.Y is incompatible" fatal message instead of the those Core-dev only undocumented hex handshake keys. Sadly the technical P5P debug info is now gone/lost/hidden if "Perl API 5.X.Y against 5.X+1.Y is incompatible" fatal message executes. -core devs, obv will have v5.X.Y matching v5.X.Y in blead perl, so they will still get the handshake keys hex numbers. Since API strings are same. -Package name will get downgraded to "Foo.c" if interp size is wrong, or 2 libperls in 1 proc happens. But the major improvement is showing left and right side Perl API version info. -The POD text is very wordy and detailed, since it has been observed over time, some Perl users, do not know Perl's backend implementation is written in the ISO C language. Or other Perl users on various internet forums or social media, do not know what the term XS code is. While they can sucessful write and debug private personal Perl 5 code, they only read the POD of CPAN modules and only use public documented APIs of CPAN modules, and rarely or never look at "private source" of CPAN modules. Therefore this group of users truly do not know MANY MANY p5p core modules and CPAN modules, make call outs to another language (C), and are unable to troubleshoot a .so file on their filing system is responsible for the error. Since they do not know about XS code concept, their troubleshooting goes very wrong as they keeping looking and keep incorrect diagnosing the problem to ASCII text somewhere in the Perl ecosystem. Either Perl source code, they wrote, or CPAN Perl source code, or a CSV, YAML or JSON file related to Perl. Make it clear, that some "Perl 5 modules" written in ASCII text in Perl 5 lang, depend on "foreign" C code and "foreign" .so/.dll files at runtime. First time Perl coders, can mistakenly assume the Perl 5 interpreter has JIT and self bootstraps to OS binaries from only Perl 5 source like Google Chrome V8 and Raku. So they guess, as first time users, Perl 5 also does it and has no dependency on legacy technologies like C or C++. This commit was specifically written for #16654 but there are dozens or 100s of them #19112
1 parent e5ef137 commit f077dc5

File tree

5 files changed

+169
-23
lines changed

5 files changed

+169
-23
lines changed

ext/XS-APItest/APItest.pm

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use strict;
44
use warnings;
55
use Carp;
66

7-
our $VERSION = '1.41';
7+
our $VERSION = '1.42';
88

99
require XSLoader;
1010

ext/XS-APItest/APItest.xs

+59
Original file line numberDiff line numberDiff line change
@@ -1941,6 +1941,65 @@ xsreturn_empty()
19411941
PPCODE:
19421942
XSRETURN_EMPTY;
19431943

1944+
void
1945+
test_mismatch_xs_handshake_api_ver(...)
1946+
ALIAS:
1947+
test_mismatch_xs_handshake_bad_struct = 1
1948+
test_mismatch_xs_handshake_bad_struct_and_ver = 2
1949+
PPCODE:
1950+
if(ix == 0) {
1951+
#ifdef MULTIPLICITY
1952+
Perl_xs_handshake(HS_KEYp(sizeof(PerlInterpreter),
1953+
TRUE, NULL, FALSE,
1954+
sizeof("v1.1337.0")-1,
1955+
sizeof("")-1),
1956+
HS_CXT, __FILE__, items, ax,
1957+
"v1.1337.0");
1958+
#else
1959+
Perl_xs_handshake(HS_KEYp(sizeof(struct PerlHandShakeInterpreter),
1960+
FALSE, NULL, FALSE,
1961+
sizeof("v1.1337.0")-1,
1962+
sizeof("")-1),
1963+
HS_CXT, __FILE__, items, ax,
1964+
"v1.1337.0");
1965+
#endif
1966+
}
1967+
else if(ix == 1) {
1968+
#ifdef MULTIPLICITY
1969+
Perl_xs_handshake(HS_KEYp(sizeof(PerlInterpreter)+1,
1970+
TRUE, NULL, FALSE,
1971+
sizeof("v" PERL_API_VERSION_STRING)-1,
1972+
sizeof("")-1),
1973+
HS_CXT, __FILE__, items, ax,
1974+
"v" PERL_API_VERSION_STRING);
1975+
#else
1976+
Perl_xs_handshake(HS_KEYp(sizeof(struct PerlHandShakeInterpreter)+1,
1977+
FALSE, NULL, FALSE,
1978+
sizeof("v" PERL_API_VERSION_STRING)-1,
1979+
sizeof("")-1),
1980+
HS_CXT, __FILE__, items, ax,
1981+
"v" PERL_API_VERSION_STRING);
1982+
#endif
1983+
}
1984+
else {
1985+
#ifdef MULTIPLICITY
1986+
Perl_xs_handshake(HS_KEYp(sizeof(PerlInterpreter)+1,
1987+
TRUE, NULL, FALSE,
1988+
sizeof("v1.1337.0")-1,
1989+
sizeof("")-1),
1990+
HS_CXT, __FILE__, items, ax,
1991+
"v1.1337.0");
1992+
#else
1993+
Perl_xs_handshake(HS_KEYp(sizeof(struct PerlHandShakeInterpreter)+1,
1994+
FALSE, NULL, FALSE,
1995+
sizeof("v1.1337.0")-1,
1996+
sizeof("")-1),
1997+
HS_CXT, __FILE__, items, ax,
1998+
"v1.1337.0");
1999+
#endif
2000+
}
2001+
2002+
19442003
MODULE = XS::APItest:Hash PACKAGE = XS::APItest::Hash
19452004

19462005
void

ext/XS-APItest/t/call.t

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use strict;
1111

1212
BEGIN {
1313
require '../../t/test.pl';
14-
plan(544);
14+
plan(547);
1515
use_ok('XS::APItest')
1616
};
1717
use Config;
@@ -385,3 +385,13 @@ eval { my @a = sort f 2, 1; $x++};
385385
print "x=$x\n";
386386
EOF
387387
}
388+
389+
fresh_perl_like('use XS::APItest;'
390+
.'XS::APItest::XSUB::test_mismatch_xs_handshake_api_ver("Dog");'
391+
, qr/\QPerl API version v1.1337.0 of Dog does not match\E/);
392+
fresh_perl_like('use XS::APItest;'
393+
.'XS::APItest::XSUB::test_mismatch_xs_handshake_bad_struct("Dog");'
394+
, qr/\Q loadable library and perl binaries are mismatched (got first handshake\E/);
395+
fresh_perl_like('use XS::APItest;'
396+
.'XS::APItest::XSUB::test_mismatch_xs_handshake_bad_struct_and_ver("Dog");'
397+
, qr/\QPerl API version v1.1337.0 of APItest.xs does not match\E/);

pod/perldiag.pod

+28-2
Original file line numberDiff line numberDiff line change
@@ -3689,7 +3689,9 @@ does when displayed.
36893689
(P) A dynamic loading library C<.so> or C<.dll> was being loaded into the
36903690
process that was built against a different build of perl than the
36913691
said library was compiled against. Reinstalling the XS module will
3692-
likely fix this error.
3692+
likely fix this error. This error is a less commonly seen subset of
3693+
L<Perl API version|perldiag/"Perl API version %s of %s does not match %s">
3694+
error.
36933695

36943696
=item Locale '%s' contains (at least) the following characters which
36953697
have unexpected meanings: %s The Perl program will use the expected
@@ -5281,7 +5283,31 @@ redirected it with select().)
52815283
=item Perl API version %s of %s does not match %s
52825284

52835285
(F) The XS module in question was compiled against a different incompatible
5284-
version of Perl than the one that has loaded the XS module.
5286+
version of Perl than the one that has loaded the XS module. If the internal
5287+
differences between the 2 incompatible Perl versions are large enough to
5288+
prevent obtaining the full module name causing this error message, a
5289+
C<.c> file name will be shown in this error message instead of the full module
5290+
name. The C<.c> file name serves as a hint to help identify the module
5291+
causing this error.
5292+
5293+
The term XS module does not mean a C<.pm> file. This error is not directly
5294+
caused by Perl code inside a particular C<.pm> file or C<.pl> file.
5295+
Instead this error is only caused by OS and CPU specific "shared library"
5296+
files created by a C or C++ compiler. This file format is called a
5297+
C<.so>, C<.dll>, C<.dylib>, C<.bundle> or C<.sl> on Perl's most popular
5298+
operating systems. These shared library files are a part of the XS API
5299+
documented in L<perlxs|perlxs>.
5300+
5301+
Each OS has a different file extension or no extension for shared libraries.
5302+
But shared library files on all OSes are non-text, unprintable, binary files
5303+
with raw machine code inside of them created by a C or C++ compiler.
5304+
5305+
The C<.so> or C<.dll> or equivalent is usually loaded by a C<.pm> or C<.pl>
5306+
file making a call to L<DynaLoader|DynaLoader> or L<XSLoader|XSLoader>, which
5307+
then calls OS specific mechanisms to load the shared library file into the Perl
5308+
process. The OS specific mechanism then calls a function or subroutine inside
5309+
the particular C<.so> or C<.dll> file. That particular C<.so> or C<.dll> file
5310+
then throws this error.
52855311

52865312
=item Perl folding rules are not up-to-date for 0x%X; please use the perlbug
52875313
utility to report; in regex; marked by S<<-- HERE> in m/%s/

util.c

+70-19
Original file line numberDiff line numberDiff line change
@@ -5513,7 +5513,7 @@ Perl_my_cxt_init(pTHX_ int *indexp, size_t size)
55135513
The meaning of the varargs is determined the U32 key arg (which is not
55145514
a format string). The fields of key are assembled by using HS_KEY().
55155515
5516-
Under PERL_IMPLICIT_CONTEX, the v_my_perl arg is of type
5516+
Under PERL_IMPLICIT_CONTEXT, the v_my_perl arg is of type
55175517
"PerlInterpreter *" and represents the callers context; otherwise it is
55185518
of type "CV *", and is the boot xsub's CV.
55195519
@@ -5535,7 +5535,17 @@ Perl_my_cxt_init(pTHX_ int *indexp, size_t size)
55355535
(remember that it assumes that the 1st arg is the interp cxt).
55365536
55375537
'file' is the source filename of the caller.
5538-
*/
5538+
5539+
Expansion provisions: Argument char * api_version is private to
5540+
#include "perl.h". EU::MM and XS authors can't modify it. perl.h could
5541+
place an aligned const pointer to a const static C struct before or after
5542+
the C string, or just the later. Or add argument #8 and 1 new bit in U32 key.
5543+
Arg U32 key can't be changed to arg U64 key, on OSes/CPUs with 32bit void*s.
5544+
Some/all 32b CCs will invisibly splice in "U32 key64_upper" as arg 2,
5545+
shifting all other args down the C stack, and breaking ABI compat of this C
5546+
function between any and all old/new permutations of a .xs vs a libperl.
5547+
5548+
See GH PR #22719 for other expansion provisions. */
55395549

55405550
Stack_off_t
55415551
Perl_xs_handshake(const U32 key, void * v_my_perl, const char * file, ...)
@@ -5546,6 +5556,7 @@ Perl_xs_handshake(const U32 key, void * v_my_perl, const char * file, ...)
55465556
void * got;
55475557
void * need;
55485558
const char *stage = "first";
5559+
bool in_abi_mismatch = FALSE;
55495560
#ifdef MULTIPLICITY
55505561
dTHX;
55515562
tTHX xs_interp;
@@ -5561,7 +5572,7 @@ Perl_xs_handshake(const U32 key, void * v_my_perl, const char * file, ...)
55615572
if (UNLIKELY(got != need))
55625573
goto bad_handshake;
55635574
/* try to catch where a 2nd threaded perl interp DLL is loaded into a process
5564-
by a XS DLL compiled against the wrong interl DLL b/c of bad @INC, and the
5575+
by a XS DLL compiled against the wrong interp DLL b/c of bad @INC, and the
55655576
2nd threaded perl interp DLL never initialized its TLS/PERL_SYS_INIT3 so
55665577
dTHX call from 2nd interp DLL can't return the my_perl that pp_entersub
55675578
passed to the XS DLL */
@@ -5585,10 +5596,10 @@ Perl_xs_handshake(const U32 key, void * v_my_perl, const char * file, ...)
55855596
stage = "second";
55865597
if(UNLIKELY(got != need)) {
55875598
bad_handshake:/* recycle branch and string from above */
5588-
if(got != (void *)HSf_NOCHK)
5589-
noperl_die("%s: loadable library and perl binaries are mismatched"
5590-
" (got %s handshake key %p, needed %p)\n",
5591-
file, stage, got, need);
5599+
if(got != (void *)HSf_NOCHK) {
5600+
in_abi_mismatch = TRUE;
5601+
goto die_mismatched_rmv_c_args;
5602+
}
55925603
}
55935604

55945605
if(key & HSf_SETXSUBFN) { /* this might be called from a module bootstrap */
@@ -5600,31 +5611,71 @@ Perl_xs_handshake(const U32 key, void * v_my_perl, const char * file, ...)
56005611
(void)gv_fetchfile(file); */
56015612
}
56025613

5614+
die_mismatched_rmv_c_args:
56035615
if(key & HSf_POPMARK) {
5604-
ax = POPMARK;
5605-
{ SV **mark = PL_stack_base + ax++;
5606-
{ dSP;
5607-
items = (Stack_off_t)(SP - MARK);
5608-
}
5616+
/* Don't touch the local unthreaded or threaded Perl stack if mismatched
5617+
ABI. The pointers inside the mark stack vars and @_ vars are
5618+
uninitialized data if we are executing in an unexpected second
5619+
libperl.{so,dll} with a different major version. The second libperl
5620+
possibly was auto-loaded by the OS, as a dependency of the out of
5621+
date XS shared library file. */
5622+
if(in_abi_mismatch) {
5623+
ax = Stack_off_t_MAX; /* silence CC & poison */
5624+
items = Stack_off_t_MAX;
5625+
}
5626+
else {
5627+
ax = POPMARK;
5628+
SV **mark = PL_stack_base + ax++;
5629+
dSP;
5630+
items = (Stack_off_t)(SP - MARK);
56095631
}
56105632
} else {
56115633
items = va_arg(args, Stack_off_t);
56125634
ax = va_arg(args, Stack_off_t);
56135635
}
5614-
assert(ax >= 0);
5615-
assert(items >= 0);
5636+
5637+
if(!in_abi_mismatch) {
5638+
assert(ax >= 0);
5639+
assert(items >= 0);
5640+
}
5641+
56165642
{
56175643
U32 apiverlen;
56185644
assert(HS_GETAPIVERLEN(key) <= UCHAR_MAX);
56195645
if((apiverlen = HS_GETAPIVERLEN(key))) {
56205646
char * api_p = va_arg(args, char*);
56215647
if(apiverlen != sizeof("v" PERL_API_VERSION_STRING)-1
56225648
|| memNE(api_p, "v" PERL_API_VERSION_STRING,
5623-
sizeof("v" PERL_API_VERSION_STRING)-1))
5624-
croak("Perl API version %s of %" SVf " does not match %s",
5625-
api_p, SVfARG(PL_stack_base[ax + 0]),
5626-
"v" PERL_API_VERSION_STRING);
5627-
}
5649+
sizeof("v" PERL_API_VERSION_STRING)-1)) {
5650+
if(in_abi_mismatch)
5651+
noperl_die("Perl API version %s of %s does not match %s",
5652+
api_p, file, "v" PERL_API_VERSION_STRING);
5653+
else {/* use %s for SV * for string literal reuse with above */
5654+
SV * package_sv = PL_stack_base[ax + 0];
5655+
Perl_croak_nocontext("Perl API version %s of %s does not match %s",
5656+
api_p, SvPV_nolen_const(package_sv),
5657+
"v" PERL_API_VERSION_STRING);
5658+
}
5659+
} /* memcmp() */
5660+
} /* if user wants API Ver Check (xsubpp default is on ) */
5661+
5662+
/* The gentler error above couldn't be shown. Maybe the 2 API ver strings DID
5663+
str eq match. So its an interpreter build time/Configure problem, or 3rd party
5664+
patches by OS vendors. Or system perl vs /home "local perl" battles.
5665+
No choice but to show the full hex debugging info and die.
5666+
5667+
On Unix, the 1st correct original libperl/perl.bin, on ELF, is irreversibly
5668+
corrupted now because new Perl API C func function have already been
5669+
linked/injected into the 1st perl.bin from the 2nd incompatible "surprise"
5670+
new libperl.so/.dll in the same process.
5671+
5672+
A quick process exit using only libc APIs, no perl APIs, is the only fool proof,
5673+
cross platform way to prevent a SEGV.
5674+
*/
5675+
if(in_abi_mismatch)
5676+
noperl_die("%s: loadable library and perl binaries are mismatched"
5677+
" (got %s handshake key %p, needed %p)\n",
5678+
file, stage, got, need);
56285679
}
56295680
{
56305681
U32 xsverlen = HS_GETXSVERLEN(key);

0 commit comments

Comments
 (0)