While I rarely write about obscure encouters I have with perl -- as most are rather obscure anyway, this one is a deviation from most established docs, so I thought I'd share.
Splitting up an XS file into multiple XS files and chaining their bootstraps can cause perl to segfault... I just followed the directions here: MakeMaker FAQ.
So, you've got this set of modules you are writing Base, Base::Foo, Base::Bar, and Base::Quux and all require XS. You could put them all in one xs file as:
MODULE = Base PACKAGE = Base PREFIX=BASE
SV *BASE_cool_func() ....
....
MODULE = Base PACKAGE = Base::Foo PREFIX=BASE_FOO
SV *BASE_FOO_cool_func() ....
...
MODULE = Base PACKAGE = Base::Bar PREFIX=BASE_BAR
SV *BASE_BAR_cool_func() ....
...
MODULE = Base PACKAGE = Base::Quux PREFIX=BASE_QUUX
SV *BASE_QUUX_cool_func() ....
...
And in our Base.pm file we have:
require DynaLoader;
use vars qw ( $VERSION );
$VERSION = "1.0";
bootstrap Base $VERSION;
That bootstrap is the magic that links all those XS function stubs back into perl so they can be called. Everything is fine right? Yes. But, as the thing grows and we want a lot of XS code we need to split it into multiple xs files. This is fine, and MakeMaker supports this natively. However, you need to be able to chain the bootstrapping so that when you bootstrap Base, it bootstraps all the other xs files for you.
Now, the MakeMaker FAQ says you can do this by adding a BOOT: section in Base.xs as:
BOOT:
boot_Base__Foo(aTHX_ cv);
We have three modules and as such I extrapolated that to be:
BOOT:
boot_Base__Foo(aTHX_ cv);
boot_Base__Bar(aTHX_ cv);
boot_Base__Quux(aTHX_ cv);
Well, what was obscurely wrong about the example is entire wrong in the intuitive extrapolation. The issue is that those boot functions (including Base's) use ST(0) and sometimes other portions of the stack and, they return into ST(0). So, when you call boot_Base__Foo(), you have just hosed your argument stack for the subsequent call to boot_base__Bar() and so on. Ouch! SIGSEGV! Perl ain't supposed to do that!
So, the solution is to create a new argument stack for each call. Now my perlapi and perlguts mojo is strong, but not perfect, so I may not have the perfect solution, but this seems to do the job:
#define crutch_stack_wrap(directive) do { \
PUSHMARK(SP); \
PUTBACK; \
directive; \
SPAGAIN; \
PUTBACK; \
} while(0)
BOOT:
crutch_stack_wrap(boot_Base__Foo(aTHX_ cv));
crutch_stack_wrap(boot_Base__Bar(aTHX_ cv));
crutch_stack_wrap(boot_Base__Quux(aTHX_ cv));
Bonzer.