4

Linux プロセスが別のプロセスのメモリにあるコードを呼び出すことは可能でしょうか?

プロセス A に関数 f() があり、プロセス B にそれを呼び出させたいとしましょう。私が考えたのは、mmap を MAP_SHARED および PROT_EXEC フラグと共に使用して、関数コードを含むメモリをマップし、ポインタを B に渡すことです。f() は A バイナリから他の関数​​を呼び出さないと仮定します。それはうまくいきますか?はいの場合、メモリ内の f() のサイズを決定するにはどうすればよいですか?

=== 編集 ===

共有ライブラリがまさにそれを行うことは知っていますが、プロセス間でコードを動的に共有できるかどうかは疑問です。

4

4 に答える 4

5

はい、できますが、最初のプロセスはmmap、メモリ マップト ファイルまたは で作成された共有領域のいずれかを介して、最初に共有メモリを作成している必要がありますshm_open

コンパイルされたコードを共有している場合、共有ライブラリはそのために作成されました。それらに対して通常の方法でリンクすることができ、共有は自動的に行われます。またはdlopen、プラグインなどを使用して手動でロードすることもできます。


アップデート:

コードはコンパイラによって生成されているため、再配置について心配する必要があります。コンパイラは、どこでも動作するコードを生成しません。.dataセクションが特定の場所にあり、.bssセクションがゼロになっていることが期待されます。GOT を実装する必要があります。静的コンストラクターを呼び出す必要があります。

要するに、あなたが望むのはおそらくdlopen. このシステムを使用すると、ファイルのように共有ライブラリを開き、関数ポインタを名前で抽出できます。ライブラリを構成する各プログラムdlopenはコード セクションを共有するため、メモリを節約できますが、それぞれがデータ セクションの独自のコピーを持つため、互いに干渉することはありません。

ライブラリ コードをコンパイルする必要があることに注意してください。-fPICそうしないと、コードを共有できなくなります (実際、多くのアーキテクチャのリンカとダイナミック ローダーは、とにかく PIC ではないライブラリをサポートしていない可能性があります)。

于 2013-02-27T14:15:36.757 に答える
4

標準的なアプローチは、 のコードをf()共有ライブラリに配置することlibfoo.soです。次に、そのライブラリにリンクするか (たとえば、プログラムAを でビルド)、またはdlopen(3)を使用gcc -Wall a.c -lfoo -o a.binして動的にロードし (たとえば、プログラムBで)、次に を使用してのアドレスを取得します。fdlsym

共有ライブラリをコンパイルするときは、次のことを行います。

  • 各ソースファイルfoo1.c位置に依存しないコードgcc -Wall -fPIC -c foo1.c -o foo1.pic.oにコンパイルし、同様ににfoo2.cfoo2.pic.o
  • libfoo.soそれらすべてをwithにリンクしgcc -Wall -shared foo*.pic.o -o libfoo.soます。追加の共有ライブラリをリンクできることに注意してくださいlbfoo.so(たとえば-lm、リンク コマンドに追加することにより)。

Program Library Howtoも参照してください。

mmap他のものを -ingすることで非常識なトリックをプレイできますが  /proc/1234/mem、それはまったく合理的ではありません。共有ライブラリを使用します。

PS。dlopen大量 (数十万) の共有オブジェクトlib*.soファイルを作成できます。あなたはそれらをしたいかもしれませdlcloseん(しかし実際にはそうする必要はありません)。

于 2013-02-27T14:15:38.507 に答える
2

そうすることは可能ですが、それがまさに共有ライブラリの目的です。

また、共有メモリのアドレスが両方のプロセスで同じであることを確認する必要があることに注意してください。そうでない場合は、「絶対」参照 (つまり、共有コード内の何かへのポインター) です。また、共有ライブラリと同様に、コードのビット数は同じである必要があり、すべての共有メモリと同様に、共有ライブラリのいずれかを変更した場合、他のプロセスを「台無し」にしないようにする必要があります。メモリー。

関数のサイズを決定することは、生成された実際のコードと利用可能な情報のレベルに応じて、「難しい」から「ほとんど不可能」にまで及びます。デバッグ シンボルは関数のサイズになりますが、2 つの関数が同じ「戻り」コードを共有するコードをコンパイラが生成するのを見たことがあることに注意してください (つまり、コンパイラは、同じビットのコードを持つ別の関数へのジャンプを生成します)。結果を返すのは、数バイトのコードを節約するためであり、とにかくジャンプする予定だったからです [たとえば、コンパイラがジャンプしなければならない if/else があります])。

于 2013-02-27T14:38:41.770 に答える