Tuesday, October 01, 2019

Why you should take warning C4013 seriously

A piece of legacy C code that was working fine on Windows 7 64 bit was crashing on Windows 10 64 bit. After a couple of days of detective work I found out that it was related to warning C4013: 'malloc' undefined; assuming extern returning int. Simplified version of the code is as follows:
When I build the code with Visual Studio with the x64 platform selected, I get two warnings:

When I run it on a Windows 10 64 bit PC with x64 setting, I get access violation:

When I look at the variable m->a, I see "unable to read memory":

To solve the problem, I have to include stdlib.h:

Explanation: You get away with it on a 32 bit build because the size of int is the same as the size of a pointer. On the other hand if you build your program as 64 bit program, the warnings become really relevant, because now pointers are 64 bits wide, but as the compiler assumes that malloc etc. return ints (i.e. 32 bit instead of 64 bit for pointer) everything get messed up.

Why does the compiler issue a warning instead of an error, even tough it cannot find definition of malloc: The symbol foo, without a declaration, is totally unknown to the compiler. Because it just compiles your code, but it's not responsible for the linkage of your symbols (I say symbols because this can include variables and functions)... The linker sees the symbol foo(4 bytes) and will start to look for any corresponding definitions of foo(4 bytes). And if it finds that (say, in another module of yours, or in the libc of your system, or as syscall wrapper), then the linker is content and it will create the executable.

When I commented out the stdio.h include, I got C4013 warnings for both printf() and getchar() as expected, but I also got a linker error for printf: "LNK 2019 unresolved external symbol". When I looked inside stdio.h for their definitions, I saw that the definition of printf was more complex and contained __CRTDECL, defined in vcruntime.h which I think is part of visual studio specific libraries, not "core" C/C++. vcruntime.h resides under Program Files (x86)/Microsoft Visual Studio 14.0/VC/... folders, while stdio.h resides under Program Files (x86)/Windows Kits/10... folders. I guess the linker always looks into the standard libraries, even if you don't include their headers.

Why is it crashing on Windows7 64 bit but working fine on Windows 10 64 bit? I don't know :(

Bonus 1: You can configure Visual Studio to treat 4013 warning as error so that the build stops.

Bonus 2: All the different reasons that can lead to a LNK2019 unresolved external symbol error. Recently I got it because I tried to use a cpp function from a c file.

Music: Internal Conflict (Black Mesa: Xen Soundtrack) - Joel Nielsen

No comments: