一部の共有ライブラリは、実行可能ファイルであるかのように実行可能である理由と方法


55

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。それを実行すると、セグメンテーション違反が発生しました。:-/


次のすべての答えに加えて、共有ライブラリにxビットを設定すると、rビットがクリアされていても実行可能ファイルからロードできる可能性があります(まだ可能です)。システムの実行可能ファイルとライブラリの世界からrビットを追放することは、かつては優れたセキュリティ対策と考えられていました。オープンソースが広く普及しているため、これは実際には匿名ftpディレクトリの/ bin / lsにのみ適用されます。私にとって、xビットを設定したままにしておくことは、その古い慣習の姿のように見えます。
ジョシュア

回答:


52

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

ここだ1つの提案、それは私のために動作しませんが、これを行う方法については。

SOに関する同様の質問に対するの回答を次に示します。これについては、恥知らずに盗用し、微調整し、少し説明を加えます。

まず、サンプルライブラリのソース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.outのリンクを示しlibtest.soます。

私は、これはglibcのは、実際に、それはおそらく、glibcの自身のようにポータブルではないので(参照、コンパイルされている方法です疑うことに注意man gccに関して-fPICおよび-pieスイッチ)が、それは基本的なメカニズムを示しています。実際の詳細については、ソースmakefileを確認する必要があります。


1
すばらしい答え、ありがとう!:-) nm共有ライブラリで使用しようとしましたが、デバッグバージョンではありませんでした。だから、なぜlibvlc他の人がクラッシュしますか?
Ho1

1
ほとんどの共有ライブラリは実行可能であることを意図していないため、GNU libcは例外です。
goldilocks

他に2つ見つけました:ldlibpthread
Ho1

@ Ho1 ld.soは他の点で特別です。ある程度までは、通常の動的にリンクされた実行可能ファイルよりも実際の実行可能ファイルに近いものです。
ランダム832

1
上記のオプションは、executable-shared-libraryを作成しますが、一部の実行可能ファイルがこれにリンクしようとしたときにエラーにフラグを立てるという意味では不完全です。詳細なサンプルリファレンスがここに追加されました:unix.stackexchange.com/a/479334/152034
parasrish

21

githubのランダムglibcリポジトリで回答を探しましょう。このバージョンはfileに「バナー」を提供します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;

(現在のドキュメントはこちらにあります

実際にldリンカは、コマンドラインオプション-e(最も実用的なソリューション)を提供する場合start、関数symbolを提供する場合、またはアセンブラにシンボルアドレスを挿入する場合、エントリポイント関数を含む実行可能ファイルを作成します。

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


1
Pythonの場合、単体テストを提供します。
エリックアロネスティ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.