2016-03-29 17 views
2

短いバージョン: 実行時にすべてのELF "セクション"ヘッダーを読み込み、ロードされた共有ライブラリごとにすべての "セクション"ヘッダーの再配置アドレスを取得することができます。Linux実行時のELFセクションの "反復"

ロングバージョン: 私は、カーネル(dyn_debug)に存在するのと同じユーザスペースでの動的デバッグのための同じメカニズムを実装しようとしています。すべてのLOGマクロインスタンスがプログラム内の特定の "セクション"に静的変数を作成する方法 __attribute__((section("__verbose"))) これは、変数を ".data"セクションではなく "__verbose"セクションに置くよう強制します。このセクションの後の開始アドレスと終了アドレスは、変数 __start___verbose、__stop___verboseを通じてアクセスできます。このようにして、いくつかの中央ルーチンは、登録されたすべての「ログ」エントリを調べ、必要に応じて属性を変更することができます。 これは静的にリンクされた実行可能ファイルを検索しますが、共有ライブラリを使用する場合は、いくつかの "__verbose"セクション(各共有ライブラリごとに1つ)と実行可能ファイル自体に1つあります。 (私はライブラリーに含めるためにもちろん-fPICフラグを使用しています) また、すべてのシンボルが確実にエクスポートされるように、 "-export-dynamic"にリンクされたすべてのものがエクスポートされます。

各共有ライブラリおよびメイン実行可能ファイルには、 属性(コンストラクタ)のinitメソッドがあります。

2つの異なる行動を観察しました。

ケース1:最初のケースライブラリはldopenではなく__start___verboseは常に同じアドレス(メインの実行のこと)、唯一の「メイン」の実行ログを返す参照「libcのローダー」

  1. によってロードされていないためにエントリが存在します。

  2. dlsym(RTLD_NEXT、__start___verbose)は、 "次の"解決可能なライブラリのシンボルのアドレスを返します。したがって、実際にはすべてのアドレスを取得します。

ケース2:ldopen

  1. __start___verboseを参照すると読み込みライブラリは、常に同じアドレス(メインの実行の)
  2. dlsymを(RTLD_NEXT、__start___verbose)戻りNULLを返します。
  3. dlsym(RTLD_DEFAULT、__start___verbose)は、 "main"プロセステーブルを返します。
  4. dlsymを(__start___verbose、ハンドル) - 戻り正しいセクションアドレス

質問:ライブラリのための任意の方法は、4が「ローダー」から明示的な呼び出しを必要とするため、4以外にも、そのシンボルを得るためにldopenとそこに開かれ

コード:

/* Main" */ 
void func1() 
{ 
    static int attribute__((section("__verbose"))) var = 1; 
} 

/* Shared library */ 
void func2() 
{ 
    static int attribute__((section("__verbose"))) var = 2; 
} 

/* Both in main and shared library 
* Prints same address !!! BAD !! */ 
void __attribute__((constructor)) initializer() 
{ 
    struct int *iter; 

    for (iter = __start___verbose; iter != __stop___verbose; ++iter) { 
     printf("Value is %d", *iter) 
    } 
} 



/* Works for libraries opened by libc runtime. 
* Does not work for libraries opened with LDOPEN*/ 
void __attribute__((constructor)) initializer() 
{ 
    struct int *iter = ; 

    for (iter = dlsym(RTLD_NEXT, "__start___verbose"); iter != __stop___verbose; ++iter) { 
     printf("Value is %d", *iter) 
    } 
} 



/* Snippet for main doing dynamic loading */ 

handle = ldopen('path', RTLD_NOW) 
iter = dlsym(handle, "__start___verbose") 
for (; iter != __stop___verbose; ++iter) { 
    printf("Value is %d", *iter) 
} 
+0

私はそれらを持っていないので、あなたは '__start ___ verbose'と' __stop ___ verbose'変数を持っている何か特別なことをしますか。 – ysdx

+0

ELFファイルの(優先度の高い)コンストラクタで変数を初期化したくないのですか?それ以外の場合は、コンストラクタのコードを実行するときに初期化されません。 – ysdx

答えて

1

は、あなたは、実行時にセクションの情報にアクセスすることを想定されていません。セクションは実行時に使用されることを想定しておらず、実行可能ファイルからリムーバブル(ストリップ)する予定です。

私はカスタムリンカスクリプトを使用する可能性がありますしたい:

.__verbose: 
{ 
    PROVIDE_HIDDEN (__verbose_start = .); 
    *(.__verbose) 
    PROVIDE_HIDDEN (__verbose_end = .); 
} 

これには、各ELFファイルは、それらのシンボルの独自のバージョンを持っていますセクションのHIDDENシンボルを定義します。 ELFファイル内

コンストラクタ(またはいくつかの他のコード)は、それらを使用することができます。

struct foo*; 
extern struct foo* __verbose_start __attribute__((visibility("hidden"))); 
extern struct foo* __verbose_stop __attribute__((visibility("hidden"))); 

void __attribute__((constructor)) initializer() 
{ 
    initialize_logging(__verbose_start,__verbose_stop); 
}