2

ripレジスタと関数 foo のアドレスを出力する次の C コードがあります。実行可能ファイルを複数回実行すると、 ripと &fooの同じ値が出力されます。

#include <stdio.h>
#include <inttypes.h>

void foo(int x) {
    printf("foo sees %d\n", x);
}

int main(int argc, char *argv[]) {
    uint64_t ip;
    asm("leaq (%%rip), %0;": "=r"(ip));
    printf("rip is 0x%016" PRIx64 "\n", ip);

    void (*fp)(int) = &foo;
    printf("foo is at offset %p\n", fp);
    (*fp)(10);        

    return 0;
}

Q1: リップが変わらないのはなぜですか?

Q2: バイナリとマシンが同じままであれば、&foo も同じままですか?

Q3: &foo はいつ変更できますか?

背景: 関数の実行時間を履歴テーブルに保存しようとしています。関数アドレスを使用してテーブルにインデックスを付け、以前の実行からの偏差を計算することを考えています。

4

3 に答える 3

2

Q1:

プラットフォームによって異なります。一部のプラットフォームでは、プログラムを仮想アドレス空間にロードするため、まったく同じコードがfooに対してまったく同じ仮想アドレスを持ちます(プログラムと OS のローダーが実行間で変更されず、ローダーがコメントごとのロードアドレス)。実行可能ファイルを仮想アドレス空間にロードしない他のプラットフォームでは、他のプログラムが実行されたかどうか、および/または実行間で終了したかどうかによって、同じアドレスを取得する場合と取得しない場合があります。

Q2:

それを頼りにしないでください。何も変わらない場合は、確定的な動作 (同じアドレス) になります。しかし、変更できることは非常に多くあります (これもプラットフォームによって異なります)。

Q3:

仮想アドレスを割り当てないプラットフォームではいつでも変更できます (他のプロセスが作業を開始/継続/終了するため)。仮想アドレスを割り当てるプラットフォームでは、プログラムまたは関連するライブラリがまったく変更された場合、ローダーの動作を変更する OS パッチがある場合、またはおそらく現時点では考えていない他の状況が原因で、それらのアドレスが変更される可能性があります。 .

結論

アドレスを保存することは、非常に特殊なケースではうまくいくかもしれませんが、それは脆弱な解決策です。

于 2012-07-30T14:49:58.383 に答える
1

何も保証されません。

解決策は、アドレスではなく関数を使用してインデックスを作成することです(C99 標準が__func__識別子を提供します)。そうすれば、OS、コンパイラ、オプション、および月の満ち欠けのすべての変更にわたって、インデックスが同じままであることが保証されます。もちろん、関数名をリファクタリングするまでは:-)

于 2012-07-30T14:55:17.967 に答える
0

Linux を使用dladdr()しているため、メモリ内の場所の近くにあるシンボルについて尋ねることができます。例えば:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>

void foo() {
}

int main() {
  Dl_info info;
  void *test = foo; // Note: not standard C
  dladdr(test, &info);
  printf("closest symbol: %s in %s\n", info.dli_sname, info.dli_fname);
  return 0;
}

以下でコンパイルした場合:

gcc -Wall -Wextra test.c -ldl -rdynamic

void*as を正しく識別しfooます。これは、ロードされる場所に関係なく正しくなりますfoo

于 2012-07-30T15:16:09.480 に答える