web-dev-qa-db-ja.com

共有ライブラリが実行可能であるかのように、なぜ、どのように実行可能か。

32ビットLinuxシステムでは、これを呼び出す

$ /lib/libc.so.6

64ビットシステムでは

$ /lib/x86_64-linux-gnu/libc.so.6

シェルでは、次のような出力を提供します。

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

なぜ、どのようにしてこれが発生し、他の共有ライブラリで同じように実行できるのでしょうか。

私は見た /usr/lib実行可能ファイルを検索し、/usr/lib/libvlc.so.5.5.0。それを実行すると セグメンテーション違反 が発生しました。 :-/

57
Ho1

そのライブラリにはmain()関数または同等のエントリポイントがあり、実行可能ファイルと共有オブジェクトの両方として役立つようにコンパイルされています。

これは 1つの提案 これを行う方法についてですが、私にはうまくいきません。

これが S.Oでの同様の質問への回答 の別のものです。これを恥知らずに盗用し、微調整し、少し説明を追加します。

まず、サンプルライブラリtest.cのソース:

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

それをコンパイルします:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

ここでは、共有ライブラリ(-fPIC)をコンパイルしていますが、通常の実行可能ファイル(-pie)であることをリンカーに通知し、そのシンボルテーブルをエクスポート可能(-Wl,-E)にしています。それは便利にリンクすることができます。

そして、fileは共有オブジェクトであると言いますが、実行可能ファイルとして機能します。

> ./libtest.so 
./libtest.so: Hello!

ここで、動的にリンクできるかどうかを確認する必要があります。プログラム例、program.c

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

externを使用すると、ヘッダーを作成する必要がなくなります。それをコンパイルします:

gcc program.c -L. -ltest

実行する前に、ダイナミックローダーのlibtest.soのパスを追加する必要があります。

export LD_LIBRARY_PATH=./

今:

> ./a.out
Test program.
./a.out: Hello!

そしてldd a.outlibtest.soへのリンケージを表示します。

これはおそらくglibcが実際にコンパイルされる方法ではないことに注意してください。これはおそらくglibc自体ほど移植性がないためです(man gccおよび-fPICスイッチについては-pieを参照してください)。基本的なメカニズム。実際の詳細については、ソースのmakefileを確認する必要があります。

54
goldilocks

Githubのランダムなglibcリポジトリで答えを探しましょう。このバージョンは、ファイル _version.c_ に「バナー」を提供します。

同じファイルにいくつかの興味深い点があります:___libc_print_version_エントリポイントとしてドキュメント化されている同じテキストとシンボル__libc_main (void)への印刷を提供する関数。したがって、このシンボルはライブラリの実行時に呼び出されます。

それでは、リンカー/コンパイラは、これがエントリポイント関数であることをどのようにして知るのでしょうか。

makefile に飛び込みましょう。リンカーフラグには、興味深いフラグがあります。

_# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main
_

したがって、これはライブラリにエントリポイントを設定するためのリンカーフラグです。ライブラリを構築するとき、リンカー作成実行可能動作に_-e function_name_を指定できます。それは本当に何をしますか?見てみましょう マニュアルで(多少日付は付けられていますが、まだ有効です)

リンカコマンド言語には、最初の実行可能命令を出力ファイル(そのエントリポイント)に定義するためのコマンドが含まれています。その引数はシンボル名です:

ENTRY(記号)

シンボルの割り当てと同様に、ENTRYコマンドは、コマンドファイル内の独立したコマンドとして配置するか、SECTIONSコマンド内のセクション定義の間に配置することができます。

ENTRYは、エントリポイントを選択するいくつかの方法の1つにすぎません。次のいずれかの方法でそれを示すことができます(優先度の降順で表示されます。リストの上位にあるメソッドは、下位にあるメソッドをオーバーライドします)。

_the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 
_

たとえば、これらのルールを使用して、割り当てステートメントでエントリポイントを生成できます。シンボルスタートが入力ファイル内で定義されていない場合は、単純に定義して適切な値を割り当てることができます---

start = 0x2020;

この例では絶対アドレスを示していますが、任意の式を使用できます。たとえば、入力オブジェクトファイルがエントリポイントに他のシンボル名の規則を使用している場合、開始する開始アドレスが含まれているシンボルの値を割り当てるだけです。

start = other_symbol;

(現在のドキュメントは here で見つかります)

実際にldリンカーは、コマンドラインオプション_-e_(最も実用的な解決策)を指定するか、関数シンボルstartを提供するか、シンボルアドレスをアセンブラ。

ただし、他のリンカーとの動作が保証されていないことに注意してください(llvmのlldに同じフラグがあるかどうかはわかりません)。なぜこれがそのファイルに関する情報を提供する以外の目的で役立つはずなのか、私は知りません。

21
IBr