GCC(binutils)のツールの紹介: nm&c++filt

前回の続きでBinutilsのツール群の説明をします。
今回はnmとc++filtを説明します。
これらのツールはどれも前回説明したobjdumpの一部の機能みたいなものです。

ソースコード

#include <cstdio>
const static int CONST_VALUE = 100;
int uninitialized_value;

class SuperClass {
public:
    virtual void print(const char *str) = 0;
};

class Sub1 : public SuperClass{
public:
    void print(const char *str){
        printf("Sub1: %s\n", str);
    }
};

class Sub2 : public SuperClass{
public:
    void print(const char *str){
        printf("Sub2: %s\n", str);
    }
};

int main(void){
    SuperClass *hoge = new Sub1();
    printf("CONST_VALUE: %p\n", &CONST_VALUE);
    printf("uninitialized_value: %p\n", &uninitialized_value);
    return 0;
}

コンパイルしておきます。今回はリンクまでします。

$ g++ -g test.cpp
$ ls
a.out test.cpp
$ ./a.out
CONST_VALUE: 0x8048774
uninitialized_value: 0x8049a34

nm

nmコマンドはオブジェクトの中のシンボルを一覧表示します。

$ nm a.out # 一部削ってます
08049898 d _DYNAMIC
08049984 d _GLOBAL_OFFSET_TABLE_
0804873c R _IO_stdin_used
         w _Jv_RegisterClasses
08048774 r _ZL11CONST_VALUE
08048654 W _ZN10SuperClassC2Ev
08048638 W _ZN4Sub15printEPKc
08048662 W _ZN4Sub1C1Ev
080487b8 V _ZTI10SuperClass
0804879c V _ZTI4Sub1
080487a8 V _ZTS10SuperClass
08048794 V _ZTS4Sub1
08048788 V _ZTV10SuperClass
08048778 V _ZTV4Sub1
080499c0 V _ZTVN10__cxxabiv117__class_type_infoE@@CXXABI_1.3
08049a00 V _ZTVN10__cxxabiv120__si_class_type_infoE@@CXXABI_1.3
         U _Znwj@@GLIBCXX_3.4
080485d4 T main
         U printf@@GLIBC_2.0
08049a34 B uninitialized_value

2列目に表示されるシンボルの意味は以下の通りです。
大文字は外部公開されるシンボルで、小文字はされないシンボルとなります。

  • A 絶対値。リンク後も値が変わらない。
  • B,b 実初期化データ(BSS)
  • D,d 初期化済みデータ(Dataセクション)
  • R,r リードオンリー
  • T,t コード(Textセクション)
  • U 未定義シンボル
  • V,v ウィークオブジェクト。
  • W,w ウィークシンボル。

ソースコードの実行結果と比較してみると、uninitialized_valueとCONST_VALUEのアドレスが一致していることがわかります。

c++filt

c++ではシンボル名が一意になるように名前マングルされるため、ぱっと見では何の関数かよくわかりません。
そこでデマングルするためのツールがこのc++filtです。
単純な使い方は引数にデマングルしたいシンボル名を渡すだけですが、標準入力から渡すこともできます。

$ nm a.out | c++filt
08049898 d _DYNAMIC
08049984 d _GLOBAL_OFFSET_TABLE_
0804873c R _IO_stdin_used
         w _Jv_RegisterClasses
08048774 r CONST_VALUE
08048654 W SuperClass::SuperClass()
08048638 W Sub1::print(char const*)
08048662 W Sub1::Sub1()
080487b8 V typeinfo for SuperClass
0804879c V typeinfo for Sub1
080487a8 V typeinfo name for SuperClass
08048794 V typeinfo name for Sub1
08048788 V vtable for SuperClass
08048778 V vtable for Sub1
080499c0 V vtable for __cxxabiv1::__class_type_info@@CXXABI_1.3
08049a00 V vtable for __cxxabiv1::__si_class_type_info@@CXXABI_1.3
         U operator new(unsigned int)@@GLIBCXX_3.4
080485d4 T main
         U printf@@GLIBC_2.0
08049a34 B uninitialized_value

この例では、nm自体にデマングルの機能があるためわざわざc++filtを使う必要はないです

$ nm -C a.out

シンボル表をどういったケースで使うかをぱっと思いつかなかったので今回は割愛します。
実際にはデバッグなどで関数のアドレスを知りたいときとかにobjdumpではなくシンボル一覧から探したりすることは結構あります。