Diagnosing a problem with calling conventions


A commenter asks for help with an unresolved external. One of my goals is to give you more insight into how things work so you can solve problems yourself. This particular problem - resolving the error "Undefined symbol: '__stdcall(0) pl_pvcam_init (_pl_pvcam_init@0)' referenced from '_main' in Acquisition.c:15" is one example of something you can solve with the tips you've already learned.

First, let's look at the unresolved external itself. "_pl_pvcam_init@0". From the article this comment was posted to, you can see that the leading underscore and trailing @0 indicate that the function uses the __stdcall calling convention. (This is confirmed by the linker's undeciration of the name.)

So your function "_main" wants the function pl_pvcam_init with the __stdcall calling convention. But it's not found in the library even though you linked to it.

If you look inside the library itself, you'll find the desired symbols with some decoration. Decode that decoration. (My psychic powers tell me that when you do, you'll find that the decoration is "_pl_pvcam_init", which is the __cdecl calling convention.)

So now you see the problem. Your code is calling with the __stdcall calling convention, but the function actually uses the __cdecl calling convention. The calling conventions don't match up, so the linkage fails.

The solution, of course, is to fix the declaration of the pl_pvcam_init function in the header file to specify the correct calling convention. My psychic powers tell me that the header file doesn't specify any calling convention at all, which puts it at the mercy of the ambient calling convention for your project, which appears to be __stdcall. But the author of the header file expected __cdecl to be the default calling convention.

Put explicit calling conventions on the functions and you should be all set.

Comments (3)
  1. Anonymous says:

    It should be noted (if it hasn’t been already) that link errors like this are a Very Good Thing. It would suck if this linked properly. It would really suck if the calling convention mismatch was in some rarely exercised (and therefore mostly untested) code path, leading to subtle and oh-so-fun-to-debug stack corruption problems.

  2. Anonymous says:

    Actually you can run into these problems if you dynamically load DLLs and do GetProcAddress. The linker doesn’t catch this sort of problem, however you can compile with /GZ in your debug bits to enable stack checking OR if you want to handle the chores yourself like so:

    #define SAVE_STACK_REGISTER() __asm(mov f_startingESPValue, esp)

    #define #define CHECK_STACK_REGISTER()

    __asm(mov f_endingESPValue, esp);

    if (f_endingESPValue != f_startingESPValue)

    {

    // Alert user of calling convention mismatch – avoid using stack variables :-)

    }

    SAVE_STACK_REGISTER()

    pDllFunction();

    CHECK_STACK_REGISTER()

    Note that you can’t use /GZ with the above method.

  3. Anonymous says:

    ".. ambient calling convention …"

    Man, that’s just poetic!

Comments are closed.