8

外部依存関係がなく、const データ セクションのみの C ファイルがあるとします。このファイルをコンパイルして、別のプログラムにロードできるバイナリ BLOB を取得したいと思います。このプログラムでは、関数ポインタを介して関数が使用されます。

例を見てみましょう。これは架空のバイナリ モジュール f1.c です。

static const unsigned char mylut[256] = {
    [0 ... 127] = 0,
    [128 ... 255] = 1,
};

void f1(unsigned char * src, unsigned char * dst, int len)
{
    while(len) {
        *dst++ = mylut[*src++];
        len--;
    }
}

f1.o、次にf1.binにコンパイルし、prog.cでこのように使用したいと思います

int somefunc() {
    unsigned char  * codedata;
    f1_type_ptr  f1_ptr;
    /* open f1.bin, and read it into codedata */

    /* set function pointer to beginning of loaded data */
    f1_ptr =(f1_type_ptr)codedata;

    /* call !*/
    f1_ptr(src, dst, len);
}

f1.c から f1.o に移動するには、位置の独立性を得るために -fPIC が必要だと思います。f1.o から f1.bin に移行するために使用できるフラグまたはリンカー スクリプトは何ですか?

明確化:

動的リンクについて知っています。この場合、動的リンクはできません。リンクステップは、可能であれば、ロードされたデータへの func ポインターをキャストする必要があります。

OS のサポートはないと仮定してください。可能であれば、たとえばアセンブリに f1 を PC 関連のアドレス指定で記述します。

4

3 に答える 3

14

まず第一に、他の人が言ったように、DLLまたはSOの使用を検討する必要があります。

とはいえ、本当にこれを実行したい場合は、リンカースクリプトを置き換える必要があります。このようなもの(あまりよくテストされていませんが、うまくいくと思います):

ENTRY(_dummy_start)
SECTIONS
{
    _dummy_start = 0;
    _GLOBAL_OFFSET_TABLE_ = 0;
    .all : { 
        _all = .;
        LONG(f1 - _all);
        *( .text .text.* .data .data.* .rodata .rodata.* ) 
    }
}

次に、次のコマンドでコンパイルします。

$ gcc -c -fPIC test.c

とのリンク:

$ ld -T script.ld test.o -o test.elf

そして、バイナリブロブを次のように抽出します。

$ objcopy -j .all -O binary test.elf test.bin

おそらく、スクリプトの説明は大歓迎です。

  • ENTRY(_dummy_start)これにより、プログラムにエントリポイントがないという警告が回避されます。
  • _dummy_start = 0;これは、前の行で使用されている記号を定義します。値は使用されません。
  • _GLOBAL_OFFSET_TABLE_ = 0;これにより、別のリンカーエラーが防止されます。この記号は本当に必要ではないと思うので、0と定義できます。
  • .allこれは、BLOBのすべてのバイトを収集するセクションの名前です。このサンプルでは、​​すべてのセクションとセクションが一緒.textになります。あなたが複雑な機能を持っているなら、あなたはもう少し必要かもしれません、この場合あなたの友人です。.data.rodataobjdump -x test.o
  • LONG(f1 - _all)本当に必要ではありませんが、関数のブロブへのオフセットを知りたいですね。オフセット0になるとは限りません。この行を使用すると、blobの最初の4バイトがシンボルf1(関数)のオフセットになります。64ビットポインタを使用する場合はで変更LONGします。QUAD

更新:そして今、簡単な「n」ダーティテスト(それは動作します!):

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

typedef void (*f1_t)(char *a, char *b, int len);
f1_t f1;

int main()
{
    char *blob = (char*)valloc(4096);
    FILE *f = fopen("test.bin", "rb");
    fread(blob, 1, 4096, f);
    fclose(f);

    unsigned offs = *(unsigned*)blob;
    f1 = (f1_t)(blob + offs);
    mprotect(blob, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    char txt[] = "¡hello world!";
    char txt2[sizeof(txt)] = "";
    f1(txt, txt2, sizeof(txt) - 1);
    printf("%s\n%s\n", txt, txt2);
    return 0;

}
于 2012-08-27T09:13:11.797 に答える
2

共有ライブラリ ( Windows の場合は.dll、Linuxの場合は.so ) の構築を検討する必要があります。

次のようにライブラリをビルドします。

gcc -c -fPIC test.c
gcc -shared test.o -o libtest.so

ライブラリをコードから動的にロードする場合は、関数dlopen(3)およびdlsym(3)を参照してください。

または、コンパイル時にライブラリをリンクする場合は、プログラムをビルドします

gcc -c main.c
gcc main.o -o <binary name> -ltest

編集:

ここで何を言うかはよくわかりませんが、研究を進める手がかりになるかもしれません...

dlopendlsymを使用したくない場合は、関数のアドレスを見つけるために.oファイルからシンボル テーブルを読み取ってから、読み取りと実行の権限でメモリ内のオブジェクト ファイルをmmapすることができます。次に、見つけたアドレスでロードされたコードを実行できるはずです。ただし、このコードで遭遇する可能性のある他の依存関係には注意してください。

マンページを確認できますelf(5)

于 2012-08-27T08:22:55.427 に答える