Evil x86_64 Bugs
smashing.org will soon be hosted on an 64-bit Opteron platform. As I'm planning to serve up a number of live mod_parrot examples on the site, I thought it prudent to make sure mod_parrot worked on x86_64. To my surprise, only one test failed, but boy was it a nasty little bugger.
mod_parrot uses NCI to call Apache's API functions, and you have to provide each function's signature so NCI knows how to pass the various types of arguments and return values between Parrot and Apache. Most of the time there is a one-to-one mapping between datatypes and all is well. If not, you need to write an NCI-friendly wrapper function to get mod_parrot and Apache talking.
The prototype for ap_log_rerror is:
AP_DECLARE(void) ap_log_rerror(const char *file, int line, int level,
apr_status_t status, const request_rec *r,
const char *fmt, ...)
I planned on letting the HLL layer format the message so mod_parrot would only have to pass a single string as the log message. So in a perfect world the last two arguments would always be "%s" and one string in the vararg (...). A constant number of arguments, and all data types supported under NCI. Perfect! Here's how I coded the NCI signature:
dlfunc func, nul, "ap_log_rerror", "vtiiiptt"
That says the function ap_log_error returns nothing (void), and accepts a string (t), 3 integers (iii), a PMC pointer (p) and two strings (tt).
Flash forward to today, and I'm faced with a test that fails only on x86_64. Not only that, but it's failing randomly and smashing the stack along the way so I can't even generate a reliable backtrace!
Then I got to thinking...what touches the stack? va_start(), va_end(), and all their little varargs friends! I immediately remembered reading somewhere that NCI didn't support passing args to variadic functions (though chromatic was threatening to work on it). I assumed that this was still the case and the fact that it worked for me on 32-bit x86 was pure dumb luck.
I recoded things so NCI calls a wrapper function, which then calls ap_log_rerror from C, where varargs are handled appropriately. I'm really glad this wrapper infrastructure was already in place!
The NCI declaration now reads:
dlfunc func, nul, "mpnci_ap_log_rerror", "vJtiiipt"
The C wrapper looks like this:
void mpnci_ap_log_rerror(Parrot_Interp interp, char *file, int line,
int level, apr_status_t status, request_rec *r, char *msg)
{
ap_log_rerror(file, line, level, status, r, "%s", msg);
}
And it all works! Let it be known that mod_parrot is now 64-bit friendly. Full credit goes to the x86_64 platform, which exposes everyone's stupid programming mistakes. Especially mine.
