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


94

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

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

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

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

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

どんな助けでもありがたいです



blueberryfieldsが指摘した質問のobjcopyリンクも、これに対する優れた一般的なソリューションです
Flexo

@blueberryfields:重複して申し訳ありません。あなたが正しい。通常、私は近いものを重複として投票します。しかし、彼ら全員がとても良い答えを投稿したので、私はただ一つを受け入れます。
Atmocreations

John Ripleyの方法は、1つの大きな理由-アラインメントのために、おそらくここで最高のものであることを付け加えることができます。標準のobjcopyまたは「ld -r -b binary -o foo.o foo.txt」を実行し、objdump -xを使用して結果のオブジェクトを確認すると、ブロックの配置が0に設定されているように見えます。 char以外のバイナリー・データの場合は、アラインメントが正しいことがわかりますが、これが良いことだとは思えません。
12

回答:


49

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::stringstreamを作成するために使用できiostreamます。std::stringstreamただし、これには適していません。もちろん、イテレータを使用できる場所であればどこでもポインタを使用できます。

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

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

  1. あなたはテキストを出力するので、バージョン管理やパッチに慎重に取り組むことができます
  2. 移植性があり、すべてのプラットフォームで明確に定義されています

2
やった!それも私が考えた解決策です。誰もがこれをしたいと思う理由は私を超えています。明確に定義された名前空間にデータを保存することは、ファイルシステムの目的です。
全知

35
場合によっては、ファイルシステムがない場合やオペレーティングシステムがない場合でも実行可能な実行可能ファイルがあります。または、アルゴリズムでルックアップ用に事前計算されたテーブルが必要です。そして、プログラムにデータを保存することが非常に理にかなっている場合、もっと多くのケースがあると私は確信しています。
ndim

15
変換の使用は全く同じであるxxd -i infile.bin outfile.h
greyfade

5
このアプローチの欠点の1つは、イメージが特に大きい場合、一部のコンパイラーがそのような巨大な静的配列を処理できないことです。これを回避する方法は、ndimが示唆するobjcopyように、バイナリデータをオブジェクトファイルに直接変換するために使用することです。ただし、これが問題になることはほとんどありません。
Adam Rosenfield、2011

3
このようにヘッダーで定義することは、それを含む各ファイルが独自のコピーを取得することを意味することに注意してください。ヘッダーでexternとして宣言し、cppで定義することをお勧めします。 ここの例
Nicholas Smith

90

更新私は、John Ripleyのアセンブリ.incbinベースのソリューションが提供するコントロールを優先するようになり、今ではそのバリアントを使用しています。

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パラメーターをさらに使用することができます。


良いアイデア!私の場合、それはあまり役に立ちません。しかし、これは実際にスニペットコレクションに入れるものです。これを共有してくれてありがとう!
Atmocreations

2
ld出力形式がそこに含まれているため、使用は少し簡単です。stackoverflow.com/ a / 4158997/201725を参照してください。
Jan Hudec、2014年

52

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だと思いますがチェックしませんでした。


非常に興味深いコメント。これを共有してくれてありがとう!
12

1
良いですね!ただ1つの質問:なぜdata_endポインターではなく配列なのですか?(またはこれは慣用的なCですか?)
xtofl

2
@xtofl、data_endポインタの場合、コンパイラはファイルの内容の後にポインタが格納されていると見なします。同様に、タイプをdataポインタに変更すると、ファイルの先頭へのポインタではなく、ファイルの最初のバイトで構成されるポインタが表示されます。私はそう思う。
Simon、

1
+1:あなたの答えにより、JavaクラスローダーとJarをexeに埋め込み、カスタムJavaランチャーを構築できます
Aubin

2
@xtofl-ポインタにする場合は、にしconst pointerます。コンパイラーでは非constポインターの値を変更できますが、配列の場合は値を変更できません。そのため、配列構文を使用する方が、タイピングが少なくなります。
ジェシーチザム2015

40

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

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

これが機能するのは、a)ほとんどの実行可能なイメージ形式は、イメージの背後に余分なデータがあるかどうかに関係なく、b)zip は、zipファイルの最後にファイルの署名を保存するためです。つまり、実行可能ファイルはこの後の通常のzipファイル(zipで処理できる事前実行可能実行可能ファイルを除く)であり、libzipで開いて読み取ることができます。


7
foo0とresources.zipをfooに結合したい場合、catのコマンドラインで両方の入力を指定するには>が必要です。(私はすでにfooにあるものに追加したくないので)
Nordic Mainframe

1
ああ、そうです。最初の読み物で、名前の0を正しく見つけていませんでした
Flexo

これはとても賢いです。+1。
Linuxios、

1
+1素晴らしい、特にminiz
mvp

これにより、無効なバイナリが生成され(少なくともMacとLinuxでは)、などのツールでは処理できませんinstall_name_tool。それ以外にも、バイナリは実行可能ファイルとして機能します。
Andy Li

36

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

最近、実行可能ファイルにファイルを埋め込む必要がありました。私はgccなどを使用してコマンドラインで作業しているため、すべてを魔法のように実行する豪華なRADツールを使用していません。これをどのように実行するかはすぐにはわかりませんでした。ネットで少し検索したところ、基本的に実行可能ファイルの最後にそれを仕掛け、それが私が知りたくないたくさんの情報に基づいている場所を解読するハックが見つかりました。より良い方法があるはずのように見えた...

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

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

# cat data.txt
Hello world

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

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

これは、objcopyに入力ファイルが「バイナリ」形式であること、出力ファイルが「elf32-i386」形式(x86のオブジェクトファイル)であることを通知します。--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

リンク後、これらの記号は埋め込みファイルの開始と終了を指定します。シンボル名は、バイナリを前に付け、ファイル名に_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がおそらく最も便利です。データをトラバースするときに、そのアドレスを取得して、ポインタを適切な型にキャストします。


36

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

アセンブリ(x86 /アーム):

    .section .rodata

    .global thing
    .type   thing, @object
    .balign 4
thing:
    .incbin "meh.bin"
thing_end:

    .global thing_size
    .type   thing_size, @object
    .balign 4
thing_size:
    .int    thing_end - thing

C:

#include <stdio.h>

extern const char thing[];
extern const unsigned thing_size;

int main() {
  printf("%p %u\n", thing, thing_size);
  return 0;
}

何を使用する場合でも、すべてのリソースを生成するスクリプトを作成し、すべてに適切なシンボル名を付けるのがおそらく最善です。

データとシステムの仕様に応じて、異なる配置値(できれ.balignば移植性のため)、またはのサイズが異なる整数型thing_size、またはthing[]配列の要素タイプを使用する必要がある場合があります。


共有してくれてありがとう!間違いなく面白そうですが、今回は私が探しているものではありません=)よろしく
お願いします

1
まさに私が探していたもの。たぶん、4で表示されないサイズのファイルでも問題ないことを確認できます。thing_sizeには追加のパディングバイトが含まれているようです。
Pavel P

ローカルシンボルにしたい場合はどうなりますか?おそらくコンパイラの出力を自分のアセンブリと一緒に猫にすることができますが、もっと良い方法はありますか?
user877329 2014年

記録のために:私の編集は、@ Pavelが指摘した追加の埋め込みバイトの問題に対処しています。
ndim

4

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

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からダウンロードできます


あなたの答えは、より多くの人に役立つように、より良い説明が必要だと思います。
kyb 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.