Name mangling && Name demangling

在讲述golang如何利用swig调用windows dll之前,需要了解一个概念:Name Mangling (或者叫Decorated Name).在百度翻译中输入Name Mangling,翻译成中文是“名字改编”,或者“名称重整”。Decorated Name,是微软的叫法,被百度翻译为“修饰名”。不管是英文名,还是翻译后的结果看,Name Mangling的叫法是比较合适的,并且是C++领域内的普遍叫法。通常情况下,C++程序员不必了解Name Mangling,但是在工程内生成或者调用动态链接库,可能需要对其有所了解。先看看下面的例子。

打开VS2010,File--New-Project,选择Visual C++--Win32,右侧选中Win32 Console Application,创建名称Simple的工程

点击OK,然后next,

选中DLL,勾选Export symbols,点击Finish完成创建。

下面是自动生成的Simple.h文件

// The following ifdef block is the standard way of creating macros which make exporting

// from a DLL simpler. All files within this DLL are compiled with the SIMPLE_EXPORTS

// symbol defined on the command line. This symbol should not be defined on any project

// that uses this DLL. This way any other project whose source files include this file see

// SIMPLE_API functions as being imported from a DLL, whereas this DLL sees symbols

// defined with this macro as being exported.

#ifdef SIMPLE_EXPORTS

#define SIMPLE_API __declspec(dllexport)

#else

#define SIMPLE_API __declspec(dllimport)

#endif

// This class is exported from the Simple.dll

class SIMPLE_API CSimple {

public:

CSimple(void);

// TODO: add your methods here.

};

extern SIMPLE_API int nSimple;

SIMPLE_API int fnSimple(void);

自动生成的代码导出了一个类,一个函数,一个变量。给CSimple类添加一个简单的方法,SayHello

#ifdef SIMPLE_EXPORTS

#define SIMPLE_API __declspec(dllexport)

#else

#define SIMPLE_API __declspec(dllimport)

#endif

// This class is exported from the Simple.dll

class SIMPLE_API CSimple {

public:

CSimple(void);

// TODO: add your methods here.

int SayHello();

};

extern SIMPLE_API int nSimple;

SIMPLE_API int fnSimple(void);

Cpp文件:

// This is an example of an exported variable

SIMPLE_API int nSimple=0;

// This is an example of an exported function.

SIMPLE_API int fnSimple(void)

{

return 42;

}

// This is the constructor of a class that has been exported.

// see Simple.h for the class definition

CSimple::CSimple()

{

return;

}

int CSimple::SayHello()

{

return 0;

}

在Configuration Manager中添加x64

编译后,在D:\Sample\Simple\x64\Debug生成的主要文件有Simple.dll,Simple.lib,Simple.pdb。

通常情况下,C++调用Dll只需要在工程链接属性中设置附加库路径和附加依赖引入Simple.lib就可以了,动态链接库的生成和调用就到此为止了。

     调用LoadLibrary 和GetProcAddress接口时,或者解决运行时缺少动态链接库问题时,往往会用到windows开发者熟知的著名工具Dependency Walker,这个工具可以查看依赖和导入导出工具,用x64版打开上面生成的64位dll,

在导出栏里我们看到Simple.dll导出了5个函数(变量),函数名都是以?开头,从代码里看,只导出了4项:CSimple类的两个成员函数CSimple::CSimple、CSimple::SayHello,fnSimple和nSimple变量,dependency Walker中的导出多了1项,从函数名上可以大概推断下,第五项是nSimple变量,第四项是fnSimple函数,第三项是CSimple::SayHello函数,其他两个不同容易看,其中一个应该是CSimple::CSimple。

在VS的安装目录下(C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64)有一些小工具,其中一个叫undname.exe,这个工具的全名应该叫Undecorated Name,与Decorated是反义,这个工具可以将导出函数名解析成程序员易读的名字。依次对1-5调用undname

 

Exported function name

undname

??0CSimple@@QEAA@XZ

"public: __cdecl CSimple::CSimple(void) __ptr64"

??4CSimple@@QEAAAEAV0@AEBV0@@Z

"public: class CSimple & __ptr64 __cdecl CSimple::operator=(class CSimple

const & __ptr64) __ptr64"

?SayHello@CSimple@@QEAAHXZ

"public: int __cdecl CSimple::SayHello(void) __ptr64"

?fnSimple@@YAHXZ

"int __cdecl fnSimple(void)"

?nSimple@@3HA

"int nSimple"

CSimple::CSimple()成员函数名经过编译器处理后变成了??0CSimple@@QEAA@XZ,CSimple::SayHello成员函数经过编译器处理后变成了?SayHello@CSimple@@QEAAHXZ,通俗地讲这个处理过程就是Name Mangling.

引用一外国哥们博文中的三段英文,虽然非官方解释,但是个人认为他把Name Mangling很清楚地讲明白了。如果希望有更严谨的解释,自行查看维基百科。

here is a quick reminder on what is name mangling and why it is used in C++. When an executable is started, some (many) of its symbols addresses are resolved during the startup - at runtime - because these addresses are not known during the static link (i.e. when the executable is generated). One way to resolve them is to search them by symbol name. This is typically what is done when "dlsym" is used, but it is also how the runtime linker resolves the address of the functions implemented in the dynamic libraries.

When developing in C, all public symbols of an executable are unique. It is not possible to have two different variables or functions with the same name. The only exceptions to this are local variables and static variables/functions. However such symbols are private to a function or a file and cannot be retrieved by name at runtime. So in C the symbols signature is the name that appears in the code. In this case the linker can find a function by using its name as the signature.

Things are not that simple in C++. The symbols names cannot just be the name of the functions or methods because the language allows polymorphism: A method can have several prototypes to operate on different kinds of data. for example it is possible to declare "void Foo(int)" and "void Foo(double)". In this case it is not possible to use the function name as a signature without a conflict. This is why name mangling is used: Some additional information is added to the symbol name to make it unique in the executable/library. One can see C++ mangling as a concatenation of the method name with its parameters types. This is a simplification because other things are used to generate a mangled name, but it is the general idea. Once names are mangled, it is possible to resolve symbols at runtime by name without conflict.

简单的翻译就是:C++允许函数重载,需要name mangling来保证名称唯一。C++标准中没有明确name mangling的规则,每家编译器的实现不太一样,甚至同一编译器的不同版本,同一编译器在不同平台上的实现,Name Mangling也可能不一样。(参考wiki Name Mangling)。

比较常见的两个C++ 编译器分别是GNU gcc(g++)编译器和微软Visual C++编译器,这两个编译器Name Mangling的结果完全不一样。GNU编译器是开源的,所以其Name Mangling的规则及代码都可以在网上搜索到。Github.com上的gcc-mirror/gcc,gcc/gcc/cp/mangle.c是gcc编译器Name Mangling部分的源码。

Visual C++是商业闭源软件,他的Name Mangling规则是非公开的,微软的术语叫decorated Name,官方资料少得可怜,可以在MSDN上查看相关内容。

对Name Mangling有了个初步的了解后,我们继续看例子。将上面VS2010自动生成的代码用g++编译成dll文件。

编译前需要在windows os中安装TDM-GCC,这是gcc编译器的一个windows版实现,官方下载地址(http://tdm-gcc.tdragon.net/download)。

在D:\Sample\文件夹下创建子文件夹GCC,将Simple.h和Simple.cpp文件拷贝到D:\Sample\gcc下,将Simple.cpp文件中的#include "stdafx.h"注释掉。打开命令行,切换到D:\Sample\gcc,执行g++ -shared -Wall -o Simple.dll Simple.cpp  -DSIMPLE_EXPORTS,生成了Simple.dll,再次用Dependency Walker打开刚刚生成的Simple.dll

乍一看,gcc编译的dll也导出了5个函数(变量),实则不然,第三和第四的Entry Point相同,同一函数有两个名称而已。GNU C++编译器也有个工具,名字叫c++filt,他的功能和微软的undname.exe是一样的,对mangled name进行demangling。依次对1-5调用c++filt

Mangled names

c++filt

_Z8fnSimplev

fnSimple()

_ZN7CSimple8SayHelloEv

CSimple::SayHello()

_ZN7CSimpleC1Ev

CSimple::CSimple()

_ZN7CSimpleC2Ev

CSimple::CSimple()

nSimple

nSimple

通过上面的两个例子可以看出,Visual C++编译器与GNU编译器Name Mangling的结果是完全不一样的,并且demangle工具给出的信息量也是不一样的,c++filt给出的结果只有名称,可区分函数和变量。

在demangling过程中,微软和GNU编译器都提供了demangling工具;但是如果想要借助一个工具从一个Name得到Mangled Name,好像没有单独的工具,根据我百度加谷歌的结果得出的答案。不过可以借助编译器预处理来间接得到结果,具体在以后的随笔中给出。

从上面的第一个例子可以看到,vs2010 C++编译器为CSimple类自动生成了拷贝赋值操作符=,看过Effective C++第三版的会知道,条款5《了解C++默认编写并调用哪些函数》讲到:“什么时候empty class(空类)不再是一个empty class?当C++处理过它之后。是的,如果你自己没声明,编译器就会为它声明(编译器版本的)一个copy构造函数,一个copy assignment操作符和一个析构函数。此外如果你没有声明任何构造函数,编译器也会为你声明一个default构造函数。所有这些都是public切inline。”后面又讲到,“唯有当这些函数被需要(被调用),他们才会被编译器创建出来。”

在生成动态链接库时,微软的编译器似乎并没有遵守条款五,因为生成dll文件时,编译器无法知道dll的调用者是否会调用拷贝赋值操作符;GUN编译器遵守了这个条款,在导出的类成员函数中只有构造函数CSimple::CSimple()和CSimple::SayHello()。 或者说,条款五描述的是GNU C++编译器的行为。

calling c++ from golang with swig--windows dll 二的更多相关文章

  1. calling c++ from golang with swig--windows dll(一)

    calling c++ from golang with swig--windows dll 之前项目组开发的项目核心代码全部使用C++语言,新项目可能会引入golang,花了一天多时间研究了wind ...

  2. calling c++ from golang with swig--windows dll (三)

    calling c++ from golang with swig--windows dll 三 使用动态链接库(DLL)主要有两种方式:一种通过链接导入库,在代码中直接调用DLL中的函数:另一种借助 ...

  3. calling c++ from golang with swig--windows dll (四)

    calling c++ from golang with swig--windows dll 四 前面讲述了windows环境下golang如何通过swig调用C++ dll.由于编译c++代码使用了 ...

  4. golang调用c++的dll库文件

    最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ...

  5. Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术

    catalogue 1. 引言2. 使用注册表注入DLL3. 使用Windows挂钩来注入DLL4. 使用远程线程来注入DLL5. 使用木马DLL来注入DLL6. 把DLL作为调试器来注入7. 使用c ...

  6. go 调用windows dll 的方法

    go 调用windows dll 的方法 ,代码如下: package main import ( "fmt" "syscall" "time&quo ...

  7. C#实现动态调用Windows DLL

    调用方法: object obj = WinDllInvoke("Kernel32.dll", "Beep", , }, typeof(void)); 函数代码 ...

  8. Golang丰富的I/O 二----cgo版Hello World

    h1 { margin-top: 0.6cm; margin-bottom: 0.58cm; direction: ltr; color: #000000; line-height: 200%; te ...

  9. Windows Forms(二)

    导读 1.用VS创建一个Windows Forms程序 2.分析上面的程序 3.Mediator pattern(中介者模式) 4.卡UI怎么办——BackgroundWorker组件 用VS创建一个 ...

随机推荐

  1. app与后台交互之间的几种安全认证机制

    1.HTTP简单基本认证方式 这个是早期交互用得比较多的一种方式,主要是使用用户名和密码来交互,由于在每次的交互中,用户名和密码都会暴露给第三方,那么这么做是不可取的,风险十分大,所以这种认证方式并没 ...

  2. iOS真机测试友盟碰到错误linker command failed with exit code 1 (use -v to see invocation) 百度地图的检索失败 sqlite 错误码

    因为友盟不支持bitcode 在模拟器上运行正常,但是在模拟器上就会报错,这是因为xocde7之后增加了一个bitcode,bitcode是被编译程序的一种中间形式的代 码.包含bitcode配置的程 ...

  3. 5. UITest测试总结

    1. 什么是Mock 当我们在做单元测试的过程中,为了保持测试又短又快和测试的隔离性,希望尽可能少地去实例化一些具体的组件.在现在面向对象的系统中,被测试的对象很可能会依赖于几个其他的对象,这时候我们 ...

  4. webpack入门与解析(一)

    每次学新东西总感觉自己是不是变笨了,看了几个博客,试着试着就跑不下去,无奈只有去看官方文档. webpack是基于node的.先安装最新的node. 1.初始化 安装node后,新建一个目录,比如ht ...

  5. 运行gpg --gen-key生成key时出现卡住的问题

    背景 在搭建Ubuntu16.04的本地apt源时,需要运行"gpg --gen-key"命令,但是在执行该命令时,出现了:"Not enough random byte ...

  6. H5常见的兼容问题及解决

    最近这两天经常遇到一些麻烦的兼容问题,统一整理一下,比较简单也不是特别全面,希望大家多多交流. 几种IE6 bug的解决方法 1)png24位的图片在iE6浏览器上出现背景,解决方案是做成PNG8.也 ...

  7. git 命令用法 流程操作

    Git 是一款免费的.开源的.分布式的版本控制系统.旨在快速高效地处理无论规模大小的任何软件工程. 每一个 Git克隆 都是一个完整的文件库,含有全部历史记录和修订追踪能力,不依赖于网络连接或中心服务 ...

  8. 剑指offer编程题Java实现——二维数组中的查找

    题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数.   下面是我实现的代码 ...

  9. EFcore与动态模型

    在开发商城系统的时候,大家会遇到这样的需求,商城系统里支持多种商品类型,比如衣服,手机,首饰等,每一种产品类型都有自己独有的参数信息,比如衣服有颜色,首饰有材质等,大家可以上淘宝看一下就明白了.现在的 ...

  10. SimpleDateFormat的线程安全问题与解决方案

    SimpleDateFormat 是 Java 中一个常用的类,该类用来对日期字符串进行解析和格式化输出,但如果使用不小心会导致非常微妙和难以调试的问题. 因为 DateFormat 和 Simple ...