tjun月1日記

なんでもいいので毎月書きたい

Shared Libraryでシンボルを隠す方法について

(過去の記事の転載) CやC++でShared Libraryを作るときのメモです。 外に出したいシンボルと、外に出したくないシンボルがあるとき、どのようにしたらよいのか知らなかったので。 常識かもしれないけど、CとC++が両方ある場合にどうすればいいのか、調べてもあんまり情報が出て来なかった。

以下のようなサンプルがあるとします。

hoge.h

#ifndef HOGE_H
#define HOGE

#ifdef __cplusplus
extern "C" {  
#endif

void hoge();

#ifdef __cplusplus
}
#endif

#endif

hoge.c

#include "hoge.h"

void hoge_local() {  
}

void hoge() {  
  hoge_local();
}

fuga.h

class Fuga {  
public:  
  Fuga();
  ~Fuga();
private:  
  int fugafuga();
};

fuga.cpp

#include "hoge.h"
#include "fuga.h"

Fuga::Fuga() {

}
Fuga::~Fuga() {  
}

int Fuga::fugafuga()  
{
  hoge();
  return 0;
}

このようにCとC++が混ざってる状況で、例えば、hoge_localは、内部でしか使わないのでライブラリにしたときに利用して欲しくない、という時があると思います。

コンパイル時にはfPICをつけます。

$ gcc -fPIC -c -o hoge.o hoge.c
$ g++ -fPIC -c -o fuga.o fuga.cpp

試しにそのままshared libraryを作って

g++ -shared -fPIC -o libhogefuga.so fuga.o hoge.o

これでシンボルをみてみると

$ nm -g -C -D libhogefuga2.so ~/dev/tmp2

w _Jv_RegisterClasses
00000000000006d0 T Fuga::fugafuga()
00000000000006bc T Fuga::Fuga()
00000000000006bc T Fuga::Fuga()
00000000000006c6 T Fuga::~Fuga()
00000000000006c6 T Fuga::~Fuga()
0000000000201020 A __bss_start
w __cxa_finalize
w __gmon_start__
0000000000201020 A _edata
0000000000201030 A _end
0000000000000738 T _fini
0000000000000598 T _init
00000000000006ee T hoge
00000000000006e8 T hoge_local

こんな感じでhoge_localもシンボルが残って外から見えてしまいます。

この状況で、hoge_localとfugafugaは隠したい。 そのようなときには、version-script というものを利用します。

先ほどの例で、hoge_localとFuga::fugafuga以外をexportしたいので、以下のように書くことができます。

hogefuga.vermap

{
  global:
    extern "C++" {      
      Fuga::Fuga*;
      Fuga::?Fuga*;
    };
    hoge;
  local:
      *;
};

globalの部分に公開するシンボルを記述して、localは*で残り全部、としてます。 C++ のマングルされたシンボルに対応するため、C++でexportしたいシンボルは extern "C++" {} で囲ってます。

上の例のようにワイルドカードが使えますが、チルダは使えないので、デストラクタのexportでは、"?"で何か1文字、ということを表して代用してます。

このversion-scriptを以下のようなオプションでリンカのオプションに渡して、shared libraryを作ります。

g++ -shared -fPIC -o libhogefuga2.so fuga.o hoge.o -Wl,--version-script=hogefuga.vermap

先ほどと同様にシンボルを見ると、

$ nm -g -C -D libhogefuga2.so ~/dev/tmp2

w _Jv_RegisterClasses
000000000000057c T Fuga::Fuga()
000000000000057c T Fuga::Fuga()
0000000000000586 T Fuga::~Fuga()
0000000000000586 T Fuga::~Fuga()
w __cxa_finalize
w __gmon_start__
00000000000005ae T hoge

とversion-scriptでglobalに書いた部分だけ残っていて、目的が達成されました。

参考にしたサイト

ACCU :: Working with GNU Export Maps