https://www.cnblogs.com/starrysky77/p/8531413.html
对于 C 编译器的修饰规则
对于 __stdcall
调用约定,编译器和链接器会在输出函数名前加上一个下划线前缀,函数名后面加上一个“@”符号和其参数的字节数,例如 _functionname@number
。__cdecl
调用约定仅在输出函数名前加上一个下划线前缀,例如 _functionname
。
__fastcall
调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,例如 @functionname@number
对于 C++ 编译器的修饰规则
C++ 的函数名修饰规则有些复杂,但是信息更充分,通过分析修饰名不仅能够知道函数的调用方式,返回值类型,参数个数甚至参数类型。不管 __cdecl
,__fastcall
还是 __stdcall
调用方式,函数修饰都是以一个“?
”开始,后面紧跟函数的名字,再后面是参数表的开始标识和按照参数类型代号拼出的参数表。
对于 __stdcall
方式,参数表的开始标识是“@@YG
”,对于 __cdecl
方式则是“@@YA
”,对于 __fastcall
方式则是“@@YI
”。参数表的拼写代号如下所示:
X--void
D--char
E--unsigned char
F--short
H--int
I--unsigned int
J--long
K--unsigned long(DWORD)
M--float
N--double
_N--bool
U--struct
指针的方式有些特别,用 PA
表示指针,用 PB
表示 const
类型的指针。后面的代号表明指针类型,如果相同类型的指针连续出现,以“0
”代替,一个“0
”代表一次重复。
U
表示结构类型,通常后跟结构体的类型名,用“@@
”表示结构类型名的结束。
函数的返回值不作特殊处理,它的描述方式和函数参数一样,紧跟着参数表的开始标志,也就是说,函数参数表的第一项实际上是表示函数的返回值类型。参数表后以“@Z
”标识整个名字的结束,如果该函数无参数,则以“Z
”标识结束。下面举两个例子,假如有以下函数声明:
int Function1 (char *var1,unsigned long);
→ ?Function1@@YGHPADK@Z
void Function2();
→?Function2@@YGXXZ
成员函数
对于 C++ 的类成员函数(其调用方式是 thiscall
),函数的名字修饰与非成员的 C++ 函数稍有不同,首先就是在函数名字和参数表之间插入以“@
”字符引导的类名;其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE
”,保护(protected)成员函数的标识是“@@IAE
”,私有(private)成员函数的标识是“@@AAE
”,如果函数声明使用了 const 关键字,则相应的标识应分别为“@@QBE
”,“@@IBE
”和“@@ABE
”。如果参数类型是类实例的引用,则使用“AAV1
”,对于 const 类型的引用,则使用“ABV1
”。
?Function@CTest@@AAEXH@Z
?CopyInfo@CTest@@IAEXABV1@@Z
?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z
?InsightClass@CTest@@QBEJK@Z
Tools
有两种方式可以检查你的程序中的函数的名字修饰:使用编译输出列表或使用 Dumpbin 工具。使用 /FAc
,/FAs
或 /FAcs
命令行参数可以让编译器输出函数或变量名字列表。使用 dumpbin.exe
/SYMBOLS
命令也可以获得 obj 文件或 lib 文件中的函数或变量名字列表。此外,还可以使用 undname.exe
将修饰名转换为未修饰形式。
undname.exe
在装 VS 的时候会装上,用 everything 找到就可以了(顺便放到 path 里)
对于 GCC 编译来说,可以使用 c++filt
解析被修饰过的名称