Less bugs through compiler optimizations

Stupid bug day !
// Get a symbol
void* sym = dlsym(handle, "function1");
// Cast it to correct signature
double (*fn)() = sym;
// Call it
// Get another symbol
void* sym2 = dlsym(handle, "function2");

// Cast it to correct signature
SomeStruct (*fn2)(float, float, float) = sym2;
// Call it
fn(1, 2, 3);

// Misassign new symbol to first pointer
fn = sym2;
// Call uninitialized function pointer
fn2(1, 2, 3);

And *BUG* ! Hmm … strange … let's try that in a Release build, one of my Stupid Voodoo Programming Tactiques, and … it works ! I stare at the code for a stupid amount of time, dumbfounded, wondering the obvious bug lies. Oh. There. I'm getting a new function pointer but calling the former one with the new signature … oups ! I'm getting a new function pointer but assigning it to the first one, then calling that uninitialized pointer.

How come it works in Release but not in Debug ? In Debug, GCC optimizes nothing and allocates two function pointers. In Release, GCC allocates one function pointer and uses it in both calls. Therefore calling fn(1, 2, 3) equals calling fn2(1, 2, 3); : that's Invisible Release Build Bug.

Moral of the day : always Release builds ! :)

EDIT I messed up while cleaning the code for this post. I even messed up the diagnostic, but … not the explanation ! :) To sum it up : XCode won't warn you when calling uninitialized function pointers.

Moral2: You might want to crank up warnings to the highest level. In that case, you would have gotten a warning that you assigned to fn2 but never used the value.

(Not sure if gcc does that - spending too much time in VS.NET land these days)

Patrick Geiller
2008 07 02

Argh … I messed up while copying and cleaning up the code for posting. I didn't assign to fn2 and then I called that undefined function pointer. It looked like this :

// No assignment
SomeStruct (*fn2)(float, float, float);
// Misassign to first pointer
fn = sym2;
// Call of undefined pointer
fn2(1, 2, 3);

GCC does catch the bug in the post, but not that one.

2008 07 02

Should you have declared fn as 'double (*fn)(void) = sym;' (note the void in between the parenthesis), I expect the compiler would have detected an error in any kind of build; wouldn't it?

Patrick Geiller
2008 07 02

I tried adding the void, to no avail. The problem doesn't seem to be the first function definition, but that you can call any function pointer that hasn't been initialized :

double (*fn)(void);

will crash without any compile-time warning.

2008 07 02

Patrick: Yes, that will crash. But the compiler should raise an error if you try to call the function with a different number of parameters, as your original code did. In fact, I tried to compile the following with gcc and got an error:

double (*fn)(void);
Patrick Geiller
2008 07 02

I do get an error in this case. However, the original post is wrong — see my first comment, I copied the code and over cleaned it up to make it shorter.

Follow me on Twitter
Planet Cocoa

2011 02 22Distance field
2010 07 202Binding through NSApp
2010 05 122Forwarding invocations
2010 02 272Core Image black fringes
2010 02 21Quickest Way to Shell
2010 02 08Who's calling ?
2009 09 2138 ways to use Blocks in Snow Leopard
2009 08 182Bracket Mess
2009 08 124Taming JavascriptCore within and without WebView
2009 04 15Debugging with Activity Monitor
2009 03 25How Core Image Color Tracking works
2009 03 1510Custom NSThemeFrame
2009 03 10Which framework is running ?
2009 03 074CoreUI can paint pretty big
2009 02 18Localization with functions
2009 01 30Did you forget to nest alloc and init?
2009 01 16JSCocoa on the iPhone
2009 01 11Mixing WebView and JavascriptCore
2009 01 09Badge overflow
2009 01 09Find your Garbage Collection leaks with Instruments

Powered by MediaWiki