2013-02-20 11 views
13

私の質問: C標準で保証関数ポインタの平等が関数ポインタ平等

  1. ですか?
  2. (1)の答えが「はい」の場合。これは、異なる最終コンパイル単位(例えば、メイン実行可能ファイルと共有ライブラリ)で得られたポインタに関係なくですか?
  3. どのようにダイナミックローダーがそれに対処していますか? (これは難しいかもしれないいくつかの理由を考えることができます.PICコード(例えば、elfのGOTテーブル、それに相当するCOFFなど)に関連しています。 (1)と(2)に関係なく、Linuxローダーはこれを保証しているようです。

次に例を示します。上記の質問は、main.cが何を出力するかをCが保証するかどうかまでわかります:"Function equality: 1"または"Function equality: 0"そして、最初のケースでは、どのようにしてダイナミックローダーが発生しますか? UNIXで

common.h:

extern void * getc_main; 
extern void * getc_shared; 
void assign_getc_shared(); 

main.c:

#include <stdio.h> 
#include "common.h" 

int main() 
{ 
    getc_main = (void*) getc; 
    assign_getc_shared(); 
    printf("Function equality: %d\n", getc_main == getc_shared); 
    return 0; 
} 

shared.c:

#include <stdio.h> 
#include "common.h" 

void assign_getc_shared() 
{ 
    getc_shared = (void*) getc; 
} 

この次のコマンドを使用してコンパイルされます:

cc -shared -fPIC -o libshared.so shared.c 
cc -o main main.c -L. -lshared 

として実行:

LD_LIBRARY_PATH=. ./main 
+0

これは、標準ライブラリ関数が実行可能ファイルに1回しか含まれないことを保証しているのかというとかなり長い時間です。 –

+0

そして、私はリスター氏の質問に対する答えは "いいえ、保証されていません" 。たとえば、関数はインライン化されていてもよく、インライン関数のアドレスをとると、コード内に「実際の」関数として含まれます。つまり、同じソース関数に対して複数の関数が存在する可能性があります。 –

+0

@ MrListerもし私がそれだけを知りたいと思っていたら、私はそれに尋ねただけです。余分な質問をする理由は、ダイナミックローダーがこの問題をどう扱うかについての詳細を知りたいからです。あなたのコメントから、あなたはそうではないと思います。それは問題ありません。 – fons

答えて

12

C 2011(N1570委員会草案)6.5.9 6:「二つのポインタがあれば等しい比較し、両者が同じ...関数へのポインタであるだけ...あれば...。つまり、同じ関数への2つのポインタが等しいと比較します。

2つの異なるオブジェクトモジュールで関数のアドレスをとると、コンパイラはオブジェクトコードにプレースホルダを置きます。そのプレースホルダは、実行時にオブジェクトモジュールが実行可能ファイルにリンクされるか、ダイナミックライブラリにリンクされるときに埋められます。

動的ライブラリの場合、必要に応じて実行可能ファイル内のすべてのプレースホルダを入力するか、実際の関数にジャンプするスタブコードの位置と、その関数内のプレースホルダスタブコードは動的ローダによって埋められます。

さらに、実行可能ファイルには複数の関数インスタンスが含まれる可能性があることに注意してください。コンパイラは、関数をいくつかの場所にインラインで挿入するか、それ自体の理由から、関数の特殊化と一般バージョンを含めることができます。しかし、関数のアドレスが取られるとき、コンパイラは単一の汎用バージョンのアドレスを提供しなければならない。 (あるいは、コンパイラは、プログラムが実行されたかのように動作するようにする必要があります。例えば、コンパイラがポインタを比較しないことを検出できれば、理論的にはアドレスのいくつかのインスタンス)

+0

これは、80x86リアルモードでC 2011準拠のCコンパイラを実装することが不可能であることを意味します。メモリ内の任意のポイントは、4096個の異なる(遠く/巨大な)ポインタを介してアクセスできます。 –

+4

@tristopia:あなたの結論がどのように続くかわかりません。各アドレスが4096の潜在的な表現を有するという事実は、コンパイラが同じアドレスの異なる表現が等しいことを保証するのを妨げるものではない。コンパイラは、単一命令で 'a == b'を実装する必要はありません。 'a'と' b'のそれぞれをどのような形式からでも完全な一意のアドレスに変換し、結果として得られる完全なアドレスを比較するために算術演算を実行することは自由です。 –

+3

さらに、コンパイラはアドレスの取得を制御するので、関数アドレスの特定の表現が使用され、他のものが確実に使用されないことがあります。 –