15

私の質問:

  1. 関数ポインタの等価性はC標準で保証されていますか?
  2. (1)の答えが「はい」の場合。異なる最終コンパイル ユニット (メインの実行可能ファイルと共有ライブラリなど) で取得されるポインターに関係なく、これは当てはまりますか?
  3. 動的ローダーはそれをどのように処理しますか? (これが難しい理由はいくつか考えられますが、すべて PIC コードに関連しています (たとえば、elf の GOT テーブルや、それに相当する COFF が使用するもの))。(1) と (2) に関係なく、Linux ローダーはこれを保証しているようです。

ここに例があります。上記の質問は、C が何main.cを出力 するかを保証するかどうか、"Function equality: 1"または"Function equality: 0"最初のケースでは、動的ローダーがそれをどのように実現するかということになります。

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;
}

Unix では、これは次のコマンドでコンパイルされます。

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

そして、以下で実行:

LD_LIBRARY_PATH=. ./main
4

1 に答える 1

14

C 2011 (N1570 委員会草案) 6.5.9 6: 「2 つのポインターが等しいのは、…両方が同じ…関数へのポインターである場合のみ…. したがって、同じ関数への 2 つのポインターは等しいと言えます。

関数のアドレスが 2 つの異なるオブジェクト モジュールで取得されると、コンパイラはオブジェクト コードにプレースホルダーを配置します。そのプレースホルダーは、オブジェクト モジュールが実行可能ファイルにリンクされるか、実行時に動的ライブラリにリンクされるときに入力されます。

動的ライブラリの場合、動的ローダーが必要に応じて実行可能ファイルのすべてのプレースホルダーを埋めるか、各関数のアドレスが実際には実際の関数にジャンプするスタブ コードの場所であり、そのスタブ コード内またはスタブ コードによって使用されるプレースホルダーが埋められます。ダイナミックローダーによって。

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

于 2013-02-20T17:05:28.097 に答える