In article <news:pa******* *************** ******@cis.ohio-state.edu>
Colin Walters <wa*****@cis.oh io-state.edu> writes:
int
always_true_fu nction (void *dummy, ...)
{
return 1;
}
Now, at one point I was calling this via a function pointer that was
defined without the varargs, like this:
typedef int (*testfunc) (void *dummy);
int
main (int argc, char **argv)
{
testfunc f = (testfunc) always_true_fun ction;
if (f (NULL))
return 0;
return 1;
}
This all worked fine up until recently, when I heard from one user who was
trying to run the program on AMD64 was getting very mysterious segfaults.
On my machine (i386, gcc 3.3.2), the above test program works fine. On
his machine (AMD64, gcc 3.3.2 + propolice patch), it segfaults.
It turned out I didn't actually need the varargs, but I'm curious - who is
at fault here? Is my code wrong, or is his gcc broken?
Your code is technically wrong (or "illegal" or any other number of
words that do not really carry the right meaning). Specifically,
the call:
f(NULL)
invokes undefined behavior by calling a function through a pointer
that has the wrong type. In this case, the AMD64 system probably
uses different call sequences for "varying arguments" vs "fixed
arguments", but in principle this can even happen if you call a
function whose actual type is (e.g.) void(void *) instead of
int(void *).
You can correct the call (without any structural changes to the
code itself, such as changing the name and/or type of
always_true_fun ction()) in at least two ways:
if (((int (*)(void *, ...))f)(NULL)) ...
will convert the function pointer stored in "f" back to the correct
type, then (correctly) call the actual function always_true_fun ction();
or you can insert an intermediate function:
int always_true_fun ction(void *dummy, ...) { return 1; }
int adapter_for_alw ays_true_functi on(void *passthrough) {
return always_true_fun ction(passthrou gh);
}
...
testfunc f = adapter_for_alw ays_true_functi on;
The adapter serves as a sort of "pipe fitting" to connect the
"two-inch pipeline" (varying argument function, in this case)
to the "3/4-inch hose" (non-varying call).
The cast method works, but requires great care -- you must always
match the converted-to type just before the call to the actual
type of the target function. This may well defeat the entire
purpose of using function pointers in the first place, so the
adapter method is more general.
It also acts as a kind of lesson: any time you have a pointer cast
in C code, be suspicious. If there is a pointer-cast-free way to
rewrite the code, that second version probably has fewer bugs. :-)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it
http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.