Undecorating names to see why a function can’t be found


Here's a problem inspired by actual events.

When I build my project, it compiles fine, but it fails during the link step with an unresolved external:

program.obj : error LNK2001: unresolved external symbol
"public: virtual wchar_t const * __thiscall
UILibrary::PushButton::GetName(class UILibrary::StringHolder * *)"
(?GetName@PushButton@UILibrary@@UAEPB_WPAPAVStringHolder@2@@Z)

The function I'm trying to call exists in the source code for uilibrary.lib; I'm looking at it right now. And the definition in the source code matches the declaration in the header file:

namespace UILibrary {
 ...
 class PushButton {
 public:
  virtual LPCWSTR GetName(StringHolder **Holder);
 };
 ...
}

Why can't the linker find it? (Other functions in uilibrary.lib link just fine.)

In order to find something, you have to be looking in the right place, and the thing you're looking for actually needs to be there. (And you have to be able to see it when it's there.) The first part, looking in the right place, appears to be addressed by the parenthetical: The linker is definitely looking in uilibrary.lib since it managed to find other things in that library.

Let's look at the second step, then. Is the thing you're looking for really there? I fired up a little hex editor on uilibrary.lib, but you could use strings or, if you really want to get fancy, link /dump /headers. I went looking for "GetName@PushButton" to see if the member function was actually in the library.

And yup, the function is there. But it looks slightly different: ?GetName@PushButton@UILibrary@@UAEPBGPAPAVStringHolder@2@@Z. (See if you can spot the difference.) Aha, the symbol couldn't be found because it indeed doesn't exist! What does exist is something that superficially resembles the symbol we want, but which has different decoration. We ask the undname program to convert this name into something a bit more readable:

C:\> undname ?GetName@PushButton@UILibrary@@UAEPBGPAPAVStringHolder@2@@Z
public: virtual unsigned short const * __thiscall
UILibrary::PushButton::GetName(class UILibrary::StringHolder * *)

Looking carefully at the two functions, we see that the difference is that the one that program.obj is looking for has a return type of wchar_t const *, whereas the one in the library returns a unsigned short const *.

At this point the answer is obvious. The library was compiled with the /Zc:wchar_t-* flag, which disables wchar_t as a native type. When that happens, the Windows header files gives the wchar_t symbol the definition typedef unsigned short wchar_t; On the other hand, the customer's project was being compiled without that switch, in which case wchar_t is a native type and not an alias for unsigned short.

Now you know enough to solve this customer's problem, which is very similar to the previous one:

When I build my project, it compiles fine, but it fails during the link step with an unresolved external:

program.obj : error LNK2019: unresolved external symbol
"long __cdecl UILibrary::Initialize(bool)"
(?Initialize@UILibrary@@YAJ_N@Z)

The function as it exists in the library undecorates as follows:

long __stdcall UILibrary::Initialize(bool)

Note

The undname program and the /Zc:wchar_t- switches are specific to the Microsoft Visual C++ compiler. Naturally, if you use a different compiler, you should use the utility or command line switch appropriate to your compiler. In particular, if you use the Visual Studio development environment, I'm told (but have not tried it myself) that the switch you're looking for is called "Treat wchar_t as a built-in type" on the "C/C++ Language" property page.

Comments (28)
  1. Mark says:

    It’s posts like these that make me realise quite how much more I still need to learn in this industry. Thank you for opening my eyes :)

  2. Nathan_works says:

    Mark, there are  many things in the industry you never have to know and can still be gainfully employed.  Consider most web developers, for example ;)

  3. Alexander Grigoriev says:

    And this is why one should not mess with project settings, unless he really understands the consequences.

  4. AsmGuru62 says:

    I just hit the same issue a few days ago. As soon as wchar_t as native was cleared – all started building as before.

  5. Koro says:

    At my old job I got called upon to fix such problems all the time.

    It is also where I learned about declspec(selectany) and it’s "wonders" (or pains), while trying to export specific template instantiations from a DLL…

  6. quotemstr says:

    We have this problem in unixland too: at least with gcc, wchar_t can either be two or four bytes long. Guess which option a major web browser uses in its compilation, and guess what every other program on the system uses.

  7. Sohail says:

    Atleast you didn’t demangle it in your head. That would scare me.

  8. Trevel says:

    Every time Raymond says "At this point the answer is obvious" I prepare myself to feel stupid.

  9. Jonathan says:

    My immediate thought after reading the title was "calling conventions", which of course is the answer to the second question.

    I didn’t know about undname. I do know that depends.exe does undecoration for exported/imported functions, but that’s only after you managed to link them into DLLs. And if you look at DLLs from, say, a Symantec product, then the undecorations won’t work, since they have their own compiler.

    And declspec(selectany) is the best thing ever. And #pragma once too.

  10. Hmm says:

    In C++ why would you want to use wchar_t as a built in type, after all it is not. It is an integral type which uses one of the other integral types as its underlying type. The size and type of its underlying type is implementation defined.

  11. Michael says:

    @Hmm: You are correct, except in saying that wchar_t is not built-in.  It is true that the C++ standard defines wchar_t to have the same /machine representation/ as an implementation-chosen integer type, but the C++ standard also makes clear that wchar_t must be built into the language and is a distinct type (not a type renaming as typedef does) from any other.  This is in contrast with standard C which does define wchar_t as a typedef, allowing the use of constants of the base type without implicit or explicit coercions.

  12. Paul says:

    I was with you all the way until "At this point the answer is obvious".  I can tell why they’re different, but I would have had no idea where to go to find out why.

    At the risk of being a nitpicker, can anyone tell me why the compiler has that option in the first place?

  13. KJK::Hyperion says:

    Hmm: wchar_t as a native type is required for the standard C++ iostream library. Without wchar_t as a native type, either all pointers to (say) unsigned short would have to be printed as wide-character strings ("%ls" in stdio), or all wide-character strings would have to be printed as pointers ("%p"). Neither is ideal, the point of iostream vs stdio is type safety, and C++ syntax is biased against struct/class in favor of built-in types, so wchar_t was made a built-in

  14. I ran into a very similar problem a couple of weeks ago.  It turned out that I had a function prototype inside of a namespace that forward declared another class inside the prototype as follows:

    <pre>

    namespace NS

    {

     void foo(class X *px)

    }

    </pre>

    Where class X was also defined inside the namespace NS.  When decorating the name of foo(), VS2005 decided that class X was in global scope, which gave a different decorated name of what it actually should have been (with X under NS).  This resulted in strange linker errors.

  15. Dean Harding says:

    "can anyone tell me why the compiler has that option in the first place?"

    *Deep breath* Backwards Compatibility!

    In previous versions of VC++ (I think version 6, but it might’ve been earlier), wchar_t was not a built-in type so you could mix-n-match it with unsigned short. In later versions, it became a proper built-in type, but in order to allow you to more easily compile programs built with the older version, the option exists to go back to the old behaviour.

  16. Worf says:

    I ran across the second issue a few months ago.

    Being that I do WinCE/WinMo on ARM platforms mostly, there is just one true calling convention, and things like __cdecl, __pascal, etc don’t exist. Naturally, this means stuff like WINAPI are #defined to nothing, so WinMain’s prototypes began like "int WinMain(…", and yes, it works fine on ARM (one calling convention).

    Guess what I had to do?

    Compile my code for x86 – spent the next half a dozen builds fixing all these issues…

  17. Chris J. says:

    Regarding this:

    namespace NS

    {

    void foo(class X *px)

    }

    I have three words:

    obsfucation obsfucation obsfucation

    Programmers who code like that should be fired.

  18. Leo Davidson says:

    Chris J, are you complaining about C++ namespaces or have I missed the point of your post?

    If you’ve failed to understand why namespaces are useful then I think it is you who should be fired. Sorry if I’ve misunderstood, though.

  19. SuperKoko says:

    @Leo Davidson:

    Chris J. is refering to Adam Rosenfield comment.

    Forward declaring a class inside a function parameter declaration is obfuscated.

  20. Cheong says:

    It seems that “link /dump /headers” will do the name undecoration automatically for you in the COMDAT part.

    *** Example from nafxcw.lib of atlmfclib ***

    SECTION HEADER #1A

       .bss name

          0 physical address

          0 virtual address

          4 size of raw data

          0 file pointer to raw data

          0 file pointer to relocation table

          0 file pointer to line numbers

          0 number of relocations

          0 number of line numbers

    C0301080 flags

            Uninitialized Data

            COMDAT; sym= “struct ATL::IAtlAutoThreadModule * ATL::_pAtlAutoThreadModule” (?_pAtlAutoThreadModule@ATL@@3PAUIAtlAutoThreadModule@1@A)

            4 byte align

            Read Write

    [Is there an echo in here? I thought I mentioned this in the article. -Raymond]
  21. @Chris J, @Leo Davidson:

    Yes, forward declaring the class inside the function prototype was not a good idea.  I wasn’t the one who wrote the code, I just fixed it.  In my project, we try to use lots of forward declarations to avoid having header files #include lots of header files that they don’t really need.

    And saying that programmers who write code like that should be fired is quite presumptuous.  The guy who wrote that code is an awesome graphics programmer whom I would be very sad to see leave.

  22. Sys64738 says:

    OldNewThing wrote:

    >>> In particular, if you use the Visual Studio development environment, I’m told (but have not tried it myself) that the switch you’re looking for is called “Treat wchar_t as a built-in type” on the “C/C++ Language” property page.

    I’m curious: you don’t use VS IDE?

    You use the Microsoft VC++ compiler without the VS IDE?

    If so, what IDE do you use? (Or you just write code from some simple editor, like Metapad, and invoke the C++ compiler from command-line ?)

    Thanks.

    [The thing that I haven’t tried is the “Treat wchar_t as a built-in type” checkbox. This was a way for me to pass on a tip without having to go to the work of researching and testing it myself. -Raymond]
  23. Raymond, just before I read the answer revolving around /Zc, I looked at the differences and (incorrectly assuming that “unsigned short” and “wchar_t” were the same), I saw the main difference being that the declared function returns non-const and the library function returns const.  Does that not play into it as well?

    [That would also need to be fixed, but in this case it was an error introduced by me during the writing process. -Raymond]
  24. Cheong says:

    > [Is there an echo in here? I thought I mentioned this in the article. -Raymond]

    Emmm… I thought that you said we need another program named “undname” to see the undecorated signature for the function. And what I said is “the link /dump /header” command will show the undecorated function signature as well.

    Not quite an echo, is it?

    [“… or, if you really want to get fancy, link /dump /headers.” -Raymond]
  25. scorpion007 says:

    Yet another alternative is dumpbin /exports, if all you’re looking for is what the library or DLL exports, without all the header information.

  26. Mike Dimmick says:

    @scorpion007: Ah, but you see dumpbin.exe is just a synonym for link.exe.

    No, really: dumpbin, editbin and lib are identical 14.2KB files and all have a description of ‘Microsoft Linker Stub’. That’s about 14KB of C runtime setup and tear-down code and one function called SpawnLinker that calls _wspawnv or _wspawnvp to start a copy of link.exe.

    If Windows (before Vista) supported hard links, and if Windows Installer had a way to create them, it would simply do what numerous Unix utilities have done before, and base its behaviour on the name it was called with. gzip and gunzip spring to mind.

    link /dump /exports is not only the same output as dumpbin /exports, but it uses the same program to do it (and also doesn’t create two processes to do so).

    And yes, the reason for /Zc:wchar_t- to exist is if you need to compile some C++ modules with the new compiler against ones you (or someone else) compiled with the old compiler. C modules, or C++ ones where the declarations were all marked ‘extern "C"’, don’t matter as they don’t have decorated names – you can declare whatever number and types of parameters you like, the linker can’t check them. Not that you should do this, of course.

  27. scorpion007 says:

    Mike, I’m well aware that dumpbin.exe is the same as link /dump. I just used dumpbin as it is shorter to write.

    While ‘dumpbin /exports’ is the same as ‘link /dump /exports’, it is not the same as ‘link /dump /headers’, which is what Raymond originally suggested.

  28. @Mike, Windows XP does support hard links, and according to the docs for CreateHardLink(), so does anything from Windows 2000 Professional onwards.  You can also manipulate hard links from the command line using the built-in ‘fsutil’ program.

Comments are closed.