我们有大量的 C++ 项目(GCC、Linux,主要是静态库),它们之间存在许多依赖关系。然后我们使用这些库编译一个可执行文件并将二进制文件部署在前端。能够识别该二进制文件将非常有用。理想情况下,我们想要的是一个小脚本,可以直接从二进制文件中检索以下信息:
$ident binary
$binary : Product=PRODUCT_NAME;Version=0.0.1;Build=xxx;User=xxx...
$ dependency: Product=PRODUCT_NAME1;Version=0.1.1;Build=xxx;User=xxx...
$ dependency: Product=PRODUCT_NAME2;Version=1.0.1;Build=xxx;User=xxx...
因此它应该显示二进制文件本身及其所有依赖项的所有信息。
目前我们的做法是:
在每个产品的编译过程中,我们生成 Manifest.h 和 Manifest.cpp,然后将 Manifest.o 注入(inject)二进制
ident 脚本解析目标二进制文件,在那里找到生成的内容并打印此信息
然而,这种方法对于不同版本的 gcc 并不总是可靠的。 我想问 SO 社区 - 有没有更好的方法来解决这个问题?
谢谢你的建议
最佳答案
在源代码(您的 Manifest.h
和 .cpp
)中存储数据的问题之一是文字数据的大小限制,这取决于编译器.
我的建议是使用ld
。它允许您在 ELF 文件中存储任意二进制数据(objcopy
也是如此)。如果您更喜欢编写自己的解决方案,请查看 libbfd
.
假设我们有一个 hello.cpp
,其中包含常用的 C++“Hello world”示例。现在我们有以下 make 文件 (GNUmakefile
):
hello: hello.o hello.om
$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
%.om: %.manifest
ld -b binary -o $@ $<
%.manifest:
echo "$@" > $@
我在这里所做的是将链接阶段分开,因为我希望 list (在转换为 ELF 对象格式之后)也链接到二进制文件中。由于我使用的是后缀规则,这是一种可行的方法,当然还有其他方法,包括更好的 list 命名方案,它们最终也以 .o
文件结尾,GNU make 可以弄清楚如何创建那些。在这里,我对食谱很明确。所以我们有 .om
文件,它们是从 .manifest
文件创建的 list (任意二进制数据)。配方声明将二进制输入转换为 ELF 对象。创建 .manifest
本身的方法只是将一个字符串通过管道传输到文件中。
显然,您的案例中棘手的部分不是存储 list 数据,而是生成 list 数据。坦率地说,我对您的构建系统知之甚少,甚至无法尝试为 .manifest
生成建议一个配方。
无论您放入 .manifest
文件中的什么都应该是一些结构化文本,这些文本可以由您提到的脚本解释,或者如果您实现命令行开关,甚至可以由二进制文件本身输出(并忽略 .so
文件和 .so
文件,当从 shell 运行时,这些文件被黑客入侵,表现得像普通的可执行文件)。
上面的 make 文件没有考虑依赖关系——或者更确切地说,它不会以任何方式帮助您创建依赖关系列表。如果您为每个目标(即静态库等)清楚地表达您的依赖关系,您可能会强制 GNU make 帮助您。但走那条路可能不值得......
另请参阅:
- C/C++ with GCC: Statically add resource files to executable/library和
- Is there a Linux equivalent of Windows' "resource files"?
如果您想要从数据中生成的符号的特定名称(在您的情况下是 list ),您需要使用稍微不同的路径并使用 John Ripley 描述的方法 here .
如何访问符号?简单。将它们声明为外部(C 链接!)数据,然后使用它们:
#include <cstdio>
extern "C" char _binary_hello_manifest_start;
extern "C" char _binary_hello_manifest_end;
int main(int argc, char** argv)
{
const ptrdiff_t len = &_binary_hello_manifest_end - &_binary_hello_manifest_start;
printf("Hello world: %*s\n", (int)len, &_binary_hello_manifest_start);
}
符号是准确的字符/字节。您也可以将它们声明为 char[]
,但这会导致后续出现问题。例如。对于 printf
调用。
我自己计算大小的原因是 a.) 我不知道缓冲区是否保证以零终止 b.) 我没有找到任何关于与 * 接口(interface)的文档_size
变量。
旁注:格式字符串中的 *
告诉 printf
它应该从参数中读取字符串的长度,然后选择下一个参数作为字符串打印出来。
关于C++ 二进制识别( list ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12561552/