C++ DLL Export: Decorated/Mangled names
Asked Answered
H

10

41

Created basic C++ DLL and exported names using Module Definition file (MyDLL.def). After compilation I check the exported function names using dumpbin.exe I expect to see:

SomeFunction

but I see this instead:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

Why?

The exported function appears undecorated (especially compared to not using the Module Def file), but what's up with the other stuff?

If I use dumpbin.exe against a DLL from any commercial application, you get the clean:

SomeFunction

and nothing else...

I also tried removing the Module Definition and exporting the names using the "C" style of export, namely:

extern "C" void __declspec(dllexport) SomeFunction();

(Simply using "extern "C" did not create an exported function)

However, this still creates the same output, namely:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

I also tried the #define dllexport __declspec(dllexport) option and created a LIB with no problem. However, I don't want to have to provide a LIB file to people using the DLL in their C# application.

It's a plain vanilla C++ DLL (unmanaged code), compiled with C++ nothing but a simple header and code. Without Module Def I get mangled exported functions (I can create a static library and use the LIB no problem. I'm trying to avoid that). If I use extern "C" __declspec(dllexport) OR a Module Definition I get what appears to be an undecorated function name... the only problem is that it is followed by an "=" and what looks like a decorated version of the function. I want to get rid of the stuff after the "=" - or at least understand why it is there.

As it stands, I'm pretty certain that I can call the function from C# using a P/Invoke... I just want to avoid that junk at the end of the "=".

I'm open to suggestions on how to change the project/compiler settings, but I just used the standard Visual Studio DLL template - nothing special.

Heterogeneity answered 10/5, 2010 at 17:31 Comment(3)
The entire point of extern "C" is to undecorate c++ functions. If it isn't working you need to make sure you're building your DLL correctly.Diapositive
Hans provided the correct answer below.......Heterogeneity
I just want to avoid that junk at the end of the "=". That junk doesn't hurt. It's there, but it doesn't change the binary interface of your module.Shornick
L
16

You can get what you want by turning off debug info generation. Project + Properties, Linker, Debugging, Generate Debug Info = No.

Naturally, you only want to do this for the Release build. Where the option is already set that way.

Ludie answered 10/5, 2010 at 19:30 Comment(3)
THANK YOU! THANK YOU! THANK YOU! It's funny because I was just reading some other answers you wrote on here........ You know, I spent way to much time researching this. I had people repeatedly telling me to use a Module Definition file, others would say "No" it has to be the "extern C"".....I even had people telling me to use 3 different options together. All this despite the fact that my module definition was ok. I just couldn't find someone to explain why there was extra "mangled" text shown by dumpbin. The remaining 10 hairs on my head solute you and live another day.Heterogeneity
Delete the .pdb file by hand so that Dumpbin.exe cannot find it.Ludie
For vs2010 try extern "C" DllExport etc. and extern "C" DllImport etc. before your function declarations after defining the macros for __declspec( dllexport ) and __declspec( dllimport ). This finally worked for me, however, the debug trick, which I accidentally voted up did not work for me. See answer below and comment above by Ron Warholic.Fertilization
P
55

Instead of using .def file just insert pragma comment like this

#pragma comment(linker, "/EXPORT:SomeFunction=_SomeFunction@@@23mangledstuff#@@@@")

Edit: Or even easier: Inside the body of the function use

#pragma comment(linker, "/EXPORT:" __FUNCTION__"=" __FUNCDNAME__)

. . . if you have troubles finding the decorated function name. This last pragma can be further reduced with a simple macro definition.

Peipus answered 10/5, 2010 at 18:54 Comment(6)
This allow to fully define the function name at source level. Perfect! Don't know why so few people uses this!Abuse
Cool trick! Be cautious, however; the mangled stuff usually ensures types!Overhead
That did the trick! As @jjones said, extern "C" don't work for __stdcall. The pragma creates a second entry in the list of functions that the DLL exports. I wish I could give you more points!Robertroberta
With Visual Studio 2015 or newer, be sure to add blanks between the double quote and the underscore character. This prevent the compiler from interpreting the code construct as a user-defined literal: #pragma comment(linker, "/EXPORT:" __FUNCTION__"=" __FUNCDNAME__)Voodoo
I'm having trouble getting this to work the way I want. I made a new question here: #45752356Taction
@Voodoo your brilliant comment helped me to fix my problem on MSVC2017!Tami
L
16

You can get what you want by turning off debug info generation. Project + Properties, Linker, Debugging, Generate Debug Info = No.

Naturally, you only want to do this for the Release build. Where the option is already set that way.

Ludie answered 10/5, 2010 at 19:30 Comment(3)
THANK YOU! THANK YOU! THANK YOU! It's funny because I was just reading some other answers you wrote on here........ You know, I spent way to much time researching this. I had people repeatedly telling me to use a Module Definition file, others would say "No" it has to be the "extern C"".....I even had people telling me to use 3 different options together. All this despite the fact that my module definition was ok. I just couldn't find someone to explain why there was extra "mangled" text shown by dumpbin. The remaining 10 hairs on my head solute you and live another day.Heterogeneity
Delete the .pdb file by hand so that Dumpbin.exe cannot find it.Ludie
For vs2010 try extern "C" DllExport etc. and extern "C" DllImport etc. before your function declarations after defining the macros for __declspec( dllexport ) and __declspec( dllimport ). This finally worked for me, however, the debug trick, which I accidentally voted up did not work for me. See answer below and comment above by Ron Warholic.Fertilization
O
13

You have to declare the functions as extern "C" if you don't want their names to be mangled.

Obit answered 10/5, 2010 at 17:37 Comment(0)
S
13

From experience, be careful if you use __stdcall in your function signature. With __stdcall, the name will remain mangled to some extent (you will find out quickly enough). Apparently, there are two levels of mangling, one the extern "C" deals with at the C++ level, but it does not deal with another level of name mangling caused by __stdcall. The extra mangling is apparently relevant to overloading -- but I am not certain of that.

Subassembly answered 15/1, 2011 at 0:45 Comment(1)
Thanks a heap, thought that it would help having WINAPI (which translates to __stdcall) there, but it didn't.Narco
B
8

Even without the mangling, the 32-bit and 64-bit builds name exports differently, even with extern "C". Check it out with DEPENDS.EXE.

This can mean BIG trouble to any client that does a LoadLibrary+GetProcAdress to access your function.

So, on top of all the others use a Module Definition File as follows:

LIBRARY MYDLL
EXPORTS
myFunction=myFunction

Yeap, it's a bit of a pain to maintain, but then how many exported functions do you write a day?

Moreover, I usually change the macros like shown below, since my DLLs export functions not C++ classes and I want them to be callable by most programming environments:

#ifdef WTS_EXPORTS
#define WTS_API(ReturnType) extern "C" __declspec(dllexport) ReturnType WINAPI
#else
#define WTS_API(ReturnType) extern "C" __declspec(dllimport) ReturnType WINAPI
#endif

WTS_API(int) fnWTS(void);

The last line used to confuse VisualAssistX a couple of years ago, I don't know if it properly digests it now :-)

Blowzy answered 23/10, 2012 at 12:35 Comment(0)
I
8

I know how many times I've tried forcing function names using code and #pragma's. And I always end with exactly same thing, using Module-Definition File (*.def) at the end. And here is the reason:

//---------------------------------------------------------------------------------------------------
// Test cases built using VC2010 - Win32 - Debug / Release << doesn't matter
//---------------------------------------------------------------------------------------------------
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = Yes (/DEBUG)
//  || (or, also doesn't matter)
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = No + delete PDB file!

extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> _SetCallback@4

__declspec(dllexport) void SetCallback(LPCALLBACK function);
> ?SetCallback@@YAXP6AXHPADPAX@Z@Z

__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> ?SetCallback@@YGXP6GXHPADPAX@Z@Z    

//---------------------------------------------------------------------------------------------------
// this also big is nonsense cause as soon you change your calling convention or add / remove
// extern "C" code won't link anymore.

// doesn't work on other cases
#pragma comment(linker, "/EXPORT:SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases
#pragma comment(linker, "/EXPORT:SetCallback=SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=_SetCallback@4")
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YAXP6AXHPADPAX@Z@Z")
__declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YGXP6GXHPADPAX@Z@Z")
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);

//---------------------------------------------------------------------------------------------------
// So far only repetable case is using Module-Definition File (*.def) in all possible cases:
EXPORTS
  SetCallback

extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback

__declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback

// And by far this is most acceptable as it will reproduce exactly same exported function name 
// using most common compilers. Header is dictating calling convention so not much trouble for
// other sw/ppl trying to build Interop or similar.

I wonder why no one did this, it took me only 10 mins to test all cases.

Iconic answered 15/12, 2014 at 22:36 Comment(0)
L
6

Sorry for replying to an old thread, but what has been marked as the answer did not work for me.

As a number of people have pointed out, the extern "C" decoration is important. Changing the "Project / Properties / Linker / Debugging / Generate debug info" setting made absolutely no difference to the mangled names being generated for me in either Debug or Release build mode.

Setup: VS2005 compiling a Visual C++ Class Library project. I was checking the compiled .dll output with Microsoft's Dependency Walker tool.

Here is an example recipe that worked for me...

In project.h:

#define DllExport extern "C" __declspec( dllexport )

DllExport bool API_Init();
DllExport bool API_Shutdown();

In project.cpp:

#include "project.h"

bool API_Init()
{
  return true;
}

bool API_Shutdown()
{
  return true;
}

Then being called from C# managed code, class.cs:

using System.Runtime.Interopservices;
namespace Foo
{
    public class Project
    {
        [DllImport("project.dll")]
        public static extern bool API_Init();

        [DllImport("project.dll")]
        public static extern bool API_Shutdown();
    }
}

Doing the above prevented the mangled names in both Debug and Release mode, regardless of the Generate debug info setting. Good luck.

Logography answered 7/7, 2011 at 5:22 Comment(3)
I actually had a similar problem but even worst, I was having decorated functions exported from a C source file (not C++). It turned out, as you wrote, that if I pre-declare the method within my C file, then I get rid of those decorations. When I did not pre-declare the method in the C source file and try to add __declspec( dllexport ) directly in the function definition/declaration I did get that decoration.Guttapercha
Finally not, my problem came from the use of both __declspec(dllexport) and __stdcall in the C function declaration. It turned out that using __stdcall got the function void myfunction(void) decorated as follows _myfunction@0 while not using __stdcall had it now exported as myfunction without any decoration!Guttapercha
Turning off the name mangling is a bad idea. It is a very good way to detect that the native function changed. Instead of crashing the program with undebuggable stack corruption, you'll get a quick unambiguous error message instead. Use the EntryPoint property to specify the mangled name, find it back with Dumpbin.exe /exports or the linker's .map file.Ludie
N
0

the SomeFunction@@@23mangledstuff#@@@@ is mangled to give the types and class of the C++ function. The simple exports are functions that are callable from C i.e. are written in C or else are declared extern "C' in C++ code. If is you want a simple interface you have to make the functions you export be use just C types and make them non member functions in the global namespace.

Nolasco answered 10/5, 2010 at 17:38 Comment(0)
C
0

Basically, when you use functions in C++, parts of their names now include their signature and suchlike, in order to facilitate language features like overloading.

If you write a DLL using __declspec(dllexport), then it should also produce a lib. Link to that lib, and you will automatically be linked and the functions registered by the CRT at start-up time (if you remembered to change all your imports to exports). You don't need to know about name mangling if you use this system.

Cavalcade answered 10/5, 2010 at 18:24 Comment(0)
K
0

In case it wasn't clear from the hundreds of lines of waffle on the subject of mangled exports. Here's my 2c worth :)

After creating a project called Win32Project2 using VS 2012 and choosing export all symbols in the wizard. You should have 2 files called Win32Project2.cpp and Win32project2.h

Both of those will reference an example exportable variable and an example exported function.

In Win32Project2.h you will have the following:

#ifdef WIN32PROJECT2_EXPORTS
#define WIN32PROJECT2_API __declspec(dllexport)
#else
#define WIN32PROJECT2_API __declspec(dllimport)
#endif

extern WIN32PROJECT2_API int nWin32Project2;
WIN32PROJECT2_API int fnWin32Project2(void);

To unmangle CHANGE the last two lines to extern "C" declarations to:

extern "C" WIN32PROJECT2_API int nWin32Project2;
extern "C" WIN32PROJECT2_API int fnWin32Project2(void);

In Win32Project2.cpp you will also have the following default definitions:

// This is an example of an exported variable
WIN32PROJECT2_API int nWin32Project2=0;

// This is an example of an exported function.
WIN32PROJECT2_API int fnWin32Project2(void)
{
    return 42;
}

To unmangle CHANGE THESE TO:

// This is an example of an exported variable
extern "C" WIN32PROJECT2_API int nWin32Project2=0;

// This is an example of an exported function.
extern "C" WIN32PROJECT2_API int fnWin32Project2(void)
{
    return 42;
}

Essentially you must use the extern "C" prefix in front of declarations in order to force the linker to produce unmangled C like names.

If you prefer to use mangled names for that bit of extra obfuscation (in case the mangling info is useful to someone somehow) use "dumpbin /exports Win32Project2.dll" from a VC command line to lookup the actual reference names. It will have the form "?fnWind32Project2@[param bytes]@[other info] . There are also other DLL viewing tools around if running a VC command shell doesn't float your boat.

Exactly why MS doesn't default to this convention is a mystery. The actual mangling information means something (like parameter size in bytes and more) which might be useful for validation and debugging but is otherwise guff.

To import the DLL function above into C# project (in this case a basic C# windows application with a form on it containing the button "button1") here's some sample code:

using System.Runtime.InteropServices;



    namespace AudioRecApp
    {

      public partial class Form1 : Form
      {
        [ DllImport("c:\\Projects\test\Debug\Win32Projects2.dll")] 
        public static extern int fnWin32Project2();

        public Form1()
        {
          InitializeComponent();
        }


        private void button1_Click(object sender, EventArgs e)
        {
          int value;

          value = fnWin32Project2();
        }
      }
    }
Knotting answered 24/7, 2014 at 5:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.