Name Mangling in C++

 

Name Mangling(C++)
Author: Chaos Lee
Date: 2012/05/06

摘要:详细介绍了C++中的Name Mangling的原理和gcc中对应的实现,经过程序代码和nm c++filt等工具来验证这些原理。对于详细了解程序的连接过程有必定的帮助。ios

Name Mangling概述c++

大型程序是经过多个模块构建而成,模块之间的关系由makefile来描述。对于由C++语言编制的大型程序而言,也是符合这个规则。
程序的构建过程通常为:各个源文件分别编译,造成目标文件。多个目标文件经过连接器造成最终的可执行程序。显然,从某种程度上说,编译器的输出是连接器的输入,连接器要对编译器的输出作二次加工。从通讯的角度看,这两个程序须要必定的协议来规范符号的组织格式。这就是Name Mangling产生的根本缘由。

C++的语言特性比C丰富的多,C++支持的函数重载功能是须要Name Mangling技术的最直接的例子。对于重载的函数,不能仅依靠函数名称来区分不一样的函数,由于C++中重载函数的区分是创建在如下规则上的:git

函数名字不一样 || 参数数量不一样||某个参数的类型不一样
那么区分函数的时候,应该充分考虑参数数量和参数类型这两种语义信息,这样才能为却分不一样的函数保证充分性。

固然,C++还有不少其余的地方须要Name Mangling,如namespace, class, template等等。sql

总的来讲,Name Mangling就是一种规范编译器和连接器之间用于通讯的符号表表示方法的协议,其目的在于按照程序的语言规范,使符号具有足够多的语义信息以保证连接过程准确无误的进行。
简单的实验
Name Mangling会带了一个很常见的负面效应,就是C语言的程序调用C++的程序时,会比较棘手。由于C语言中的Name Mangling很简单,不如C++中这么复杂。下面的代码用于演示这两种不一样点:
 
  
  
           
  
  
  1. /* 
  2. * simple_test.c 
  3. * a demo to show that different name mangling technology in C++ and C 
  4.  
  5. * Author: Chaos Lee 
  6.  
  7. */ 
  8.   
  9. #include<stdio.h> 
  10.   
  11. int rect_area(int x1,int x2,int y1,int y2) 
  12.  
  13.         return (x2-x1) * (y2-y1); 
  14.   
  15. int elipse_area(int a,int b) 
  16.  
  17.         return 3.14 * a * b; 
  18.   
  19. int main(int argc,char *argv[]) 
  20.  
  21.         int x1 = 10, x2 = 20, y1 = 30, y2 = 40; 
  22.         int a = 3,b=4; 
  23.         int result1 = rect_area(x1,x2,y1,y2); 
  24.         int result2 = elipse_area(a,b); 
  25.         return 0; 

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ gcc -c simple_test.c 
  2.  
  3. [lichao@sg01 name_mangling]$ nm simple_test.o 
  4.  
  5. 0000000000000027 T elipse_area 
  6.  
  7. 0000000000000051 T main 
  8.  
  9. 0000000000000000 T rect_area 
从上面的输出结果上,能够看到使用gcc编译后对应的符号表中,几乎没有对函数作任何修饰。接下来使用g++编译:
 
  
  
           
  
  
  1.  [lichao@sg01 name_mangling]$ nm simple_test.o 
  2. 0000000000000028 T _Z11elipse_areaii 
  3.  
  4. 0000000000000000 T _Z9rect_areaiiii 
  5.  
  6.                  U __gxx_personality_v0 
  7. 0000000000000052 T main 
显然,g++编译器对符号的改编比较复杂。因此,若是一个由C语言编译的目标文件中调用了C++中实现的函数,确定会出错的,由于符号不匹配。
简单对_Z9rect_areaiiii作个介绍:

l C++语言中规定 :如下划线并紧挨着大写字母开头或者以两个下划线开头的标识符都是C++语言中保留的标示符。因此_Z9rect_areaiiii是保留的标识符,g++编译的目标文件中的符号使用_Z开头(C99标准)。express

l 接下来的部分和网络协议很相似。9表示接下来的要表示的一个字符串对象的长度(如今知道为何不让用数字做为标识符的开头了吧?)因此rect_area这九个字符就做为函数的名称被识别出来了。
l 接下来的每一个小写字母表示参数的类型,i表示int类型。小写字母的数量表示函数的参数列表中参数的数量。
l 因此,在符号中集成了用于区分不一样重载函数的足够的语义信息。
若是要在C语言中调用C++中的函数该怎么作?这时候可使用C++的关键字extern “C”。对应代码以下:
 
  
  
           
  
  
  1. /* 
  2. * simple_test.c 
  3. * a demo to show that different name mangling technology in C++ and C 
  4.  
  5. * Author: Chaos Lee 
  6.  
  7. */ 
  8.   
  9. #include<stdio.h> 
  10.   
  11. #ifdef __cplusplus 
  12.  
  13. extern "C" { 
  14.  
  15. #endif 
  16. int rect_area(int x1,int x2,int y1,int y2) 
  17.  
  18.         return (x2-x1) * (y2-y1); 
  19.   
  20. int elipse_area(int a,int b) 
  21.  
  22.         return (int)(3.14 * a * b); 
  23.   
  24. #ifdef __cplusplus 
  25.  
  26. #endif 
  27.   
  28. int main(int argc,char *argv[]) 
  29.  
  30.         int x1 = 10, x2 = 20, y1 = 30, y2 = 40; 
  31.         int a = 3,b=4; 
  32.         int result1 = rect_area(x1,x2,y1,y2); 
  33.         int result2 = elipse_area(a,b); 
  34.         return 0; 
下面是使用gcc编译的结果:

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ gcc -c simple_test.c 
  2.  
  3. [lichao@sg01 name_mangling]$ nm simple_test.o 
  4.  
  5. 0000000000000027 T elipse_area 
  6.  
  7. 0000000000000051 T main 
  8.  
  9. 0000000000000000 T rect_area 
在使用g++编译一次:

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ g++ -c simple_test.c 
  2.  
  3. [lichao@sg01 name_mangling]$ nm simple_test.o 
  4.  
  5.                  U __gxx_personality_v0 
  6.  
  7. 0000000000000028 T elipse_area 
  8.  
  9. 0000000000000052 T main 
  10.  
  11. 0000000000000000 T rect_area 
可见,使用extern “C”关键字以后,符号按照C语言的格式来组织了。

事实上,C标准库中使用了大量的extern “C”关键字,由于C标准库也是能够用C++编译器编译的,可是要确保编译以后仍然保持C的接口而不是C++的接口(由于是C标准库),因此须要使用extern “C”关键字。网络

下面是一个简单的例子:
 
  
  
           
  
  
  1. /* 
  2. * libc_test.c 
  3. * a demo program to show that how the standard C 
  4.  
  5. * library are compiled when encountering a C++ compiler 
  6.  
  7. */ 
  8. #include<stdio.h> 
  9. int main(int argc,char * argv[]) 
  10.  
  11.         puts("hello world.\n"); 
  12.         return 0; 

搜索一下puts,咱们并无看到extern “C”.奇怪么?ide

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ g++ -E libc_test.c | grep 'puts' 
  2.  
  3. extern int fputs (__const char *__restrict __s, FILE *__restrict __stream); 
  4.  
  5. extern int puts (__const char *__s); 
  6.  
  7. extern int fputs_unlocked (__const char *__restrict __s, 
  8.  
  9.  puts("hello world.\n"); 
搜索一下 extern “C”试下

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ g++ -E libc_test.c | grep 'extern "C"' 
  2.  
  3. extern "C" { 
  4.  
  5. extern "C" { 
这是因为extern “C”可使用{}的形式将其做用域内的函数所有声明为C语言可调用的接口形式。
标准

不一样编译器使用不一样的方式进行name mangling, 你可能会问为何不将C++的 name mangling标准化,这样就能实现各个编译器之间的互操做了。事实上,在C++的FAQ列表上有对此问题的回答:函数

"Compilers differ as to how objects are laid out, how multiple inheritance is implemented, how virtual function calls are handled, and so on, so if the name mangling were made the same, your programs would link against libraries provided from other compilers but then crash when run. For this reason, the ARM (Annotated C++ Reference Manual) encourages compiler writers to make their name mangling different from that of other compilers for the same platform. Incompatible libraries are then detected at link time, rather than at run time."工具

“编译器因为内部实现的不一样而不一样,内部实现包括对象在内存中的布局,继承的实现,虚函数调用处理等等。因此若是将name mangling标准化了,不错,你的程序确实可以连接成功,可是运行确定要崩的。偏偏是由于这个缘由,ARM鼓励为同一平台提供的不一样编译器应该使用不一样的name mangling方式。这样在编译的时候,不兼容的库就会被检测到,而不至于连接时虽然经过了,可是运行时崩溃了。”
显然,这是基于“运行时崩溃比连接时失败的代价更大”这个原则而考虑的。
GCC name mangling

GCC采用IA 64的name mangling方案,此方案定义于Intel IA64 standard ABI.在g++的FAQ列表中有如下一段话:
       "GNU C++ does not do name mangling in the same way as other C++ compilers.布局

This means that object files compiled with one compiler cannot be used with

another”

GNU C++的name mangling方案和其余C++编译器方案不一样,因此一种编译器生成的目标文件并不能被另一种编译器生成的目标文件使用。

如下为内置的编码类型:

 

  
  
           
  
  
  1. Builtin types encoding 
  2.  
  3.   <builtin-type> ::= v  # void 
  4.                  ::= w  # wchar_t 
  5.                  ::= b  # bool 
  6.                  ::= c  # char 
  7.                  ::= a  # signed char 
  8.                  ::= h  # unsigned char 
  9.                  ::= s  # short 
  10.                  ::= t  # unsigned short 
  11.                  ::= i  # int 
  12.                  ::= j  # unsigned int 
  13.                  ::= l  # long 
  14.                  ::= m  # unsigned long 
  15.                  ::= x  # long long, __int64 
  16.                  ::= y  # unsigned long long, __int64 
  17.                  ::= n  # __int128 
  18.                  ::= o  # unsigned __int128 
  19.                  ::= f  # float 
  20.                  ::= d  # double 
  21.                  ::= e  # long double, __float80 
  22.                  ::= g  # __float128 
  23.                  ::= z  # ellipsis 
  24.                  ::= u <source-name>    # vendor extended type 
操做符编码:

Operator encoding

 
  
  
           
  
  
  1. <operator-name> ::= nw # new           
  2.                  ::= na        # new[] 
  3.                  ::= dl        # delete        
  4.                  ::= da        # delete[]      
  5.                  ::= ps        # + (unary) 
  6.                  ::= ng        # - (unary)     
  7.                  ::= ad        # & (unary)     
  8.                  ::= de        # * (unary)     
  9.                  ::= co        # ~             
  10.                  ::= pl        # +             
  11.                  ::= mi        # -   
  12.  
  13.                                   ::= ml        # *             
  14.  
  15.                  ::= dv        # /             
  16.                  ::= rm        # %             
  17.                  ::= an        # &             
  18.                  ::= or        # |             
  19.                  ::= eo        # ^             
  20.                  ::= aS        # =             
  21.                  ::= pL        # +=            
  22.                  ::= mI        # -=            
  23.                  ::= mL        # *=            
  24.                  ::= dV        # /=            
  25.                  ::= rM        # %=            
  26.                  ::= aN        # &=            
  27.                  ::= oR        # |=            
  28.                  ::= eO        # ^=            
  29.                  ::= ls        # <<            
  30.                  ::= rs        # >>            
  31.                  ::= lS        # <<=           
  32.                  ::= rS        # >>=           
  33.                  ::= eq        # ==            
  34.                  ::= ne        # !=            
  35.                  ::= lt        # <             
  36.                  ::= gt        # >             
  37.                  ::= le        # <=            
  38.                  ::= ge        # >=            
  39.                  ::= nt        # !             
  40.                  ::= aa        # &&            
  41.                  ::= oo        # ||            
  42.                  ::= pp        # ++            
  43.                  ::= mm        # --            
  44.                  ::= cm        # ,              
  45.                  ::= pm        # ->*           
  46.                  ::= pt        # ->            
  47.                  ::= cl        # ()            
  48.                  ::= ix        # []            
  49.                  ::= qu        # ?             
  50.                  ::= st        # sizeof (a type) 
  51.                  ::= sz        # sizeof (an expression) 
  52.                  ::= cv <type> # (cast)        
  53.  
  54.                  ::= v <digit> <source-name>   # vendor extended operator 
类型编码:

 

  
  
           
  
  
  1. <type> ::= <CV-qualifiers> <type> 
  2.  
  3.          ::= P <type>   # pointer-to 
  4.          ::= R <type>   # reference-to 
  5.          ::= O <type>     # rvalue reference-to (C++0x) 
  6.          ::= C <type>   # complex pair (C 2000) 
  7.          ::= G <type>   # imaginary (C 2000) 
  8.          ::= U <source-name> <type>     # vendor extended type qualifier 
下面是一段简单的代码:
 
  
  
           
  
  
  1. /* 
  2. * Author: Chaos Lee 
  3.  
  4. * Description: A simple demo to show how the rules used to mangle functions' names work 
  5.  
  6. * Date:2012/05/06 
  7.  
  8. */ 
  9. #include<iostream> 
  10. #include<string> 
  11. using namespace std; 
  12.  
  13. int test_func(int & tmpInt,const char * ptr,double dou,string str,float f) 
  14.  
  15.         return 0; 
  16. int main(int argc,char * argv[]) 
  17.  
  18.         char * test="test"
  19.         int intNum = 10; 
  20.         double dou = 10.012; 
  21.         string str="str"
  22.         float f = 1.2; 
  23.         test_func(intNum,test,dou,str,f); 
  24.         return 0; 

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ g++ -c func.cpp 
  2.  
  3. [lichao@sg01 name_mangling]$ nm func.cpp 
  4.  
  5. nm: func.cpp: File format not recognized 
  6.  
  7. [lichao@sg01 name_mangling]$ nm func.o 
  8.  
  9. 0000000000000060 t _GLOBAL__I__Z9test_funcRiPKcdSsf 
  10.                  U _Unwind_Resume 
  11. 0000000000000022 t _Z41__static_initialization_and_destruction_0ii 
  12.  
  13. 0000000000000000 T _Z9test_funcRiPKcdSsf 
  14.  
  15.                  U _ZNSaIcEC1Ev 
  16.                  U _ZNSaIcED1Ev 
  17.                  U _ZNSsC1EPKcRKSaIcE 
  18.                  U _ZNSsC1ERKSs 
  19.                  U _ZNSsD1Ev 
  20.                  U _ZNSt8ios_base4InitC1Ev 
  21.                  U _ZNSt8ios_base4InitD1Ev 
  22. 0000000000000000 b _ZSt8__ioinit 
  23.  
  24.                  U __cxa_atexit 
  25.                  U __dso_handle 
  26.                  U __gxx_personality_v0 
  27. 0000000000000076 t __tcf_0 
  28.  
  29. 000000000000008e T main 

加粗的那行就是函数test_func通过name mangling以后的结果,其中:

l Ri,表示对整型变量的引用
l PKc:表示const char *指针
Ss :目前尚未找到缘由。先留着~
l f:表示浮点型
name demangling

C++的name mangling技术通常使得函数变得面目全非,而不少状况下咱们在查看这些符号的时候并不须要看到这些函数name mangling以后的效果,而是想看看是否认义了某个函数,或者是否引用了某个函数,这对于咱们调试程序是很是有帮助的。

因此须要一种方法从name mangling以后的符号变换为name mangling以前的符号,这个过程称之为name demangling.事实上有不少工具提供这些功能,最经常使用的就是c++file命令,c++filt命令接受一个name mangling以后的符号做为输入并输出demangling以后的符号。例如:

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ c++filt _Z9test_funcRiPKcdSsf 
  2.  
  3. test_func(int&, char const*, double, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, float
通常更经常使用的方法为:

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ nm func.o | c++filt 
  2.  
  3. 0000000000000060 t global constructors keyed to _Z9test_funcRiPKcdSsf 
  4.  
  5.                  U _Unwind_Resume 
  6. 0000000000000022 t __static_initialization_and_destruction_0(intint
  7.  
  8. 0000000000000000 T test_func(int&, char const*, double, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, float
  9.  
  10.                  U std::allocator<char>::allocator() 
  11.  
  12.                  U std::allocator<char>::~allocator() 
  13.  
  14.                  U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) 
  15.  
  16.                  U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) 
  17.  
  18.                  U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() 
  19.  
  20.                  U std::ios_base::Init::Init() 
  21.                  U std::ios_base::Init::~Init() 
  22. 0000000000000000 b std::__ioinit 
  23.  
  24.                  U __cxa_atexit 
  25.                  U __dso_handle 
  26.                  U __gxx_personality_v0 
  27. 0000000000000076 t __tcf_0 
  28.  
  29. 000000000000008e T main 
另外使用nm命令也能够demangle符号,使用选项-C便可,例如:

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ nm -C func.o 
  2.  
  3. 0000000000000060 t global constructors keyed to _Z9test_funcRiPKcdSsf 
  4.  
  5.                  U _Unwind_Resume 
  6. 0000000000000022 t __static_initialization_and_destruction_0(intint
  7.  
  8. 0000000000000000 T test_func(int&, char const*, double, std::string, float
  9.  
  10.                  U std::allocator<char>::allocator() 
  11.  
  12.                  U std::allocator<char>::~allocator() 
  13.  
  14.                  U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) 
  15.  
  16.                  U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) 
  17.  
  18.                  U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() 
  19.  
  20.                  U std::ios_base::Init::Init() 
  21.                  U std::ios_base::Init::~Init() 
  22. 0000000000000000 b std::__ioinit 
  23.  
  24.                  U __cxa_atexit 
  25.                  U __dso_handle 
  26.                  U __gxx_personality_v0 
  27. 0000000000000076 t __tcf_0 
  28.  
  29. 000000000000008e T main 

又到了Last but not least important的时候了,还有一个特别重要的接口函数就是__cxa_demangle(),此函数的原型为:

 
  
  
           
  
  
  1. namespace abi { 
  2. extern "C" char* __cxa_demangle (const char* mangled_name, 
  3.  
  4. char* buf, 
  5. size_t* n, 
  6. int* status); 
用于将mangled_name所指向的mangled进行demangle并将结果存放在buf中,n为buf的大小。status存放函数执行的结果,返回值为0表示执行成功。
下面是使用这个接口函数进行demangle的例子:
 
  
  
           
  
  
  1. /* 
  2. * Author: Chaos Lee 
  3.  
  4. * Description: Employ __cxa_demangle to demangle a mangling function name. 
  5.  
  6. * Date:2012/05/06 
  7.  
  8. * 
  9. */ 
  10. #include<iostream> 
  11. #include<cxxabi.h> 
  12. using namespace std; 
  13.  
  14. using namespace abi; 
  15.  
  16. int main(int argc,char *argv[]) 
  17.  
  18.         const char * mangled_string = "_Z9test_funcRiPKcdSsf"
  19.  
  20.         char buffer[100]; 
  21.         int status; 
  22.         size_t n=100; 
  23.         __cxa_demangle(mangled_string,buffer,&n,&status); 
  24.  
  25.         cout<<buffer<<endl; 
  26.         cout<<status<<endl; 
  27.         return 0; 
测试结果:

 

  
  
           
  
  
  1. [lichao@sg01 name_mangling]$ g++ cxa_demangle.cpp -o cxa_demangle 
  2.  
  3. [lichao@sg01 name_mangling]$ ./cxa_demangle 
  4.  
  5. test_func(int&, char const*, double, std::string, float
  6.  
name mangling 与***
l 使用demangling能够破解动态连接库中的没有公开的API

l 编写名称为name mangling接口函数,打开重复符号的编译开关,能够替换原来函数中连接函数的指向,从而改变程序的运行结果。

相关文章
相关标签/搜索