Name Mangling

news/2024/6/29 12:01:30 标签: 编译器

引子

为了在链接的时候能够让链接器唯一标识全局变量或者函数等的符号,编译器实现了一种name mangling的技术(也叫做name decoration),这个技术能够给所有目标文件中的符号一个唯一的名字。

具体的实现方式就是编译器函数结构体或者其他的数据类型的对象名中附加上相应的类型信息,然后这个信息供链接器使用来实现链接过程中的符号决议。由于很多语言都允许对象同名,这些同名的对象或者处于不同的名称空间下(namespace),或者有不同的函数签名(signatures)。

链接器在链接的时候不仅需要函数名,还需要函数的参数个数以及返回值等类型等信息,这些信息都可以从mangled name中获取。


C中的name mangling

虽然name mangling技术不太用于不支持函数重载的语言中,例如C和经典Pascal,但是在某些场景下,除了名字之外,还需要其他的信息。例如多数编译器都支持多种不同的调用惯例(calling conventions),调用惯例决定了函数参数的入栈方式以及由哪一方负责来清理栈帧。这就需要将函数的调用惯例信息有编译器附加到目标文件的符号名中。例如:

int _cdecl    f (int x) { return 0; }
int _stdcall  g (int y) { return 0; }
int _fastcall h (int z) { return 0; }

然后对应的符号名有可能是:

_f
_g@4
@h@4

C++中的name mangling

C++应该是使用name mangling最为广泛的语言了,但是C++语言却没有为name mangling定义标准,类似于C++ ABI,C++标准应该推出一个标准出来,来去除不同编译器编译到的目标文件之间的壁垒。C++非常复杂的语言特性,例如classtemplatesnamespaces以及符号重载。这些特性都可以使用name mangling技术编码到符号名中。并且不同的编译器有不同的name-mangling体系,所以很少有链接器能够链接不同的编译器编译得到的目标文件。

namespace wikipedia 
{
   class article 
   {
   public:
      std::string format (void); 
         /* = _ZN9wikipedia7article6formatEv */

      bool print_to (std::ostream&); 
         /* = _ZN9wikipedia7article8print_toERSo */

      class wikilink 
      {
      public:
         wikilink (std::string const& name);
            /* = _ZN9wikipedia7article8wikilinkC1ERKSs */
      };
   };
}

如上代码所示就是一个比较复杂的name mangling的例子,所有mangled symbols都以 _Z 开头,对于nestd names,包括名称空间和类都跟在字母N后面,然后就是一组《length, id》,其中length表示标识符的长度,最后以E结尾。例如wikipedia::article::format对应的mangled name如下所示:

_ZN9wikipedia7article6formatE

All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.

不同的编译器定义了不同的name-mangling系统,比较流行就是ItaniumCXXABI和Microsoft Visual C++定义的系统。

extern “C”

C++为了与C兼容,在符号的管理上,C++有一个用来声明或定义一个C的符号的”extern C”关键字。

extern "C" {
    int func(int);
    int var;
}

C++编译器会将在“extern ‘C’ “的大括号内部的代码当做C语言代码处理,也就是说括号中的代码不会使用C++的名称修饰机制。

很多时候我们会碰到有些头文件声明了一些C语言的函数和全局变量,但是这个头文件可能会被C语言代码或C++代码包含。比如很常见的,我们的C语言库函数中的string.h中声明了memset函数,它的原型如下:

void *memset(void *, int, size_t);

如果不加任何处理,C语言程序包含string.h的时候,并且用到了memset这个函数,编译器会正确处理memset的符号引用;但是在C++语言中,编译器会认为这个memset函数是一个C++函数,将memset的符号修饰成_Z6memsetPvii,这样链接器就无法和C语言库中的memset符号进行链接(注意库中符号名是C语言修饰的符号名)。所以对于C++来说,必须使用extern “C”来声明memset函数。

如下代码所示:

#ifdef __cplusplus
extern "C" {
#endif

void *memset (void *, int, size_t);

#ifdef __cplusplus
}
#endif

C++编译器会在编译C++的程序时默认定义这个宏,我们可以使用该条件宏来判断当前编译单元是不是C++代码。如果是C++代码,那么memset会在extern “C”里面被声明;如果是C代码,就直接声明。

C++的name mangling系统

即使不同的编译器采用了相同的”name mangling”系统,还会有很多其他的问题导致不同编译生成的目标文件不能互相链接,因为name mangling只是C++ ABI中很少的一部分,例如异常处理(exception handling),虚表布局(virtual table layout),结构体和栈帧padding等都会导致不同编译器生成的目标文件不兼容。

C++标准没有为”name mangling”指定标准,引用g++问题列表上的一句话:

“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 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”

上面的黑体部分已经说明了为什么C++标准没有将name mangling标准化。虽然有很多说明C++没有必要为name mangling指定标准,但是Itanium C++指定的mangling标准还是被很多编译器采纳了。

Clang中的name mangling

Clang实现了两种name mangling,ItaniumMangleMicrosoftMangle,由于Clang既支持类Unix平台又支持Windows平台,所以实现了两种name mangling系统。

下面我们从源码层面展示一下name mangling,如果我们对C++中的RTTI符号名是如何编码的比较感兴趣,可以定位到相关函数查看具体的实现。

void ItaniumMangleContextImpl::mangleCXXRTTI(QualType Ty, raw_ostream &Out) {
    // <special-name> ::= TI <type>  # typeinfo structure
    assert(!Ty.hasQualifiers() && "RTTI info cannot have top-level qualifiers");
    CXXNameMangler Mangler(*this, Out);
    Mangler.getStream() << "_ZTI";
    Mangler.mangleType(Ty);
}

从图中我们可以看出在Itanium mangle系统中,虚表符号都是以”_ZTI”开头的。

void MicrosoftMangleContextImpl::mangleCXXRTTI(QualType T, raw_ostream &Out) {
    MicrosoftCXXNameMangler Mangler(*this, Out);
    Mangler.getStream() << "\01??_R0";
    Mangler.mangleType(T, SourceRange(), MicrosoftCXXNameMangler::QMM_Result);
    Mangler.getStream() << "@8";
}

如果我们对C++中的虚表符号是如何编码的比较感兴趣,可以参看与虚表mangled name相关的源码。

void ItaniumMangleContextImpl::mangleCXXVTable(const CXXRecordDecl *RD,
         raw_ostream &Out) {
    // <special-name> ::= TV <type>  # virtual table
    CXXNameMangler Mangler(*this, Out);
    Mangler.getStream() << "_ZTV";
    Mangler.mangleNameOrStandardSubstitution(RD);
}

具体的name mangling细则请参见:
Name mangling demystified
Itanium C++ ABI


http://www.niftyadmin.cn/n/1428257.html

相关文章

思路整理 -- 物质与形式

从网上看到一篇不错的文章&#xff0c;里面的一些思想对我们很有指导意义. 笛卡尔认为&#xff0c;物质与形式是可以分离的&#xff0c;从而我们便可以从概念的角度来研究。这带给我们的指导意义是&#xff0c;我们写的代码从某种程度上都是一种物质的表现形式&#xff0c;但是…

数据流分析(三)

想学数据流分析的人还是找一个国外大学的讲义学吧&#xff0c;以下内容都是自己多年前按照自己的理解写的&#xff0c;很多内容可能会误人子弟&#xff0c;sorry #引子 在数据流分析&#xff08;一&#xff09;和数据流分析&#xff08;二&#xff09;中我们介绍了数据流分析的…

思路整理 -- 经典语录

收集的关于编程方面的经典语录&#xff1a; &#xff08;更新中 &#xff09; 1. 过早的优化是一切问题之源。 ---C.A.R.Hoare 2.一切都应该尽可能简单。但要做到这一点你必须掌握复杂性。 3.我们所学的技术是有一定的相通性的&#xff0c;比如当我们学会C之后&#xf…

数据流分析的局限性

业余民科&#xff0c;垃圾内容勿看 引子 前面我们介绍了很多关于数据流分析的知识&#xff0c;虽然说数据流分析是整个代码分析基础中的基础&#xff0c;但是数据流分析还是存在很大的局限性&#xff0c;一部分局限性来自于数据流分析所做的假设&#xff08;例如假设所有路径…

数据库感想 -- 为何要知道各个数据库的优势?

为什么要知道各个数据库的优势&#xff0c;我有自己的擅长的&#xff0c;为何还要了解其他的数据库&#xff1f; 1.知道数据库处理的短板&#xff0c;以及自己所使用数据库的优势。 2.每种数据库的开发&#xff0c;都是有一定偏向的&#xff0c;也就是说其更适合处理某一类数…

clang static analyzer源码分析(一)

引子 clang静态代码分析是clang相对于gcc一个比较能够引起关注的点&#xff0c;特别是clang静态代码分析基于checker的架构和大部分的静态分析工具都不相同。clang静态代码分析使用符号执行的技术执行路径敏感的代码分析&#xff0c;符号执行引擎并不实际进行报错&#xff0c;…

提示the windows installer service could not be accessed

1. 首先检查当前用户有没有管理员权限&#xff0c;因为很多时候低权限用户是不能进行软件安装的。 2. 接下来&#xff0c;从“控制面板-服务”(或者&#xff0c;开始-运行-Services.msc)中察看Windows Installer服务&#xff0c;是否已被停用&#xff0c;并尝试启用该服务(右击…

AOT JIT and Interpretation

在接触虚拟机的时候&#xff0c;首先遇到的三个概念就是AOT、JIT 和 Interpretation&#xff0c;那么这三个概念有什么区别呢&#xff1f; AOT Ahead-of-time(AOT) 是一种编译方式&#xff0c;现在常见的高级语言都会采用这种方式&#xff0c;例如C/C代码以及Java中间代码&…