4

my_bin.c のような単純な C プログラムがあります。

#include <stdio.h>

int main()
{
    printf("Success!\n");
    return 0;
}

gcc でコンパイルし、実行可能ファイル my_bin を取得しました。

ここで、別の C プログラムを使用して main (またはこの my_bin を実行) を呼び出したいと考えています。次のようにmmapと関数ポインターを使用して行いました。

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
int main()
{
   void (*fun)();
   int fd;
   int *map;
   fd = open("./my_bin", O_RDONLY);
   map = mmap(0, 8378, PROT_READ, MAP_SHARED, fd, 0);
   fun = map;
   fun();
   return 0;
}

EDIT 1: PROT_EXEC を追加 応答からより明確にする ... 2 番目のプログラム内で外部バイナリ プログラムを呼び出したい。

メイン(他のプログラム)のアドレスで関数ポインタを初期化する方法がわかりません。何か案が?

編集2:

グーグルで調べた後、なぜセグフォルトが判明したのか、それは私のサイズとmmapのオフセット引数が原因です。ページサイズの倍数にする必要があります。【参考:C言語でmmapを使ってバイナリファイルを読み込んでいる時のセグメンテーション違反】

コードは次のようになります。

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
int main()
{
   void (*fun)();
   int fd;
   int *map;
   int offset = 8378;
   int pageoffset = offset % getpagesize();
   fd = open("./my_bin", O_RDONLY);
   if(fd == -1) {
        printf("Err opening file\n");
        return -1;
   }
   map = mmap(0, 8378 + pageoffset, PROT_READ|PROT_EXEC,
                        MAP_SHARED, fd, offset - pageoffset);
   perror("Err\n"); //This is printing err and Success!
   //fun = map; // If I uncomment this and 
   //fun();     // this line then, still it 
                // print err and Success! from perror
                // but later it says Illegal instruction. 
   return 0;
}

まだ fun() があるか、それがないと印刷されません... main() アドレスを指定する方法がわかりません。

編集2[解決済み]:

まず、定義を適切に読み取っていませんでした。バイナリ ファイルを読み取るアドレスを既に指定しています。2 番目: mmap: サイズとオフセットはページサイズの倍数である必要があります。

4

3 に答える 3

6

main()通常、C プログラムの最初の関数ではありません。リンカは、その前にいくつかのセットアップ/初期化コードを配置します。これは、とりわけ、環境をセットアップし、コマンドライン引数を取得し、それらを文字列配列に解析します。

新しいmain()関数がメモリ割り当てルーチンを設定し始めると、問題が発生します。基本的に、これにより、メイン アプリケーションの重要なデータ構造がすべて台無しになります。

関数を (つまり なしで) 実行したい場合はmain()、C コードを共有ライブラリにコンパイルし、それをdlopen()OS の同等のものでロードします。

本当に必要な場合は、 とmain()を使用fork()してexec()ください。

于 2012-09-13T15:40:15.110 に答える
1

一般に、コンパイルとリンクによって生成される実行可能ファイルは、メモリに読み込んで実行できる単純なバイナリ イメージではありません。通常、ローダーと呼ばれる特別なプログラムは、実行可能ファイルを読み取り、ファイルの内容と命令を使用してメモリを準備し、特別なシステム コールを使用してプログラムの実行を開始する必要があります。新しいプロセスとして。

たとえば、実行可能ファイルには通常、メモリにコピーしてから読み取り専用としてマークする必要があるデータ、メモリにコピーしてから読み取り可能および書き込み可能としてマークする必要があるデータ、および「テキスト」またはプログラム命令と呼ばれるデータが含まれます。メモリにコピーし、実行可能としてマークする必要があります。通常、実行可能ファイルには、メモリの準備に関するその他の情報も含まれています。たとえば、最初にクリアされたメモリの量、スタック スペース用に一定量の確保、実行を開始するアドレス (通常は ではありませんmain) などです。

複雑な側面の 1 つは、実行可能ファイルには、使用されているメモリ アドレスに応じて、プログラム テキストとデータのどの部分を変更する必要があるかに関する情報が含まれていることです。データとテキストがメモリに配置されるアドレスは、コンパイル時にはわからない可能性があるため、コンパイラはプロトタイプ コードを記述し、ローダーはアドレスを決定した後にコードを調整する必要があります。

もう 1 つの問題は、実行可能ファイルに動的ライブラリ内のシンボルへの参照が含まれている可能性があることです。ローダーは、これらの参照を調べて、必要な動的ライブラリをロードする必要があります。

ご覧のとおり、ロードは単純なプロセスではありません。実行可能ファイルのどこから始まるかがわかったとしても、単純に実行可能ファイルをメモリマップしてそこにジャンプすることはできませんmain

一部のシステムは、ローダーへのインターフェースを提供します。これらは通常、動的ライブラリで使用されます。スタンドアロンの実行可能プログラムを構築する代わりに、動的ライブラリを構築します。次に、動的ライブラリ インターフェイスへの呼び出しを使用してライブラリをロードし、ライブラリ内のルーチンのアドレスを見つけて、それらのルーチンを呼び出すことができます。

main一般に、プログラミング環境には、実行可能ファイルをロードしてそのルーチンを呼び出す便利な方法はありません。実行可能ファイルを作成する代わりに動的ライブラリを作成し、動的ライブラリをロードしてその中のルーチンを呼び出す方が簡単な場合があります。別の方法として、実行可能ファイルを別のプロセスとして呼び出す方法があります。これにより、呼び出しプロセスとメモリを共有することなく、コマンド ラインで入力されたコマンドのように、本質的に個別に実行されます。

于 2012-09-13T17:24:18.167 に答える
-1

ある実行可能ファイルの機能をリンクする場合の「正しい」解決策IMHOは、別の関数からmain()を呼び出さないことです。むしろ、main()が行うことをまとめて、両方のmain()から呼び出す関数を作成します。

int success()
{   
  printf("Success!\n");
  return 0;
}

int main()
{
  return success();
}

外部システムコールの呼び出しには、systemを使用します

于 2012-09-13T15:53:48.223 に答える