DLL function is exported uisng #pragma comment(linker, "/export:Func4\")

Forrest 25 Reputation points
2024-09-07T13:46:57.64+00:00

In DLL, I use this method to export the function.

#pragma comment(linker, "/export:Func4=?Func4@@YAXXZ")
void Func4();

Then, I got example.dll, example.lib file. In DLL file, the function name is Func4.

When I use this DLL via implicit linking:

#pragma comment(lib, "example.lib")

But I cannot use function via name Func4.

void Func4();

If I use these method to export function:

// __declspec(dllexport)
__declspec(dllexport) void Func1();

// .def
void Func2();

I can use function vis name Func1, Func2. I want to know, In the situation, How should I use the Func4?

At present, I have two solution:

  1. Using Explicit Linking.
typedef void (*Func)();
int main(int, char **) {
  HMODULE hMod = LoadLibrary(TEXT("Dynamic.dll"));
  if (hMod == NULL) {
    std::cerr << "Cannot find library.\n";
    FreeLibrary(hMod);
    return 1;
  }
  Func func = (Func)GetProcAddress(hMod, "Func4");
  if (func == nullptr) {
    std::cerr << "Cannot find function.\n";
    FreeLibrary(hMod);
    return 1;
  }
  func();
}
  1. Using extern "C" In call function file, But need to modify the function export method.

Call:

extern "C" void Func4();

Export:

#pragma comment(linker, "/export:_Func4=?Func4@@YAXXZ")
void Func4();

The function name "Func4" to "__Func4". Because If use extern "C", linker will use symbol _Func or _main .

C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,693 questions
0 comments No comments
{count} votes

Accepted answer
  1. Darran Rowe 916 Reputation points
    2024-09-07T14:26:53.18+00:00

    --EDITED--

    Since the question was clarified, I will edit this anwer to provide a better answer.

    First, it is going to be almost impossible to provide just a plain name to a C++ function. The tooling just doesn't work this way. The linker would just totally ignore non C++ names when trying to make import libraries.

    The best option is to export the functions as extern "C". Nothing is gained by exporting a C++ function as a plain name, but the ability for the compiler to detect this is lost. Using extern"C" will get the library close while allowing the compiler to detect attempts at overloading a function. The ability to overload a function is lost when the name mangling is removed, so at that point it is better to just use extern"C". There is also the fact that C++ name mangling is not standardised. This means that the name mangling scheme can and has changed.

    When extern "C" is used, surprisingly, the goal has been achieved already. When Visual C++ exports a C function from a DLL, a __cdecl function will be exported from the DLL using its plain name.

    Dump of file testlib.dll
    
    File Type: DLL
    
      Section contains the following exports for testlib.dll
    
        00000000 characteristics
        FFFFFFFF time date stamp
            0.00 version
               1 ordinal base
               2 number of functions
               2 number of names
    
        ordinal hint RVA      name
    
              1    0 00001010 ?fun2@@YAXXZ
              2    1 00001000 fun1
    
      Summary
    
            2000 .data
            1000 .fptable
            7000 .rdata
            1000 .reloc
            D000 .text
    

    Notice how fun1 does not have the leading _. Whats more, the import library is automatically set up to allow the linker find the entry.

    Archive member name at 6E6: testlib.dll/
    FFFFFFFFFFFFFFFF time/date
             uid
             gid
           0 mode
          26 size
    correct header end
    
      Version      : 0
      Machine      : 14C (x86)
      TimeDateStamp: D77D3838
      SizeOfData   : 00000012
      DLL name     : testlib.dll
      Symbol name  : _fun1
      Type         : code
      Name type    : no prefix
      Hint         : 1
      Name         : fun1
    

    Notice how Symbol name is _fun1, but the Name is fun1. The symbol name is the name that the compiler generates and looks for, but Name is the name exported from the DLL. So just by using extern "C", the goal has been achieved. The original function definition and export was:

    extern"C"
    void fun1()
    {
    }
    
    #ifdef _M_IX86
    #pragma comment(linker, "/export:_fun1")
    #else
    #pragma comment(linker, "/export:fun1")
    #endif
    

    Notice how, for x86 builds, I am explicitly exporting the mangled name of the function.

    If for some reason a plain name for a C++ exported name is needed, there are a couple of methods available, but it is required to export the mangled C++ name if link time dynamic linking is required (this is where the import library (.lib) file is used). It is impossible to associate a C++ mangled name with a plain symbol exported from a DLL. If a module definition like:

    EXPORTS
        ?fun1@@YAAXXZ=fun1
    

    is used, lib.exe seems to assume that the plain name to the right is actually mangled, or it ignores the name. This module definition is required to construct an import library that makes the association between the C++ mangled name and the DLL export so the linker is able to use it. There is no issue exporting the same function twice as long as each export is uniquely named.

    #ifdef _M_IX86
    #pragma comment(linker, "/export:_fun1")
    #pragma comment(linker, "/export:?fun2@@YAXXZ")
    #pragma comment(linker, "/export:fun2=?fun2@@YAXXZ,PRIVATE")
    #else
    //For anything currently supported but x64, C exports do not
    //use mangling for __cdecl, __vectorcall still mangles though
    #pragma comment(linker, "/export:fun1")
    //Even though in this case the x86 C++ name is the same, do not
    //assume that it will always be the same.
    #pragma comment(linker, "/export:?fun2@@YAXXZ")
    #pragma comment(linker, "/export:fun2=?fun2@@YAXXZ,PRIVATE")
    #endif
    

    The private on the plain name definition of fun2 only tells the linker not to put a reference to it in the import library, so fun2 will still be visible as an export for the DLL.

    If there is a desire to keep the DLL exports simple, then the only option is to use a forwarder DLL. The plain names can be placed into a separate DLL:

    LIBRARY testlibforward.dll
    
    EXPORTS
    	fun1=testlib.fun1
    	fun2=testlib.?fun2@@YAXXZ
    

    This helps split things between libraries, but in the end the C++ name still has to be exported. If you wish to access the plain name then the forwarder library can be used. In this case, Windows will automatically load testlib.dll and use ?fun2@@YAXXZ when fun2 is used in testlibforwarder.dll.

    --Original Post--

    The question is unclear, so I will make some assumptions here.

    For names such as ?Func4@@YAXXZ, these are C++ mangled names. If the intent is to export a C++ function then function overloading has to be taken into account. For function overloading to work, the C++ mangled names must be exported.

    If there is no intent of making a function overloadable, then putting the function definition into an extern "C" block helps. C++ name mangling isn't standardised and can change, so using the C name mangling is better because it is relatively fixed. Also, as a note, _Func or _main is only used for __cdecl for x86 C name mangling, x64 will use Func or main.

    If the intent is to export the C name as a plain funcion name, the documentation for the linker /EXPORT option states:

    #pragma comment(linker, "/export:PlainFuncName=_PlainFuncName@4")
    BOOL CALLBACK PlainFuncName( Things * lpParams)
    

    as an example. The thing to note is that the plain name comes first, this is the name that is exported from the library, where the decorated name comes second.So, for x86 builds of the DLL only,

    extern "C"
    void Func4();
    
    #pragma comment(linker, "/export:Func4=_Func4")
    

    Appears to be what you wish to do.

    However, parsing the question a bit more, if the intent is to export the function from the DLL using the plain name, but have it available in the import library in a form that is usable by the linker, then the solution must be external to the project.

    To use kernel32.dll as an example,

    File Type: DLL
    
      Section contains the following exports for KERNEL32.dll
    
        00000000 characteristics
        F503DEE8 time date stamp
            0.00 version
               1 ordinal base
            1643 number of functions
            1643 number of names
    
        ordinal hint RVA      name
    
              4    0          AcquireSRWLockExclusive (forwarded to NTDLL.RtlAcquireSRWLockExclusive)
              5    1          AcquireSRWLockShared (forwarded to NTDLL.RtlAcquireSRWLockShared)
              6    2 0002F240 ActivateActCtx
              7    3 0001CE50 ActivateActCtxWorker
              8    4 0001F8D0 ActivatePackageVirtualizationContext
              9    5 000546B0 AddAtomA
             10    6 00016090 AddAtomW
    

    The first fer entries of the x86 bit DLL (more explicitly the WoW64 x86 DLL) show all of the functions exported from the DLL using the plain names. Some are forwarded, but that is a discussion for another time. However, if we look at the kernel32.lib entry for AddAtomA,

    Archive member name at 291E6: KERNEL32.dll/   
    FFFFFFFFFFFFFFFF time/date
             uid
             gid
           0 mode
          2D size
    correct header end
    
      Version      : 0
      Machine      : 14C (x86)
      TimeDateStamp: C5B79BF5
      SizeOfData   : 00000019
      DLL name     : KERNEL32.dll
      Symbol name  : _AddAtomA@4
      Type         : code
      Name type    : undecorate
      Hint         : 5
      Name         : AddAtomA
    

    Notice how the Symbol name entry is _AddAtomA@4? This is the __stdcall mangled name for the function, and this is what the compiler and linker is looking for.

    However, if we look at the entry for my previous example library in TestLib.lib:

    Archive member name at 628: TestLib.dll/
    FFFFFFFFFFFFFFFF time/date
             uid
             gid
           0 mode
          25 size
    correct header end
    
      Version      : 0
      Machine      : 14C (x86)
      TimeDateStamp: F9115131
      SizeOfData   : 00000011
      DLL name     : TestLib.dll
      Symbol name  : fun1
      Type         : code
      Name type    : name
      Hint         : 0
      Name         : fun1
    

    Notice how the Symbol name entry is fun1? Which is the unmangled name. For this to be usable by the linker, the import library must be created in a way to make that link again. This requires creating a separate module definition file.

    NAME TestLib.dll
    
    EXPORTS
    	fun1=fun1
    

    The weird fun1=fun1 thing is needed because the linker attempts to do some name mangling. But if this module definition file is made into an import library, then it will produce:

    Archive member name at 62C: TestLib.dll/
    66DC6E87 time/date Sat Sep  7 16:17:27 2024
             uid
             gid
           0 mode
          26 size
    correct header end
    
      Version      : 0
      Machine      : 14C (x86)
      TimeDateStamp: 66DC6E87 Sat Sep  7 16:17:27 2024
      SizeOfData   : 00000012
      DLL name     : TestLib.dll
      Symbol name  : _fun1
      Type         : code
      Name type    : no prefix
      Hint         : 0
      Name         : fun1
    

    Notice that the Symbol name is _fun1, and the Name is fun1. So if the intent is to export a function using a non default name, then the link must be made visible to the linker in the import library.


1 additional answer

Sort by: Most helpful
  1. Forrest 25 Reputation points
    2024-09-07T15:57:29.2733333+00:00

    Thank you for your answering.Acutely, My question is, If I use this method to export function,

    #pragma comment(linker, "/export:_Func4=?Func4@@YAXXZ")
    void Func4();
    

    I got dll:

     Section contains the following exports for Dynamic.dll
        00000000 characteristics
        FFFFFFFF time date stamp
            0.00 version
               1 ordinal base
               5 number of functions
               5 number of names
        ordinal hint RVA      name
              1    0 00001FB4 ?Func1@@YAXXZ = @ILT+4015(?Func1@@YAXXZ)
              2    1 00003F67 Func2 = @ILT+12130(?Func2@@YAXXZ)
              4    2 00003797 Func3 = @ILT+10130(_Func3)
              5    3 000039A9 Func4 = @ILT+10660(?Func4@@YAXXZ)
              3    4 000015FF Func5 = @ILT+1530(?Func5@@YAXXZ)
    

    And then, I want to use Func4:

    // example.h
    void Func4();
    
    // example.cpp
    #include "example.h"
    #pragma comment(lib, "../libs/dynamic/Dynamic.lib")
    int main(int, char **) {
      Func4();
    }
    

    It's not feasible.I got this:

    The error message "main.obj : error LNK2019: unresolved external symbol "void __cdecl Func4(void)" (?Func4@@YAXXZ) referenced in function _main [E:\Forrest\C++\HelloWorld\build\hello.vcxproj]" indicates that the linker is unable to find the definition (implementation) of the function Func4 that is being referenced from the main function.

    ow should I use the Func4 via implicit linking?

    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.