如何将文件嵌入到可执行文件中?
我有两个问题,第一个问题已经解决了。
当前问题
如果我嵌入需要库加载的文件(例如 jpeg 图像或 mp3 音乐),我将需要使用该文件作为库的输入。然而,每个库都是不同的,并使用一种方式来获取文件作为输入,输入可能是文件名或 FILE* 指针(来自 libc 的文件接口)。
我想知道如何访问带有名称的嵌入文件。如果我创建一个临时文件,效率会很低,有其他办法吗?我可以将文件名映射到内存吗?我的平台是Windows和Linux。
如果 show_file(const char* name) 是库中的函数,我将需要一个字符串来打开该文件。
我看到过这些问题:
如何获取内存中缓冲区的文件描述符?
从 C 中的文件描述符获取文件名
,以下代码是我的解决方案。这是一个好的解决方案吗?效率低吗?
# include <stdio.h>
# include <unistd.h>
extern char _binary_data_txt_start;
extern const void* _binary_data_txt_size;
const size_t len = (size_t)&_binary_data_txt_size;
void show_file(const char* name){
FILE* file = fopen(name, "r");
if (file == NULL){
printf("Error (show_file): %s\n", name);
return;
}
while (true){
char ch = fgetc(file);
if (feof(file) )
break;
putchar( ch );
}
printf("\n");
fclose(file);
}
int main(){
int fpipe[2];
pipe(fpipe);
if( !fork() ){
for( int buffsize = len, done = 0; buffsize>done; ){
done += write( fpipe[1], &_binary_data_txt_start + done, buffsize-done );
}
_exit(0);
}
close(fpipe[1]);
char name[200];
sprintf(name, "/proc/self/fd/%d", fpipe[0] );
show_file(name);
close(fpipe[0]);
}
另一个问题(已解决)
我尝试使用 GCC 在 Linux 上嵌入一个文件,并且成功了。然而,我尝试在 Windows 上使用 Mingw 做同样的事情,但它没有编译。
代码是:
# include <stdio.h>
extern char _binary_data_txt_start;
extern char _binary_data_txt_end;
int main(){
for (char* my_file = &_binary_data_txt_start; my_file <= &_binary_data_txt_end; my_file++)
putchar(*my_file);
printf("\n");
}
编译命令是:
objcopy --input-target binary --output-target elf32-i386 --binary-architecture i386 data.txt data.o
g++ main.cpp data.o -o test.exe
在 Windows 上,我收到以下编译器错误:
undefined reference to `_binary_data_txt_start'
undefined reference to `_binary_data_txt_end'
我尝试将 elf32-i386 替换为 i386-pc-mingw32,但我仍然得到同样的错误。
I have two problems, the first has been solved.
Current problem
If I embed a file that requires a library to load it, such as a jpeg image or a mp3 music, I will need to use the file as input to the library. However, each library is different and uses a way to get a file as input, the input may be the file name or a FILE* pointer (from libc's file interface).
I would like to know how to access an embedded file with a name. It will be inefficient if I create a temporary file, is there another way? Can I map a file name to memory? My platforms are Windows and Linux.
If show_file(const char* name) is a function from a library, I will need a string to open the file.
I have seen these questions:
How to get file descriptor of buffer in memory?
Getting Filename from file descriptor in C
and the following code is my solution. Is it a good solution? Is it inefficient?
# include <stdio.h>
# include <unistd.h>
extern char _binary_data_txt_start;
extern const void* _binary_data_txt_size;
const size_t len = (size_t)&_binary_data_txt_size;
void show_file(const char* name){
FILE* file = fopen(name, "r");
if (file == NULL){
printf("Error (show_file): %s\n", name);
return;
}
while (true){
char ch = fgetc(file);
if (feof(file) )
break;
putchar( ch );
}
printf("\n");
fclose(file);
}
int main(){
int fpipe[2];
pipe(fpipe);
if( !fork() ){
for( int buffsize = len, done = 0; buffsize>done; ){
done += write( fpipe[1], &_binary_data_txt_start + done, buffsize-done );
}
_exit(0);
}
close(fpipe[1]);
char name[200];
sprintf(name, "/proc/self/fd/%d", fpipe[0] );
show_file(name);
close(fpipe[0]);
}
The other problem (solved)
I tried to embed a file on Linux, with GCC, and it worked. However, I tried to do the same thing on Windows, with Mingw, and it did not compile.
The code is:
# include <stdio.h>
extern char _binary_data_txt_start;
extern char _binary_data_txt_end;
int main(){
for (char* my_file = &_binary_data_txt_start; my_file <= &_binary_data_txt_end; my_file++)
putchar(*my_file);
printf("\n");
}
The compilation commands are:
objcopy --input-target binary --output-target elf32-i386 --binary-architecture i386 data.txt data.o
g++ main.cpp data.o -o test.exe
On Windows, I get the following compiler error:
undefined reference to `_binary_data_txt_start'
undefined reference to `_binary_data_txt_end'
I tried to replace elf32-i386 with i386-pc-mingw32, but I still get the same error.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我认为要与 MinGW 一起使用,您需要从 .c 文件中的名称中删除前导下划线。有关一些详细信息,请参阅使用 gcc mingw 嵌入二进制 blob。
看看使用以下内容是否有帮助:
如果您需要相同的源来运行 Linux 或 MinGW 版本,则可能需要使用预处理器以便在不同的环境中使用正确的名称。
I think that for this to work with MinGW you'll need to remove the leading underscore from the names in the .c file. See Embedding binary blobs using gcc mingw for some details.
See if using the following helps:
If you need the same source to work for Linux or MinGW builds, you might need to use the preprocessor to have the right name used in the different environments.
如果您使用的库需要
FILE*
来读取数据,那么您可以使用fmemopen(3)
从内存 blob 创建伪文件。这将避免在磁盘上创建临时文件。不幸的是,它是一个 GNU 扩展,所以我不知道它是否适用于 MinGW(可能不可用)。但是,大多数编写良好的库(例如 libpng 和 IJG 的 JPEG 库)提供了从内存而不是从磁盘打开文件的例程。特别是 libpng,甚至提供了一个流接口,您可以在 PNG 文件完全读入内存之前对其进行增量解码。例如,如果您正在从网络传输隔行扫描的 PNG,并且希望在加载时显示隔行扫描的数据以获得更好的用户体验,则此功能非常有用。
If you're using a library that requires a
FILE*
for reading data, then you can usefmemopen(3)
to create a pseudofile out of a memory blob. This will avoid creating a temporary file on disk. Unfortunately, it's a GNU extension, so I don't know if it's available with MinGW (likely not).However, most well-written libraries (such as libpng and the IJG's JPEG library) provide routines for opening a file from memory as opposed to from disk. libpng, in particular, even offers a streaming interface, where you can incrementally decode a PNG file before it's been completely read into memory. This is useful if, say, you're streaming an interlaced PNG from the network and you want to display the interlaced data as it loads for a better user experience.
在 Windows 上,您可以将自定义资源嵌入到可执行文件中。您需要一个
.RC
文件和一个资源编译器。使用 Visual Studio IDE,您可以轻松完成此任务。在代码中,您将使用
FindResource
、LoadResource
和LockResource
函数在运行时将内容加载到内存中。以长字符串形式读取资源的示例代码:其中,
nResourceID
是自定义资源类型DATA
下资源的 ID。 DATA 只是一个名称,您可以选择其他名称。其他内置资源有光标、对话框、字符串表等。On Windows, you can embed custom resource into executable file. You would need a
.RC
file and a resource compiler. With Visual Studio IDE you can do it without hassle.In your code, you would use
FindResource
,LoadResource
andLockResource
functions to load the contents into memory at runtime. A sample code that reads the resource as long string:Where
nResourceID
is the ID of resource under custom resource typeDATA
. DATA is just a name, you may choose another name. Other in-built resources are cursors, dialogs, string-tables etc.我创建了一个名为 elfdataembed 的小型库,它提供了一个简单的界面,用于提取/引用使用
对象复制
。这允许您将偏移量/大小传递给另一个工具,或使用文件描述符直接从运行时引用它。希望这对将来的人有所帮助。值得一提的是,这种方法比编译为符号更有效,因为它允许外部工具引用数据而无需提取,并且它也不需要将整个二进制文件加载到内存中以提取/引用它。
I've created a small library called elfdataembed which provides a simple interface for extracting/referencing sections embedded using
objcopy
. This allows you to pass the offset/size to another tool, or reference it directly from the runtime using file descriptors. Hopefully this will help someone in the future.It's worth mentioning this approach is more efficient than compiling to a symbol, as it allows external tools to reference the data without needing to be extracted, and it also doesn't require the entire binary to be loaded into memory in order to extract/reference it.
使用 nm data.o 查看它为符号命名的内容。它可能是像文件系统差异一样简单,导致文件名派生符号不同(例如文件名大写)。
编辑:刚刚看到你的第二个问题。如果您使用线程,您可以创建一个管道并将其传递给库(如果需要
FILE *
,首先使用fdopen()
)。如果您对需要交谈的 API 更具体,我可以添加更具体的建议。Use
nm data.o
to see what it named the symbols. It may be something as simple as the filesystem differences causing the filename-derived symbols to be different (eg filename capitalized).Edit: Just saw your second question. If you are using threads you can make a pipe and pass that to the library (first using
fdopen()
if it wants aFILE *
). If you are more specific about the API you need to talk to I can add more specific advice.