I have a source code which builds successfully with zig cc, but not zig build-exe.
What would be the right way to debug this problem ? I guess these two commands use slightly different flags, but cannot figure it out. Here are the compilation results:
makeheaders.c contains some code that causes undefined behavior (UB), specifically in the Hash function. It tries to shift h five bits to the left without checking if the result would fit in an int. This would be fine if h were unsigned, but because int is a signed type, we get UB! (At the bit level, you don’t want to shift 1 bits into or past the sign bit of a signed integer.)
Changing the int to an unsigned int should work:
--- makeheaders.c.bak 2023-11-09 15:30:19.732884800 -0500
+++ makeheaders.c 2023-11-09 15:32:45.913824400 -0500
@@ -487,7 +487,7 @@
** value between 0 and 2**31 - 1
*/
static int Hash(const char *z, int n){
- int h = 0;
+ unsigned int h = 0;
if( n<=0 ){
n = strlen(z);
}
I’m not sure if there’s more problems lurking somewhere.
As for the differences in those three Zig commands:
zig cc -lc makeheaders.c: Debug builds are the default in Zig, and Zig enables Undefined Behavior Sanitizer (UBSan) for debug builds. I guess Zig doesn’t support the fancy runtime library that prints pretty error messages, so instead an illegal instruction is executed (like ud2 on x86).
zig cc -lc -g -O2 makeheaders.c: In this case, Zig parses -O2 and “understands” that you want a release build. It disables UBSan, and your program will hopefully work just fine.
zig build-exe -lc -cflags -g -O2 -- makeheaders.c: I think Zig blindly passes the arguments between -cflags and -- to Clang, so it doesn’t “understand” that you want a release build and leaves UBSan enabled. If you do zig build-exe -lc -O ReleaseFast -cflags -g -- makeheaders.c, UBSan should be disabled.
By the way, I think you can explicitly disable UBSan using -fno-sanitize=undefined for zig cc or -fno-sanitize-c for zig build-exe. -fno-sanitize=undefined should also work in -cflags.
P.S.: Funnily enough, Hash masks out the sign bit before returning to ensure the return value is non-negative. I don’t understand why Hash does that instead of just using unsigned int.
Try using a debugger. That’s how I determined that Hash was problematic. At the very least, using a debugger can help you narrow down the bug to a single function or statement or instruction, which is a good starting point for more investigation :^)