4

私は今日この小さなプログラムを書きました、そして私はその結果に驚かされました。これがプログラムです


int main(int argc, char **argv)
{
 int a;
 printf("\n\tMain is located at: %p and the variable a is located at address: %p",main,&a);
 return 0;
}

私のマシンでは、メイン関数は常にアドレス「0x80483d4」にロードされ、変数のアドレスは変化し続けます。これはどのように発生しますか?私は、仮想化スキームの一部として、OSが命令のアドレスを再配置し続けるオペレーティングシステムを読みました。では、なぜこのプログラムを実行するたびに、メインが同じアドレスにロードされるのですか?

よろしくお願いします。

4

2 に答える 2

8

LinuxなどのELFシステムでは、通常の実行可能ファイル(ELFタイプET_EXEC)のセグメントがロードされるアドレスは、コンパイル時に固定されます。ET_DYNライブラリなどの共有オブジェクト(ELFタイプ)は、位置に依存しないように構築されており、それらのセグメントはアドレス空間のどこにでもロードできます(一部のアーキテクチャにはいくつかの制限がある可能性があります)。実際のように実行可能ファイルを作成することは可能です。ET_DYNこれらは「位置に依存しない実行可能ファイル」(PIE)として知られていますが、一般的な手法ではありません。

あなたが見ているのは、あなたのmain()関数がコンパイルされた実行可能ファイルの固定アドレステキストセグメントにあるという事実です。また、ライブラリ関数のアドレスを印刷してみてください。たとえば、システムがアドレス空間printf()配置dlsym()のランダム化(ASLR)をサポートし、有効にしている場合は、その関数のアドレスがプログラムの実行ごとに変化することを確認してください。 。(参照をコードに直接入れてライブラリ関数のアドレスを出力するだけの場合、実際に取得できるのは、実行可能ファイルの固定アドレスに静的にコンパイルされる関数のプロシージャルックアップテーブル(PLT)トランポリンのアドレスです。 。)

表示される変数は、静的に割り当てられたメモリではなく、スタック上に作成された自動変数であるため、実行ごとにアドレスが変更されます。OSとバージョンによっては、ASLRがなくても、スタックのベースのアドレスが実行から実行にシフトする場合があります。変数宣言を関数の外部でグローバルに移動すると、関数と同じように動作することがわかりますmain()

これが完全な例です-次のようなものでコンパイルしますgcc -o example example.c -dl

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

int a = 0;

int main(int argc, char **argv)
{
    int b = 0;
    void *handle = dlopen(NULL, RTLD_LAZY);
    printf("&main: %p; &a: %p\n", &main, &a);
    printf("&printf: %p; &b: %p\n", dlsym(handle, "printf"), &b);
    return 0;
}
于 2010-09-13T12:40:11.877 に答える
0

main(...)オペレーティング システムが毎回ロードして実行するランタイム スタートアップ ライブラリ コードです。コンパイラに応じて、これを行うためのコードが含まれている CRT (C ランタイム ライブラリ) を参照してください。

もう 1 つ心に留めておくべきことは、そのアドレスです。C コードが機能する限り、あまり気にする必要はありません。これは偶然のパターンであり、OS の負荷、使用するドライバー、ハードウェア、ウイルス対策ソフトウェアなど、さまざまな要因に依存します。

また、コードに関連して、静的変数、関数、ポインターを追加すると、バイナリ コードのレイアウトが変更され、さらに重要ことに、実行時に読み込まれるシンボルのアドレスが異なります。

于 2010-09-13T11:19:03.317 に答える