[解決済み] 静的にリンクされたライブラリ間のシンボル衝突に対処するには?
質問
ライブラリを書く際の最も重要なルールとベストプラクティスの1つは、ライブラリのすべてのシンボルをライブラリ固有の名前空間に置くことです。
をライブラリ固有の名前空間に置くことです。C++では
namespace
というキーワードがあるためです。Cでは
C では、通常のアプローチは、識別子の前にライブラリ固有の接頭辞を付けることです。
C 標準の規則では、(安全なコンパイルのために)それらにいくつかの制約を加えています。C コンパイラーは識別子の最初の 8 文字だけを見ることができます。
8 文字しか見ませんので
foobar2k_eggs
と
foobar2k_spam
は同じ
しかし、現代のすべてのコンパイラは、任意の長い識別子を許容しています。
(21世紀) では、このことについて悩む必要はないはずです。
しかし、シンボル名や識別子を変更できないライブラリに直面した場合はどうでしょうか。もしかしたら、あなたは 静的なバイナリおよびヘッダーだけを入手した場合、または、自分で調整および再コンパイルすることを望まないか、許可されていない場合です。
どのように解決するのですか?
少なくとも 静的 ライブラリの場合、非常に便利にそれを回避することができます。
ライブラリのヘッダを考えてみましょう。 foo と bar . このチュートリアルのために、ソースファイルもあげておきます。
例/ex01/foo.h
int spam(void);
double eggs(void);
examples/ex01/foo.c (これは不透明/利用できない可能性があります)
int the_spams;
double the_eggs;
int spam()
{
return the_spams++;
}
double eggs()
{
return the_eggs--;
}
例/ex01/bar.h
int spam(int new_spams);
double eggs(double new_eggs);
examples/ex01/bar.c (これは不透明か利用できないかもしれません)
int the_spams;
double the_eggs;
int spam(int new_spams)
{
int old_spams = the_spams;
the_spams = new_spams;
return old_spams;
}
double eggs(double new_eggs)
{
double old_eggs = the_eggs;
the_eggs = new_eggs;
return old_eggs;
}
これらをプログラムfoobarで使用したい。
例/ex01/foobar.c
#include <stdio.h>
#include "foo.h"
#include "bar.h"
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", spam(), eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
spam(new_bar_spam), new_bar_spam,
eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
一つの問題がすぐに明らかになります。Cはオーバーロードを知らないのです。つまり、同じ名前で署名が異なる2つの関数が2回あります。 が2つあることになります。だから、それを区別する方法が必要なんだ。ともかく、これに関して コンパイラがこれに対して何を言うか見てみましょう。
example/ex01/ $ make
cc -c -o foobar.o foobar.c
In file included from foobar.c:4:
bar.h:1: error: conflicting types for ‘spam’
foo.h:1: note: previous declaration of ‘spam’ was here
bar.h:2: error: conflicting types for ‘eggs’
foo.h:2: note: previous declaration of ‘eggs’ was here
foobar.c: In function ‘main’:
foobar.c:11: error: too few arguments to function ‘spam’
foobar.c:11: error: too few arguments to function ‘eggs’
make: *** [foobar.o] Error 1
さて、これは驚きではなく、私たちがすでに知っていたこと、あるいは少なくとも疑っていたことを教えてくれただけです。
では、オリジナルのライブラリのソースコードやヘッダーを変更することなく、どうにかして識別子の衝突を解決することはできないでしょうか? ソースコードやヘッダーを変更することなく、この識別子の衝突を解決することはできるでしょうか? 実際、可能です。
まず、コンパイル時の問題を解決しましょう。このために、ヘッダーインクルードを
プリプロセッサの束
#define
ディレクティブで囲み、ライブラリからエクスポートされる全てのシンボルにプレフィックスを付けます。
後で、私たちは、いくつかの素晴らしい居心地の良いラッパーヘッダを使用してこれを行います。
でそのままやっています。
foobar.c
ソース ファイルでそのまま行っています。
例/ex02/foobar.c
#include <stdio.h>
#define spam foo_spam
#define eggs foo_eggs
# include "foo.h"
#undef spam
#undef eggs
#define spam bar_spam
#define eggs bar_eggs
# include "bar.h"
#undef spam
#undef eggs
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", foo_spam(), foo_eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
bar_spam(new_bar_spam), new_bar_spam,
bar_eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
さて、これをコンパイルすると...
example/ex02/ $ make
cc -c -o foobar.o foobar.c
cc foobar.o foo.o bar.o -o foobar
bar.o: In function `spam':
bar.c:(.text+0x0): multiple definition of `spam'
foo.o:foo.c:(.text+0x0): first defined here
bar.o: In function `eggs':
bar.c:(.text+0x1e): multiple definition of `eggs'
foo.o:foo.c:(.text+0x19): first defined here
foobar.o: In function `main':
foobar.c:(.text+0x1e): undefined reference to `foo_eggs'
foobar.c:(.text+0x28): undefined reference to `foo_spam'
foobar.c:(.text+0x4d): undefined reference to `bar_eggs'
foobar.c:(.text+0x5c): undefined reference to `bar_spam'
collect2: ld returned 1 exit status
make: *** [foobar] Error 1
...最初は事態が悪化したように見えます。しかし、よく見てください。実はコンパイル段階は はうまくいきました。リンカが、シンボルが衝突していると文句を言っているだけです。 そして、それが発生した場所(ソースファイルと行)を教えてくれるのです。そして、見ての通り これらのシンボルは接頭辞なしです。
を持つシンボルテーブルを見てみましょう。 nm ユーティリティを使って見てみましょう。
example/ex02/ $ nm foo.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
example/ex02/ $ nm bar.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
そこで今度は、これらの記号に不透明なバイナリで接頭辞を付けるという演習に挑戦することになります。そう、私は知っています。 この例ではソースがあるので、そこでこれを変更することができます。しかし、今はただ のみを持っていると仮定してください。 .o ファイル、あるいは .a (これは実際には単なる .o ).
objcopy を使用しています。
私たちにとって特に興味深いツールが1つあります。 objcopy
objcopyは一時ファイル上で動作するので、あたかもインプレースで操作しているように使うことができます。1 つの というオプション/操作があります。 -プレフィックス・シンボル というオプションがあり、これが何をするものなのか、3つほど推測できます。
では、この人を私たちの頑固なライブラリに投げてみましょう。
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
nm は、これがうまくいったようであることを示しています。
example/ex03/ $ nm foo.o
0000000000000019 T foo_eggs
0000000000000000 T foo_spam
0000000000000008 C foo_the_eggs
0000000000000004 C foo_the_spams
example/ex03/ $ nm bar.o
000000000000001e T bar_eggs
0000000000000000 T bar_spam
0000000000000008 C bar_the_eggs
0000000000000004 C bar_the_spams
この全体をリンクしてみましょう。
example/ex03/ $ make
cc foobar.o foo.o bar.o -o foobar
そして実際、うまくいきました。
example/ex03/ $ ./foobar
foo: spam = 0, eggs = 0.000000
bar: old spam = 0, new spam = 3 ; old eggs = 0.000000, new eggs = 5.000000
を自動的に抽出するツール/スクリプトを実装することは、読者の課題として残されています。 を使ってライブラリのシンボルを自動的に抽出する nm という構造のラッパーのヘッダーファイルを書きます。
/* wrapper header wrapper_foo.h for foo.h */
#define spam foo_spam
#define eggs foo_eggs
/* ... */
#include <foo.h>
#undef spam
#undef eggs
/* ... */
で、シンボルプレフィックスを静的ライブラリのオブジェクトファイルに適用します。 objcopy .
シェアードライブラリは?
原理的には、共有ライブラリでも同じことができます。しかし、共有ライブラリはその名の通り、複数のプログラム間で共有されます。 は複数のプログラムで共有されているため、この方法で共有ライブラリをいじることはあまり良いアイデアではありません。
トランポリンラッパーを書いたところで、どうにもならないでしょう。さらに悪いことに、オブジェクト ファイル レベルで共有ライブラリに対してリンクすることはできません。 に対してリンクすることができず、動的ロードを行うことを余儀なくされます。しかし、これはまさに独自の記事に値するものです。
ご期待ください、そして楽しいコーディングを。
関連
-
赤線の位置は必ずしも間違っていない:式は変更可能なlvalueでなければならないエラーは、この文とは別の場所に存在する可能性があります。
-
#137: 式は変更可能なlvalueでなければならない問題 // 文字列配列の代入問題
-
error: 'for' loop initial declaration is only allowed in C99 mode 原因と解決方法
-
C/C++の再定義
-
エラー: 宣言されていない識別子 'bool' の使用と C コンパイラでの問題点
-
[解決済み] flexible array member not at end of structエラーの原因は何ですか?
-
[解決済み] ++iとi++の違いは何ですか?
-
[解決済み] スタティック・ライブラリとシェアード・ライブラリの違い?
-
[解決済み] なぜ16進数には0xがつくのですか?
-
[解決済み】共有オブジェクト(.so)、静的ライブラリ(.a)、DLL(.so)の違い?)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] munmap_chunk(): 無効なポインタ
-
[解決済み] Xcode - 警告。C99 では関数の暗黙の宣言は無効です。
-
[解決済み] C言語でchar配列をコピーする方法は?
-
[解決済み] flexible array member not at end of structエラーの原因は何ですか?
-
[解決済み] Linux Socket write() によるBad File Descriptor C
-
[解決済み] mallocの結果はキャストするのですか?
-
[解決済み] Cコードの単体テスト【終了しました
-
[解決済み] C言語でランダムなint型を生成するには?
-
[解決済み] C言語標準に準拠した構造体の初期化方法
-
[解決済み] 2つのライブラリが同じ名前の関数を提供し、競合が発生した場合、どうすればよいですか?