C/C++ 名字修饰

C 语言的名字修饰

C语言并不支持重载,因此C程序中禁止函数与函数同名,也就没有必要做name mangling。但C语言的函数调用协议(calling conventions)五花八门,用某一种调用协议编译的静态库或动态库的函数,如果用另外的调用协议去调用将会导致错误甚至系统崩溃。因此C语言编译器对函数的名字做少量的修饰,用于区别该函数支持哪种调用协议,这可以给编译、链接、特别是库函数的载入提供额外的检查信息。

C语言常用的调用协议有三种:cdecl, stdcall 与 fastcall。

  • cdecl 的函数被改名为_name;
  • stdcall的函数被改名为_name@X;
  • fastcall的函数被改名为@name@X。

其中X是函数形参所占用的字节长度(包括那些用寄存器传递的参数, 如fastcall协议).

1
2
3
int __cdecl foo(int i); // mangled name is _foo;
int __stdcall foo(int j); // mangled name is _foo@4
int __fastcall foo(int i) ; // mangled name is @foo@4

注意:Windows 的 64位 Microsoft C 的调用约定中没有前导下划线。

C++ 的名字修饰

C++语言并没有规定一个标准的名字修饰方式,所以各款编译器都使用各自的名字修饰方式。(相同编译器的不同版本也可能使用不同的修饰规则)

GCC/Clang 编译器

MSVC 编译器

Visual C++ 名字修饰符主要包含以下部分:

  1. 问号前缀;
  2. 函数名称或不包括类名的方法名称。构造、析构函、运算符重载等具有特定的函数名;
  3. 如果不是特殊函数名,那么加一个分隔符@;
  4. 如果是类的方法,那么由所属类开始依次加上类名和父类名,每个类名后面跟一个@符号,所有类名加好后,再加上@Q或者@S(静态方法)。如果不是类的方法,那么直接加上@Y;
  5. 调用约定代码。对于不属于任何类的函数,C调用约定(__cdecl)的代码为A,__fastcall约定的代码为I,__stdcall 的代码为G,对于类方法,调用约定前会加一个字符A,this调用的代码为E.
  6. 返回值编码。
  7. 参数列表编码,以@符号结束。
  8. 后缀Z。

C++名称修饰组成规律:

  1. 都是以?开始,以字符Z结束,中间由@符号分割为多个部分。整个名称的长度最长为2048个字节。
  2. 类的成员函数,其基本结构为:?方法名@类名@@调用约定 返回类型 参数列表 Z。
  3. 非类的成员函数,其基本结构: ?函数名@@Y调用约定 返回类型 参数列表Z。

特殊函数名:

特殊方法名 编码
构造函数 ?0
析构函数 ?1
重载 new ?2
重载 delete ?3

数据类型:

编码 数据类型
A Type modifier (reference)
B Type modifier (volatile reference)
D char
E unsigned char
F short
G unsigned short
H int
I unsigned int
J long
K unsigned long
M float
N double bool
O long double Array
P Type modifier (pointer)
Q Type modifier (const pointer)
R Type modifier (volatile pointer)
S Type modifier (const volatile pointer)
T union
U struct
V class
W enum
X void

预编译宏

GCC/Clang 以及微软都提供了预定义宏,用于获取源码中的方法名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// GCC 支持: __PRETTY_FUNCTION__ 和 __FUNCTION__
// MSVC 支持: __FUNCTION__ 和 __FUNCDNAME__ 和 __FUNCSIG__

void exampleFunction()
{
printf("Function name: %s\n", __FUNCTION__);
printf("Decorated function name: %s\n", __FUNCDNAME__);
printf("Function signature: %s\n", __FUNCSIG__);

// 输出为:
// -------------------------------------------------
// Function name: exampleFunction
// Decorated function name: ?exampleFunction@@YAXXZ
// Function signature: void __cdecl exampleFunction(void)

}

修饰解析

  1. GCC/Clang 编译的符号通过 c++filt 去修饰。
  2. MSVC 编译的符号使用微软 Visual C++ 中的解析修饰名字的工具软件 undname.exe

查看Visual C++的函数的修饰后的名字:

  1. 直接用工具软件(如微软开发环境提供的dumpbin)查看obj、exe等二进制文件。使用dumplib查看.obj或.lib文件时,使用”/SYMBOLS”命令行选项。
  2. 编译时使用”/FA[c|s|u]”编译选项,生成带有丰富注释信息的汇编源程序,其文件扩展名是.cod或者.asm,可以查看每个C/C++函数的修饰名。

参考

https://zh.wikipedia.org/wiki/Visual_C%2B%2B%E5%90%8D%E5%AD%97%E4%BF%AE%E9%A5%B0