Skip to content

Commit e3ff370

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. This commit was specifically written for #16654 but there are dozens or 100s of them #19112
1 parent 4acc9fb commit e3ff370

File tree

6 files changed

+129
-23
lines changed

6 files changed

+129
-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.38';
7+
our $VERSION = '1.39';
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/perldelta.pod

+1-3
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ XXX Remove this section if F<Porting/corelist-perldelta.pl> did not add any cont
125125

126126
=item *
127127

128-
L<XXX> has been upgraded from version A.xx to B.yy.
129-
130-
XXX If there was something important to note about this change, include that here.
128+
L<XS::APItest> has been upgraded from version 1.38 to 1.39.
131129

132130
=item *
133131

pod/perldiag.pod

+4-1
Original file line numberDiff line numberDiff line change
@@ -5198,7 +5198,10 @@ redirected it with select().)
51985198
=item Perl API version %s of %s does not match %s
51995199

52005200
(F) The XS module in question was compiled against a different incompatible
5201-
version of Perl than the one that has loaded the XS module.
5201+
version of Perl than the one that has loaded the XS module. The XS module
5202+
name will be replaced by a C<.c> file name, that serves as a hint to the module
5203+
name, if the internal differences between the 2 incompatible versions
5204+
are large enough to prevent obtaining the module name.
52025205

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

util.c

+53-17
Original file line numberDiff line numberDiff line change
@@ -5546,6 +5546,7 @@ Perl_xs_handshake(const U32 key, void * v_my_perl, const char * file, ...)
55465546
void * got;
55475547
void * need;
55485548
const char *stage = "first";
5549+
bool in_abi_mismatch = 0;
55495550
#ifdef MULTIPLICITY
55505551
dTHX;
55515552
tTHX xs_interp;
@@ -5585,10 +5586,10 @@ Perl_xs_handshake(const U32 key, void * v_my_perl, const char * file, ...)
55855586
stage = "second";
55865587
if(UNLIKELY(got != need)) {
55875588
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);
5589+
if(got != (void *)HSf_NOCHK) {
5590+
in_abi_mismatch = 1;
5591+
goto die_mismatched_rmv_c_args;
5592+
}
55925593
}
55935594

55945595
if(key & HSf_SETXSUBFN) { /* this might be called from a module bootstrap */
@@ -5599,32 +5600,67 @@ Perl_xs_handshake(const U32 key, void * v_my_perl, const char * file, ...)
55995600
if (PERLDB_LINE_OR_SAVESRC)
56005601
(void)gv_fetchfile(file); */
56015602
}
5602-
5603+
die_mismatched_rmv_c_args:
56035604
if(key & HSf_POPMARK) {
5604-
ax = POPMARK;
5605-
{ SV **mark = PL_stack_base + ax++;
5606-
{ dSP;
5607-
items = (Stack_off_t)(SP - MARK);
5608-
}
5605+
/* Don't touch the local unthreaded or threaded Perl stack if mismatched
5606+
ABI. The pointers inside the mark stack vars and @_ vars are
5607+
uninited data if we are executing in a surprise 2nd, auto-loaded by OS,
5608+
diff maj ver, libperl.so/.dll.*/
5609+
if(in_abi_mismatch) {
5610+
ax = Stack_off_t_MAX; /* silence CC & poison */
5611+
items = Stack_off_t_MAX;
5612+
}
5613+
else {
5614+
ax = POPMARK;
5615+
SV **mark = PL_stack_base + ax++;
5616+
dSP;
5617+
items = (Stack_off_t)(SP - MARK);
56095618
}
56105619
} else {
56115620
items = va_arg(args, Stack_off_t);
56125621
ax = va_arg(args, Stack_off_t);
56135622
}
5614-
assert(ax >= 0);
5615-
assert(items >= 0);
5623+
5624+
if(!in_abi_mismatch) {
5625+
assert(ax >= 0);
5626+
assert(items >= 0);
5627+
}
5628+
56165629
{
56175630
U32 apiverlen;
56185631
assert(HS_GETAPIVERLEN(key) <= UCHAR_MAX);
56195632
if((apiverlen = HS_GETAPIVERLEN(key))) {
56205633
char * api_p = va_arg(args, char*);
56215634
if(apiverlen != sizeof("v" PERL_API_VERSION_STRING)-1
56225635
|| memNE(api_p, "v" PERL_API_VERSION_STRING,
5623-
sizeof("v" PERL_API_VERSION_STRING)-1))
5624-
Perl_croak_nocontext("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-
}
5636+
sizeof("v" PERL_API_VERSION_STRING)-1)) {
5637+
if(in_abi_mismatch)
5638+
noperl_die("Perl API version %s of %s does not match %s",
5639+
api_p, file, "v" PERL_API_VERSION_STRING);
5640+
else/* use %s for SV * for string literal reuse with abv */
5641+
Perl_croak_nocontext("Perl API version %s of %s does not match %s",
5642+
api_p, SvPV_nolen(PL_stack_base[ax + 0]),
5643+
"v" PERL_API_VERSION_STRING);
5644+
} /* memcmp() */
5645+
} /* if user wants API Ver Check (xsubpp default is on ) */
5646+
5647+
/* The gentler error above couldn't be shown. Maybe the 2 API ver strings DID
5648+
str eq match. So its a interp build time/Configure problem, or 3rd party patches
5649+
by OS vendors. Or system perl vs /home "local perl" battles.
5650+
No choice but to show the full hex debugging info and die.
5651+
5652+
On Unix, the 1st correct original libperl/perl.bin, on ELF, is irreverisbly
5653+
corrupted now. B/c new Perl API C func bodies have already been
5654+
linked/injected into the 1st perl.bin from the 2nd incompatible "surprise"
5655+
new libperl.so/.dll in the same proc.
5656+
5657+
A quick process exit using only libc APIs, no perl APIs, is only fool proof,
5658+
cross platform way to prevent a SEGV.
5659+
*/
5660+
if(in_abi_mismatch)
5661+
noperl_die("%s: loadable library and perl binaries are mismatched"
5662+
" (got %s handshake key %p, needed %p)\n",
5663+
file, stage, got, need);
56285664
}
56295665
{
56305666
U32 xsverlen = HS_GETXSVERLEN(key);

0 commit comments

Comments
 (0)