使用 LLVM 工具将二进制数据嵌入到可执行文件中

使用 LLVM 工具将二进制数据嵌入到可执行文件中

过去,我首先.o使用 GNU 链接器将资源文件(图像)转换为文件,从而将资源文件(图像)嵌入到程序中。例如:

ld -r -b binary -o file.o file.svg

从 FreeBSD 12 开始,默认链接器已从 GNU 更改为 LLVM。尽管链接器似乎可以理解命令行选项,但它会导致错误。例如:

ld -r -b binary -o file.o file.svg
ld: error: target emulation unknown: -m or at least one .o file required

还尝试使用命令行选项ld.lld(1)手册页:

ld --relocatable --format=binary -o file.o file.svg
ld: error: target emulation unknown: -m or at least one .o file required

我使用的工具正确吗?我需要为该-m选项指定一个值吗?

答案1

看来你需要添加-z noexecstack(这也是为 ELF 二进制文件添加的LLD 7.0.0)。默认情况下有一个可执行堆栈区域,该区域容易受到攻击通过堆栈内存进行利用。您的二进制映像没有可执行堆栈,我相信这就是它失败的原因。该错误让您感到困惑,因为它要求您告诉堆栈要使用什么目标模拟(您没有)。

大卫赫尔曼做了所有艰苦的工作并找到了一个跨平台解决方案,其中包括:

  • GNU-ld
  • GNU-金
  • GNU 库工具
  • 使用交叉编译
  • 使用 LLVM
  • 不需要任何外部非标工具

魔法调用是:

$(LD) -r -o "src/mydata.bin.o" -z noexecstack --format=binary "src/mydata.bin"

大多数情况下,您希望该二进制段是只读的:

$(OBJCOPY) --rename-section .data=.rodata,alloc,load,readonly,data,contents "src/mydata.bin.o"

更新:

我无法测试,因为我的系统是:

$ uname -r
11.2-STABLE
$ ld -V
GNU ld 2.17.50 [FreeBSD] 2007-07-03
  Supported emulations:
   elf_x86_64_fbsd
   elf_i386_fbsd

我用 FreeBSD 12.0 启动了一个虚拟机来测试这一点,发现了这一点:

$ uname -r
12.0-RELEASE
$ ld -V
LLD 6.0.1 (FreeBSD 335540-1200005) (compatible with GNU linkers)

仅在 7.0.0 中添加-z noexecstack,并且未在列表中列出手册页对于 6.0.1。更烦人的是,指定不支持的值-z不会触发错误!

我还没有升级到 LLVM 7 来测试这是否有效。 @Richard Smith 通过-m在另一个答案中指定模拟,自己找到了正确的解决方案。如果 LLD 列出了支持的仿真-V,那么这条路线会容易得多。

如果您使用该file命令,file.o您将看到它标识为 SYSV ELF。这对你来说可能已经足够了。但如果你想要与系统完全相同,那么elf_amd64_fbsd使用别名为了elf_x86_64_fbsd。令人烦恼ld -V的是,它不像 GNU ld 那样输出支持的 LLD仿真。

$ file /bin/cat
/bin/cat: ELF 64-bit LSB executable, x86-64, version 1 (FreeBSD), dynamically linked, interpreter /libexec/ld-elf.so.1, for FreeBSD 12.0 (1200086), FreeBSD-style, stripped
$ ld -r -b binary -m elf_amd64 -o data.o data.bin
$ file data.o
data.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
$ ld -r -b binary -m elf_amd64_fbsd -o data.o data.bin
$ file data.o
data.o: ELF 64-bit LSB relocatable, x86-64, version 1 (FreeBSD), not stripped

elf_amd64_fbsdelf_x86_64_fbsd(参见D7837D24356)。希望 LLD 将仿真添加到-V输出中。

答案2

查了一下源码,正确的是目标仿真我的平台是elf_amd64。因此,从二进制文件到目标文件的转换可以使用:

ld -r -b binary -m elf_amd64 -o file.o file.svg

答案3

真的值得这么麻烦吗?这首先是不可移植的。

你最好将 .svg 转换为 C char 数组;例子:

$ cat Makefile
.SUFFIXES: .svg
.svg.c:
        od -tx1 $< | sed 's/ /,0x/g;s/[^,]*//;1s/,/char $*[]={/;$$s/$$/};/' > $@

$ make file.o
od -tx1 file.svg | sed 's/ /,0x/g;s/[^,]*//;1s/,/char file[]={/;$s/$/};/' > file.c
cc    -c -o file.o file.c
rm file.c

当然,您可以将数组的名称设置为不同的/更健壮的名称(例如,char $(subst /,_,$*)[] = ...而不是char $*[] = ...使用 GNU make)。另外,您可以构建一个用 C 编写的临时bin2c转换器,而不是那个可怕的 od+sed 组合。

答案4

我想提供一个简单的工具,称为资源嵌入。它具有类似的范围,但不基于 LLVM。它基本上使用 NASM 或 Gas 通过汇编文件嵌入二进制文件。它被包装到一个高级 CMake 接口中:

add_executable(res_example "example.cpp")
res_embed(TARGET res_example NAME "my_cool_image" PATH ${CMAKE_CURRENT_SOURCE_DIR}/file.svg)

然后您可以在程序中使用嵌入的资源,如下所示:

#include "res_embed.h"

#include <cstdio>

int main(int argc, char* argv[])
{
    printf("%s", res::embed::get("my_cool_image"));
    return 0;
}

如果大多数平台上都可以使用gas,那么该解决方案就是便携式的。它还通过 NASM 支持 Windows 中的 MSVC。它在以下方面与您的 LLD 方法类似: res_embed 避免了尝试将资源嵌入为 C 数组的后果,这会在无缘由地出现大量资源的情况下大大减慢编译速度。

相关内容