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