Visual C/C++ incremental linker bug

,

There’s a bug in the Microsoft Visual C/C++ incremental linker: it sometimes fails to update the executable with updated object code.1 To reproduce this using Visual C/C++ 2015 Community Edition,2 create a file a.c with the contents:

#include <stdio.h>
int main()
{
   puts("a");
   return 0;
}

and a file b.c with the contents:

#include <stdio.h>
int main()
{
   puts("b");
   return 0;
}

Then open a Developer Command Prompt for VS2015 and execute this command line:

cl /nologo /Foo /Fee a.c /link /debug >nul & e & cl /nologo /Foo /Fee b.c /link /debug >nul & e

This compiles a.c into the object file o.obj and links it into the executable e.exe, which is run. Then it compiles b.c into the (same) object file o.obj and links it into the (same) executable e.exe, which is run again. We expect to get the output:

a
b

But sometimes (well over half the time in my experiments) the actual output is as follows:

a
a

It seems that the second time that cl was run, it failed to update the executable with the updated object code! What could possibly be going on here? Some exploration of the envelope of the bug gives some clues as to what might be happening.

  1. First, suppose that we change the command line so that b.c is compiled to a different object file:

    cl /nologo /Foo /Fee a.c /link /debug >nul & e & cl /nologo /Fob /Fee b.c /link /debug >nul & e

    Now the output is always as expected.

  2. Second, suppose that we change the command line to put a timeout after the first compilation and before the second:

    cl /nologo /Foo /Fee a.c /link /debug >nul & e & timeout 1 >nul & cl /nologo /Foo /Fee b.c /link /debug >nul & e

    Now the output is always as expected.

  3. Third, leave the command line alone but change b.c so that it says:

       puts("bb");
    

    Now if you run the original command line then the output is always:

    a
    bb
    

    as expected.

It looks to me as though the linker is using an unreliable heuristic to decide whether it needs to update the object code in the executable. Perhaps it is using the object file’s name, timestamp in seconds, and size? This would explain the behaviour in the three cases above:

  1. The object file name explains this case: b.obj is different from o.obj and so the linker knows to update the executable.

  2. The object file timestamp in seconds explains this case: after waiting for the timeout the new object file has a different timestamp and so the linker knows to update the executable.

  3. The object file size explains this case: after changing the puts line, the object file from compiling b.c is one byte longer than the object file from a.c and so the linker knows to update the executable.

I reported this to Microsoft as issue 3104624.


  1.  I found this because the Memory Pool System test suite was occasionally failing on Windows, and this bug turned out to be the cause.

  2.  The procedure is similar in Visual C/C++ 2010, which also has the bug, except that instead of “Developer Command Prompt for VS2015”, use “Visual Studio 2010 Command Prompt”. It’s likely that the bug is also present in other versions of Visual C/C++, but 2010 and 2015 are the ones I’ve tested.