6

私の本ExpertCProgrammingを読んで、関数の介在と、それが意図せずに行われた場合にバグを見つけるのが難しい深刻な問題につながる可能性があるという章に出くわしました。

この本で与えられている例は次のとおりです。

my_source.c

mktemp() { ... }

main() {
  mktemp();
  getwd();
}

libc

mktemp(){ ... }
getwd(){ ...; mktemp(); ... }

この本によると、何が起こるかとmain()いうと、mktemp()(標準Cライブラリ関数)がmy_source.cの実装によって挿入されているということです。main()私の実装を呼び出すことmktemp()は意図された動作ですが、getwd()(別のCライブラリ関数)私の実装を呼び出すことは意図されmktemp()ていません。

どうやら、この例は、SunOS4.0.3のバージョンのに存在した実際のバグでしたlpr。この本は、my_source.cstaticの定義にキーワードを追加することで修正されたことを説明しています。名前を完全に変更すると、この問題も修正されたはずですが。 mktemp()

この章では、未解決の質問をいくつか残しておきます。皆さんが答えてくれることを願っています。

  1. GCCには機能の介入について警告する方法がありますか?私たちは確かにこれが起こることを意図していません、そして私はそれが起こったらそれについて知りたいです。
  2. static私たちのソフトウェアグループは、公開したくないすべての機能の前にキーワードを配置するという慣習を採用する必要がありますか?
  3. 静的ライブラリによって導入された関数で介入が発生する可能性はありますか?

助けてくれてありがとう。

編集

私の質問は、標準Cライブラリ関数だけでなく、他のライブラリ、おそらくサードパーティ、おそらく社内で作成されたライブラリに含まれる関数にも介入することを目的としていることに注意してください。基本的に、介在する関数がどこにあるかに関係なく、介在するインスタンスをキャッチしたいと思います。

4

6 に答える 6

2

これは実際にはリンカーの問題です。

多数のCソースファイルをコンパイルすると、コンパイラはそれぞれのオブジェクトファイルを作成します。各.oファイルには、このモジュールのパブリック関数のリストに加えて、モジュール内のコードによって呼び出される関数のリストが含まれますが、実際にはそこで定義されていません。つまり、このモジュールがライブラリの提供を期待している関数です。

多数の.oファイルをリンクして実行可能ファイルを作成する場合、リンカーはこれらの欠落している参照をすべて解決する必要があります。これは、介入が発生する可能性があるポイントです。「mktemp」と呼ばれる関数への未解決の参照があり、いくつかのライブラリがその名前のパブリック関数を提供している場合、どのバージョンを使用する必要がありますか?これに対する簡単な答えはありません。間違ったものを選択すると、奇妙なことが起こる可能性があります。

そうです、他のソースファイルから実際に使用する必要がない限り、Cではすべてを「静的」にすることをお勧めします。実際、他の多くの言語では、これがデフォルトの動作であり、外部からアクセスできるようにする場合は、「公開」とマークする必要があります。

于 2010-05-06T17:24:49.223 に答える
1

純粋に形式的には、あなたが説明する介入は、C言語定義規則(C ++用語ではODR規則)の直接的な違反です。適切なコンパイラは、これらの状況を検出するか、それらを検出するためのオプションを提供する必要があります。これらの関数がどこで定義されているかに関係なく、C言語で同じ名前の複数の関数を定義することは単に違法です(標準ライブラリ、他のユーザーライブラリなど)。

多くのプラットフォームが、いくつかの標準関数を弱い記号として定義することにより、[標準]ライブラリの動作をカスタマイズする手段を提供していることを理解しています。これは確かに便利な機能ですが、コンパイラーは、標準の診断を実施する手段をユーザーに提供する必要があると思います(機能ごとまたはライブラリごとに)。

したがって、ライブラリに弱いシンボルがない場合でも、介入について心配する必要はありません。実行する場合(または実行する疑いがある場合)は、コンパイラのドキュメントを参照して、弱い記号の解像度を検査する手段が提供されているかどうかを確認する必要があります。

たとえば、GCCでは、を使用して弱い記号機能を無効にすることができますが-fno-weak、これは基本的に弱い記号に関連するすべてのものを強制終了します。これは必ずしも望ましいとは限りません。

于 2010-05-06T17:19:35.947 に答える
1

関数に名前の競合があることをツールが検出することが必要なようです。つまり、外部からアクセス可能な関数名が誤って同じ名前になり、同じ名前の関数を「オーバーライド」または非表示にしたくない場合です。ライブラリ内の名前。

この問題に関連する最近のSOの質問がありました:GCCを使用した重複するクラス名を持つライブラリのリンク

リンク先のすべてのライブラリでオプションを使用する--whole-archiveと役立つ場合があります(ただし、あちらの回答で述べたように、これがどれほどうまく機能するか、またはビルドにオプションをすべてのライブラリに適用するよう説得するのがどれほど簡単かはわかりません)

于 2010-05-06T18:37:43.510 に答える
0

関数が存在するCファイルの外部からアクセスする必要がない場合は、そうです。関数を作成することをお勧めしますstatic

これをキャッチするためにできることの1つは、構成可能な構文の強調表示を備えたエディターを使用することです。私は個人的にSciTEを使用しており、すべての標準ライブラリ関数名を赤で表示するように構成しています。そうすれば、使用すべきでない名前を再利用しているかどうかを簡単に見つけることができます(ただし、コンパイラーによって強制されるものはありません)。

于 2010-05-06T17:03:13.250 に答える
0

nm -oすべての.oファイルとライブラリで実行され、プログラムとライブラリの両方で外部名が定義されているかどうかを確認するスクリプトを作成するのは比較的簡単です。Unixリンカーが1974年にスタックし、一度に1つのファイルを調べているために提供されていない、多くの賢明なサービスの1つにすぎません。(ライブラリを間違った順序で配置してみて、有用なエラーメッセージが表示されるかどうかを確認してください!)

于 2010-05-07T02:49:18.833 に答える
0

インターポジショニングは、リンカが個別のモジュールをリンクしようとしているときに発生します。 モジュール内では発生しません。モジュールに重複するシンボルがある場合、リンカはこれをエラーとして報告します。

* nixリンカーの場合、意図しないインターポジショニングが問題になり、リンカーがそれを防ぐことは困難です。この回答の目的のために、2つのリンク段階を検討してください。

  1. リンカは、変換ユニットをモジュール(基本的にはアプリケーションまたはライブラリ)にリンクします。
  2. リンカは、モジュールを検索することにより、残っている見つからないシンボルをリンクします。

「エキスパートCプログラミング」およびSiegeXの質問で説明されているシナリオを検討してください。リンカの拳は、アプリケーションモジュールを構築しようとします。シンボルmktemp()が外部であると評価し、シンボルの関数定義を見つけようとします。リンカは、アプリケーションモジュールのオブジェクトコードで関数の定義を見つけ、シンボルを見つかったものとしてマークします。この段階で、シンボルmktemp()は完全に解決されます。anothereモジュールがシンボルを定義する可能性を考慮して、暫定的なものとは見なされません。多くの点で、これは理にかなっています。リンカは最初に、現在リンクしているモジュール内の外部シンボルを解決しようとする必要があるためです。他のモジュールにリンクするときに検索するのは、見つからないシンボルだけです。さらに、シンボルは解決済みとしてマークされているため、リンカは、このシンボルを解決する必要がある他の場合には、アプリケーションmktemp()を使用します。したがって、アプリケーションバージョンのmktemp()がライブラリによって使用されます。

問題を防ぐ簡単な方法は、アプリケーションまたはライブラリ内のすべての外部システムを一意にすることです。限定的にのみ共有されるモジュールの場合、一意の識別子を追加してモジュール内のすべての外部シンボルが一意であることを確認することで、これをかなり簡単に行うことができます。

広く共有されているモジュールの場合、一意の名前を構成することが問題になります。

于 2016-07-14T11:53:23.187 に答える