过去,我首先.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_fbsd
是elf_x86_64_fbsd
(参见D7837和D24356)。希望 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 数组的后果,这会在无缘由地出现大量资源的情况下大大减慢编译速度。