2011年10月25日 星期二

C++名稱修飾(Name Mangling)


在70年代以前,Compiler產生Object檔時
符號名稱和對應的變數、函式名稱是一樣的
也就是說如果你在code宣告定義一個foo函式
foo在Object檔中對應的符號名稱也叫做foo

這樣的設計有個缺點,如果你使用C Library
你就不能使用C Library已經定義好的變數與函式名稱
否則有重複定義的問題

因此後來UNIX就規定code經過編譯之後
要在對應的符號名稱前加入"_"
比如說"foo"就變成"_foo"
這個方法的確可以減少符號衝突的機率
但是並沒辦法完全解決符號衝突的問題
因此C++加入了Namespace來解決符號衝突的問題

C++有繼承和多載等特性
以多載來說,要讓Compiler能分辨兩個相同名稱的函式
就必須要在產生的符號上動一些手腳
這個動作叫做Name Mangling

以下列這個例子來說
int func(int);
float func(float);

class C {
    int func(int);
    class C2 {
        int func(int);
    };
};

namespace N {
    int func(int);
    class C {
        int func(int);
    };
}

這段code一共有6個func函式,他們不同的地方只有參數以及Namespace不一樣
Compiler會使用這些函式的Function Signature來辨別他們
Function Signature會加入函式所在Class以及Namespace等資訊
用來辨別不同的函式,就好像函式的簽名一樣


例如:
class C {                      Function Signature
    int func(int);        ==>  int C::func(int)
    class C2 {
        int func(int);    ==>  int C::C2::func(int)
    };
};

namespace N {
    int func(int);       ==>  int N::func(int)
    class C {
        int func(int);   ==>  int N::C::func(int)
    };
}

當compiler在把source code編譯成object檔的時候
會使用Name Mangling這個方法,把Function Signature修飾後轉成對應的符號
例如(使用GCC):


                            修飾過後的符號名稱
int C::C2::func(int)   ==>  _ZN1C2C24funcEi
int N::C::func(int)    ==>  _ZN1N1C4funcEi
int func(int)            ==>  _Z4funci
float func(float)      ==>  _Z4funcf

GCC的C++名稱修飾規則如下

1.所有的符號都由"_Z"開頭
2.如果是巢狀的名稱,就在開頭的"_Z"之後加上"N"
3.接下來是Namespace以及Class的名稱,寫法為字串長度 + "N" or "C"
4.如果是巢狀名稱的話,最後以"E"結尾
5.E後面接retrun type,int寫為i,float寫為f

此外binutils當中提供一個工具"c++filt"可以幫我們解析修飾過得名稱
ex:
$ c++filt _ZN1N1C4funcEi
N::C::func(int)
C++的global varible以及static varible同樣也會經過修飾
例如Namespace foo中的global varible bar會被修飾成 _ZN3foo3barE

不同編譯器Name Mangling的作法通常會不一樣
例如Visual C++就使用和GCC完全不同的Name Mangling方法


Reference: http://en.wikipedia.org/wiki/Name_mangling
                 程式設計師的自我修養:連結、載入、程式庫

沒有留言:

張貼留言