Help us understand the problem. What is going on with this article?

[C++]コンパイルしたら全く同じ関数が2つバイナリに埋め込まれた

More than 1 year has passed since last update.

はじめに

C++で書かれたソースコードをコンパイルすると、ソースコードに書かれている関数がバイナリに埋め込まれ、呼び出し可能な状態で保存される。

しかし、GCCにおいてはある条件下で全く同名の関数が埋め込まれるということが分かった。

今後同じような状況に遭遇した人が混乱しないように記事を残しておく。

実験

問題のないケース

foo.hpp
int foo();
foo.cpp
#include "foo.hpp"

int foo() { return 1; }

これは全く問題ない。foo関数がバイナリに埋め込まれるのは一度だけである。

$ g++ -c foo.cpp

$ nm --demangle foo.o
0000000000000000 T foo()

シンボルがTとなっているのは、バイナリに実装が埋め込まれていることを示している。

問題のケース

ことが起きたのは、コンストラクタやデストラクタを定義した場合である。

bar.hpp
struct Bar {
  int var;
  Bar();
  ~Bar();
};
bar.cpp
#include "bar.hpp"

Bar::Bar() { var = 1; }
Bar::~Bar() { var = 2; }

先ほどと同様にコンパイルしてみると、奇妙な現象が起こる。

$ g++ -c bar.cpp

$ nm --demangle bar.o
0000000000000000 T Bar::Bar()
0000000000000000 T Bar::Bar()
0000000000000016 T Bar::~Bar()
0000000000000016 T Bar::~Bar()

なんと、全く同名の関数が2つずつ埋め込まれている。しかも、Tというシンボルは実装が埋め込まれていることを示している。

私が最初これを見た時、多重定義(ODR違反)ではないか?またGCCのバグか?と思ったがそんなことはないようだ。

Two identical constructors emitted? That's not a bug!

 

これによると、GCCでは一つのコンストラクタから以下のような関数群を生成する。

complete object constructor
オブジェクトそのものを生成するための関数
 
base object constructor
基底クラスのコンストラクタとして呼び出した際に使用される関数。ただし、仮想継承を用いていない場合はcomplete object constructorと同じ実装を用いる。
 
complete object allocating constructor
オブジェクトの生成後、operator new(sizeof(class))を呼び出して、メモリを確保するための関数。通常この関数は生成されず、他のコンパイラを併用する場合などに用いられる(参考: Why are C3 allocating constructors never used?)。

デストラクタに対しても同様の関数群が生成される。
またこれらの関数はマングリングした後の名前が異なるが、その名前をデマングルするといずれも同じ名前になるため、同一の関数が複数存在するように見える。

先程のbar.oの中身を、デマングルせずに表示してみよう。

$ nm bar.o
0000000000000000 T _ZN3BarC1Ev
0000000000000000 T _ZN3BarC2Ev
0000000000000016 T _ZN3BarD1Ev
0000000000000016 T _ZN3BarD2Ev

なるほど、確かに、マングリング後の名前は異なっている。

クラス名の後にC1と書いてあるものがcomplete object constructorで、C2はbase object constructorである。

関数のアドレス(offset)が一致しており、同じ実装を用いていることが分かる。

したがって、これは全くもって正常な動作であり、GCCのバグなどではなかったのだ!

このことはGCCのサイトのNon-bugsというセクションにも記載されている。

まとめ

GCCにおいて、コンストラクタやデストラクタの実装を含むソースコードをコンパイルすると全く同名の関数がバイナリに埋め込まれるが、これはGCC本来の仕様で、正常に動作するために必要なものである。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away