July 31st, 2020

What does the /ALTERNATENAME linker switch do?

There’s an undocumented switch for the Microsoft Visual Studio linker known as /ALTERNATENAME. Despite being undocumented, people use it a lot. So what is it?

This is effectively a command line switch version of the OLDNAMES.LIB library. When you say /ALTERNATENAME:X=Y, then this tells the linker that if it is looking for a symbol named X and can’t find it, then before giving up, it should redirect it to the symbol Y and try again.

The C runtime library uses this mechanism for various sneaky purposes. For example, there’s a part that goes

BOOL (WINAPI * const _pDefaultRawDllMain)(HANDLE, DWORD, LPVOID) = NULL;
#if defined (_M_IX86)   
#pragma comment(linker, "/alternatename:__pRawDllMain=__pDefaultRawDllMain")   
#elif defined (_M_IA64) || defined (_M_AMD64)   
#pragma comment(linker, "/alternatename:_pRawDllMain=_pDefaultRawDllMain")   
#else  /* defined (_M_IA64) || defined (_M_AMD64) */   
#error Unsupported platform   
#endif  /* defined (_M_IA64) || defined (_M_AMD64) */  

What this does is say, “If you need a symbol called _pRawDllMain, but you can’t find it, then try again with _pDefaultRawDllMain.” If an object file defines _pRawDllMain, then that definition will be used. Otherwise _pDefaultRawDllMain will be used.

Note that /ALTERNATENAME is a linker feature and consequently operates on decorated names, since the linker doesn’t understand compiler-specific name-decoration algorithms. This means that you typically have to use different versions of the /ALTERNATENAME switch, depending on what architecture you are targeting. In the above example, the C runtime library knows that __cdecl decoration prepends an underscore on x86, but not on any other platform.

This use of /ALTERNATENAME here is a way for the compiler to generate hooks into the DLL startup process based on the code being compiled. If there is no _pRawDllMain defined by an object file, then _pDefaultRawDllMain will be used instead, and that version is just a null pointer, which means, “Don’t do anything special.”

This pattern of using the /ALTERNATENAME switch lets you provide a default value for a function or variable, which others can override if they choose. For example, you might do something like this:

void default_error_log() { /* do nothing */ }
// For expository simplification: assume x86 cdecl
#pragma comment(linker, "/alternatename:_error_log=_default_error_log")   

If nobody defines a custom error_log function, then all references to error_log are redirected to default_error_log, and the default error log function does nothing.¹

The C++/WinRT library uses /ALTERNATENAME for a different purpose. The C++/WinRT library wants to support being used both with and without windows.h, so it contains its own declarations for the Windows functions and structures that it needs.

But now there’s a problem: If it is used with windows.h, then there are structure definition errors. Therefore, C++/WinRT needs to give its equivalent declarations of Windows structures some other name, to avoid redefinition errors.

But this in turn means that the function prototypes in the C++/WinRT library need to use the renamed structures, rather than the original Windows structures, in case the C++/WinRT library is used without windows.h. This declaration will in turn create a conflict if the C++/WinRT library is used with windows.h when the real declarations are encountered in windows.h.

The solution is to rename the C++/WinRT version of Windows functions, too. C++/WinRT gives them a WINRT_IMPL_ prefix, so that there is no function declaration collision.

We now have two parallel universes. There’s the windows.h universe, and the C++/WinRT universe, each with their own structures and functions. The two parallel universes are unified by the /ALTERNATENAME directive, which tells the linker, “If you find yourself looking for the function WINRT_IMPL_GetLastError, try again with GetLastError.” Since nobody defines WINRT_IMPL_GetLastError, the “try again” kicks in, and all of the calls to WINRT_GetLastError end up redirected to the operating system GetLastError function, which is what we wanted in the first place.

¹ The more traditional way of doing this (that doesn’t rely on undocumented vendor-specific linker features) is to take advantage of the classical model for linking, specifically the part where you can let an OBJ override a LIB: What you do is define _pRawDllMain in a separate OBJ file that defines nothing except that one variable, and put that OBJ in the C runtime LIB. If the module provides its own definition of _pRawDllMain in an OBJ file, then that definition is used. Otherwise, the linker will search through the LIBs, and eventually it will find the one in the C runtime LIB and use that one.

So why does /ALTERNATENAME exist if you could already get this effect via LIBs, and in way that all linkers support, not just the Microsoft C linker?

C++/WinRT is a header-only library. It has no LIB in which to put these default definitions. It therefore has to use the “command line switch version of a LIB”.

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

7 comments

Discussion is closed. Login to edit/delete existing comments.

  • Jonathan Wilson

    The question I have is why C++/WinRT needs to support being used without windows.h in the first place…
    What’s the use case for that?

    • Raymond ChenMicrosoft employee Author

      windows.h is a huge header file, and UWP apps can get a lot done using only the classes provided by the Windows Runtime. It also allows C++/WinRT to be compiled by a wider range of compilers, and it permits C++/WinRT to be more easily ported to other platforms.

  • 紅樓鍮

    How does C++/WinRT, being part of the compilation unit, issue a pragma for the linker if the linker may be invoked separately from the compiler?

    • Ivan K

      Looking at the coff .obj format, there is a “.drectve” section that can contain linker directives (from pragmas or whatever)… or maybe rather “Lnker” directives, snce ‘s seem to be at a premum.
      This WinRT library may only be headers, but there should still be object files created by project compilation.

  • Dzmitry Konanka

    Hm, it seems it looks, sweems and quacks like GCC’s weak symbols + aliases. See:

    void default_function ()
    {
     .....
    }
    void weak_function () __attribute__ ((weak, alias ("default_function")));
  • word merchant

    So, to summarise: one team in Microsoft has added a universal hack so that at least two other teams who surely could’ve settled all this up over beer and pizza now don’t have to talk to each other. If you scale this scenario up sufficiently, you can imagine the development process that lead to Teams.

    • Tim Weis

      I’m curious, how would you have addressed the use-case of a header-only library, that needs to use symbols that may or may not be declared outside the library?