web-dev-qa-db-ja.com

Linuxでライブラリをロードするために使用されるシステムコールは何ですか?

strace出力では、実行可能ファイルが呼び出すライブラリへのパスはopen()への呼び出しにあります。これは、動的にリンクされる実行可能ファイルによって使用されるシステムコールですか? dlopen()はどうですか? open()は、プログラムの実行に役割を果たすと私が推測した呼び出しではありません。

24
Melab

dlopenはシステムコールではなく、 libdl library のライブラリ関数です。 straceにはシステムコールのみが表示されます。

Linuxおよびその他の多くのプラットフォーム(特に、実行可能ファイルにELF形式を使用するプラットフォーム)では、dlopenは、open()でターゲットライブラリを開き、mmap()mmap()は、ここでは本当に重要な部分です。これは、ライブラリをプロセスのアドレス空間に組み込んで、CPUがそのコードを実行できるようにするものです。ただし、ファイルをopen()する前に、ファイルをmmap()する必要があります。

34
Celada

dlopenは、あなたが考えるように、共有ライブラリとは何の関係もありません。共有オブジェクトをロードするには、2つの方法があります。

  1. コンパイル時リンカー(ldですが、通常はコンパイラーから呼び出されます)に、特定の共有ライブラリーの関数を使用するように指示します。このアプローチでは、コンパイル時リンカーが実行されるときにライブラリの名前が何であるかを知る必要がありますが、ライブラリの関数をプログラムに静的にリンクされているかのように呼び出すことができます。アプリケーションが実行されると、動的な実行時リンカー(ld.so)がmain関数が呼び出される直前に呼び出され、アプリケーションが次の関数を見つけるようにアプリケーションのプロセス空間を設定します図書館。これには、ライブラリをopen() ingし、次にmmap() ingしてから、いくつかのルックアップテーブルを設定します。
  2. libdlとリンクすることをコンパイル時リンカーに通知し、そこから(最初のメソッドを使用して)dlopen()およびdlsym()関数を呼び出すことができます。 dlopenを使用すると、ライブラリへのハンドルを取得できます。このハンドルをdlsymで使用して、特定の関数への関数ポインターを受け取ることができます。この方法は、最初の方法よりもプログラマーにとってはるかに複雑であり(リンカーに自動的に設定させるのではなく、手動で設定する必要があるため)、また、より脆弱です(コンパイルできないため) -timeは、最初のメソッドで取得するように、正しい引数の型で関数を呼び出していることを確認します)が、利点は、実行時にロードする共有オブジェクトを決定できる(またはそれをロードするかどうかを決定できる)ことです。これは、プラグインタイプの機能用のインターフェイスです。最後に、dlopenインターフェースは、そのメカニズムが動的リンカーの正確な実装に依存するため、他の方法よりも移植性が低くなります(したがって、これらの違いを抽象化しようとするlibtoolのlibltdl)。
6
Wouter Verhelst

今日、ほとんどのオペレーティングシステムは、1987年後半にSunOS-4.0によって導入された共有ライブラリの方法を使用しています。このメソッドは、mmap()によるメモリのマッピングに基づいています。

1990年代の初めに、Sunは古いa.outベースのコード(当時のSolarisはすでにELFベース)をFreeBSDの人々に寄付し、このコードは後で他の多くのシステム(Linuxを含む)に引き渡されたという事実を踏まえて、プラットフォーム間で大きな違いがない理由を理解できます。

4
schily

ltrace -S最小限の例の分析は、mmapがglibc 2.23で使用されていることを示しています

Glibc 2.23、Ubuntu 16.04では、dlopenを使用する最小限のプログラムでlatrace -Sを実行します。

ltrace -S ./dlopen.out

ショー:

dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "\177ELF\002\001\001", 832)                              = 832
SYS_brk(0)                                                           = 0x244c000
SYS_brk(0x246d000)                                                   = 0x246d000
SYS_fstat(3, 0x7fff42f9ce30)                                         = 0
SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128)                   = 54
SYS_mmap(0, 0x201028, 5, 2050)                                       = 0x7f1c323fe000
SYS_mprotect(0x7f1c323ff000, 2093056, 0)                             = 0
SYS_mmap(0x7f1c325fe000, 8192, 3, 2066)                              = 0x7f1c325fe000
SYS_close(3)                                                         = 0
SYS_mprotect(0x7f1c325fe000, 4096, 1)                                = 0

したがって、dlopenopen + mmapを呼び出すことがすぐにわかります。

素晴らしいltraceツールは、ライブラリコールとシステムコールの両方をトレースするため、この場合に何が起こっているかを調べるのに最適です。

より詳細な分析は、openがファイル記述子3を返すことを示しています(stdin、out、およびerrの後に次に解放されます)。

readはそのファイル記述子を使用しますが、- TODO理由mmapの引数が4つに制限されており、5番目の引数なので、どのfdがそこで使用されたかがわかりません =。 straceは、予想どおり3が1であることを確認し、ユニバースの順序が復元されます。

勇敢な魂もglibcコードに挑戦できますが、迅速なgrepの後でmmapを見つけることができず、怠惰です。

これでテスト GitHubでのボイラープレートのビルドの最小例

straceは、システムコール(つまり、カーネルによって直接実装された関数)について報告します。動的ライブラリはカーネル関数ではありません。 dlopenはCライブラリの一部であり、カーネルではありません。 dlopenの実装は、open(システムコール)を呼び出してライブラリファイルを開き、読み取り可能にします。

2
cjm