web-dev-qa-db-ja.com

GCCを使用したC / C ++:リソースファイルを静的に実行可能ファイル/ライブラリに追加する

GCCを使用して、リソースファイルを実行可能ファイルまたは共有ライブラリファイルに静的にコンパイルする方法を知っている人はいますか?

たとえば、決して変更しない画像ファイルを追加したい(変更した場合は、とにかくファイルを置き換える必要があります)ため、ファイルシステム内に配置したくありません。

これが可能な場合(そして、Visual C++ for Windowsもこれができるためだと思います)、独自のバイナリに格納されているファイルをロードするにはどうすればよいですか?実行可能ファイルはそれ自体を解析し、ファイルを見つけて、そこからデータを抽出しますか?

まだ見たことのないGCCのオプションがあるかもしれません。検索エンジンを使用しても、実際には適切なものが吐き出されませんでした。

これは、共有ライブラリと通常のELF実行可能ファイルで機能するために必要です。

どんな助けでも大歓迎です

87
Atmocreations

imagemagick の場合:

convert file.png data.h

次のようなものを与えます:

/*
  data.h (PNM).
*/
static unsigned char
  MagickImage[] =
  {
    0x50, 0x36, 0x0A, 0x23, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 
    0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4D, 0x50, 0x0A, 0x32, 0x37, 
    0x37, 0x20, 0x31, 0x36, 0x32, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0xFF, 0xFF, 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 

....

他のコードとの互換性のために、fmemopenを使用して「通常の」FILE *オブジェクト、またはstd::stringstreamiostreamを作成します。 std::stringstreamはこれには適していません。もちろん、イテレータを使用できる場所ならどこでもポインタを使用できます。

これをautomakeで使用している場合は、 set BUILT_SOURCES を適切に忘れないでください。

この方法で行うことの良い点は次のとおりです。

  1. テキストを出力するので、バージョン管理やパッチを適切に適用できます
  2. 移植性があり、すべてのプラットフォームで明確に定義されています
46
Flexo

Updateコントロールを好むようになりました John Ripley's Assembly .incbin based solution オファーを提供し、現在ではバリアントを使用していますそれ。

Objcopy(GNU binutils)を使用して、ファイルfoo-data.binのバイナリデータを実行可能ファイルのデータセクションにリンクしました。

objcopy -B i386 -I binary -O elf32-i386 foo-data.bin foo-data.o

これにより、foo-data.oオブジェクトファイルが作成され、実行可能ファイルにリンクできます。 Cインターフェイスは次のようになります

/** created from binary via objcopy */
extern uint8_t foo_data[]      asm("_binary_foo_data_bin_start");
extern uint8_t foo_data_size[] asm("_binary_foo_data_bin_size");
extern uint8_t foo_data_end[]  asm("_binary_foo_data_bin_end");

のようなものを行うことができます

for (uint8_t *byte=foo_data; byte<foo_data_end; ++byte) {
    transmit_single_byte(*byte);
}

または

size_t foo_size = (size_t)((void *)foo_data_size);
void  *foo_copy = malloc(foo_size);
assert(foo_copy);
memcpy(foo_copy, foo_data, foo_size);

ターゲットアーキテクチャに定数データと変数データの保存場所に関する特別な制約がある場合、またはそのデータを.textセグメントに保存してプログラムコードと同じメモリタイプに収めたい場合は、 objcopyパラメーターをもう少し。

84
ndim

ldリンカーを使用して、実行可能ファイルにバイナリファイルを埋め込むことができます。たとえば、ファイルfoo.barがある場合、次のコマンドをldに追加して実行可能ファイルに埋め込むことができます

--format=binary foo.bar --format=default

ldからgccを呼び出す場合は、-Wlを追加する必要があります

-Wl,--format=binary -Wl,foo.bar -Wl,--format=default

ここで、--format=binaryは、次のファイルがバイナリであり、--format=defaultがデフォルトの入力形式に戻ることをリンカーに伝えます(foo.barの後に他の入力ファイルを指定する場合に便利です)。

次に、コードからファイルのコンテンツにアクセスできます。

extern uint8_t data[]     asm("_binary_foo_bar_start");
extern uint8_t data_end[] asm("_binary_foo_bar_end");

"_binary_foo_bar_size"という名前のシンボルもあります。タイプはuintptr_tであると思いますが、チェックしませんでした。

47
Simon

すべてのリソースをZipファイルに入れて、実行可能ファイルの最後に追加

g++ foo.c -o foo0
Zip -r resources.Zip resources/
cat foo0 resources.Zip >foo

これは機能します。a)ほとんどの実行可能な画像形式は、画像の背後に余分なデータがあるかどうかを気にせず、b)Zipはファイルの署名をZipファイルの終わりに保存するからです。つまり、実行可能ファイルはこの後の通常のZipファイル(Zipで処理できるupfront実行可能ファイルを除く)であり、libzipで開いて読み取ることができます。

39

正確なシンボル名とリソースの配置を制御する場合は、GNUアセンブラー(実際にはgccの一部ではない)を使用して(またはスクリプト)バイナリファイル全体をインポートできます。これを試してください。

アセンブリ(x86/arm):

    .section .rodata
    .global thing
    .type   thing, @object
    .align  4
thing:
    .incbin "meh.bin"
thing_end:
    .global thing_size
    .type   thing_size, @object
    .align  4
thing_size:
    .int    thing_end - thing

C:

#include <stdio.h>
extern char thing[];
extern unsigned thing_size;
int main() {
  printf("%p %u\n", thing, thing_size);
  return 0;
}

使用するものが何であれ、すべてのリソースを生成するスクリプトを作成し、すべてにニース/統一シンボル名を付けるのがおそらく最善です。

35
John Ripley

から http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967

最近、ファイルを実行可能ファイルに埋め込む必要がありました。私はコマンドラインでgccなどを使って作業しており、魔法のようにすべてを実現する派手なRADツールではなく、これを実現する方法はすぐにはわかりませんでした。ネット上で少し検索したところ、本質的に実行可能ファイルの最後に侵入し、知りたくない情報の束に基づいてそれを解読するハックが見つかりました。 ...

そして、救助のためのオブジェクトコピーです。 objcopyは、オブジェクトファイルまたは実行可能ファイルをある形式から別の形式に変換します。理解できる形式の1つは「バイナリ」です。これは基本的に、理解できる他の形式のいずれにも属さないファイルです。おそらく、アイデアを思い描いたでしょう:埋め込みたいファイルをオブジェクトファイルに変換すれば、残りのコードと簡単にリンクできます。

実行可能ファイルに埋め込むファイル名data.txtがあるとします。

# cat data.txt
Hello world

これをプログラムにリンクできるオブジェクトファイルに変換するには、objcopyを使用して ".o"ファイルを生成します。

# objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 data.txt data.o

これは、入力ファイルが「バイナリ」形式であり、出力ファイルが「elf32-i386」形式(x86上のオブジェクトファイル)であることをobjcopyに伝えます。 --binary-architectureオプションは、出力ファイルがx86で「実行」されることを意図していることをobjcopyに伝えます。これは、ldがx86の他のファイルとリンクするためのファイルを受け入れるために必要です。出力形式を「elf32-i386」と指定すると、これが暗黙に示されますが、そうではありません。

オブジェクトファイルができたので、リンカを実行するときに含めるだけで済みます。

# gcc main.c data.o

結果を実行すると、出力のために祈られます:

# ./a.out
Hello world

もちろん、私はまだ全体の話をしておらず、main.cも示していません。 objcopyが上記の変換を行うと、変換されたオブジェクトファイルに「リンカー」シンボルが追加されます。

_binary_data_txt_start
_binary_data_txt_end

リンク後、これらのシンボルは埋め込みファイルの開始と終了を指定します。シンボル名は、binaryを先頭に追加し、ファイル名に_startまたは_endを追加することで形成されます。ファイル名にシンボル名で無効になる文字が含まれている場合、それらはアンダースコアに変換されます(たとえば、data.txtはdata_txtになります)。これらのシンボルを使用してリンクするときに未解決の名前が表示される場合は、オブジェクトファイルでhexdump -Cを実行し、objcopyが選択した名前のダンプの終わりを確認します。

埋め込まれたファイルを実際に使用するためのコードは、今やかなり明白なはずです:

#include <stdio.h>

extern char _binary_data_txt_start;
extern char _binary_data_txt_end;

main()
{
    char*  p = &_binary_data_txt_start;

    while ( p != &_binary_data_txt_end ) putchar(*p++);
}

重要な微妙な注意点の1つは、オブジェクトファイルに追加されるシンボルは「変数」ではないということです。データは含まれず、アドレスが値です。この例に便利なため、これらをchar型として宣言します。埋め込みデータは文字データです。ただし、データが整数の配列の場合はintとして、データがfooバーの配列の場合はstruct foo_bar_tとして、それらを何でも宣言できます。埋め込まれたデータが均一でない場合、おそらくcharが最も便利です。アドレスを取得し、データを走査するときに適切な型にポインターをキャストします。

35
Hazok

こことインターネットですべての投稿を読んで、リソース用のツールはないという結論を下しました。

1)コードで使いやすい。

2)自動化(cmake/makeに簡単に含めるため)。

3)クロスプラットフォーム。

ツールを自分で書くことにしました。コードはこちらから入手できます。 https://github.com/orex/cpp_rsc

Cmakeで使用するのは非常に簡単です。

このようなコードをCMakeLists.txtファイルに追加する必要があります。

file(DOWNLOAD https://raw.github.com/orex/cpp_rsc/master/cmake/modules/cpp_resource.cmake ${CMAKE_BINARY_DIR}/cmake/modules/cpp_resource.cmake) 

set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/cmake/modules)

include(cpp_resource)

find_resource_compiler()
add_resource(pt_rsc) #Add target pt_rsc
link_resource_file(pt_rsc FILE <file_name1> VARIABLE <variable_name1> [TEXT]) #Adds resource files
link_resource_file(pt_rsc FILE <file_name2> VARIABLE <variable_name2> [TEXT])

...

#Get file to link and "resource.h" folder
#Unfortunately it is not possible with CMake add custom target in add_executable files list.
get_property(RSC_CPP_FILE TARGET pt_rsc PROPERTY _AR_SRC_FILE)
get_property(RSC_H_DIR TARGET pt_rsc PROPERTY _AR_H_DIR)

add_executable(<your_executable> <your_source_files> ${RSC_CPP_FILE})

アプローチを使用した実際の例は、ここからダウンロードできます https://bitbucket.org/orex/periodic_table

4
user2794512