C++二进制标识(清单)
C++ binary identification (manifest)
我们有一大组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注入二进制
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 "$@" > $@
我在这里所做的是分离链接阶段,因为我希望清单(转换为ELF对象格式后)也链接到二进制文件中。由于我使用后缀规则,这是一种方法,其他方法当然也是可能的,包括为清单提供更好的命名方案,它们也最终成为.o
文件,GNU make可以找到如何创建这些文件。在这里,我要明确说明配方。所以我们有.om
文件,它们是清单(任意二进制数据),由.manifest
文件创建。配方说明将二进制输入转换为ELF对象。创建.manifest
的方法本身只是将一个字符串通过管道传输到文件中。
显然,在您的案例中,棘手的部分不是存储清单数据,而是生成它。坦率地说,我对您的构建系统了解太少,甚至无法尝试提出.manifest
生成的配方。
无论你在.manifest
文件中放入什么,都可能是一些结构化文本,这些文本可以由你提到的脚本解释,或者如果你实现了命令行切换,甚至可以由二进制文件本身输出(并且忽略.so
文件和.so
文件在从shell运行时表现得像普通可执行文件)。
上面的make文件没有考虑依赖项,或者说它没有以任何方式帮助您创建依赖项列表。如果你清楚地表达了你对每个目标的依赖关系(即静态库等),你可能会强迫GNU make来帮助你。但走这条路可能不值得。。。
另请查看:
- 带有GCC的C/C++:静态地将资源文件添加到可执行文件/库和
- 有没有相当于Windows'"资源文件">
如果您想要从数据(在您的情况下是清单)生成的符号的特定名称,则需要使用稍微不同的路径,并使用John Ripley在此处描述的方法。
如何访问符号?容易的将它们声明为外部(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: %*sn", (int)len, &_binary_hello_manifest_start);
}
符号是精确的字符/字节。您也可以将它们声明为char[]
,但这会导致以后出现问题。例如,对于printf
呼叫。
我自己计算大小的原因是:a.)我不知道缓冲区是否保证为零终止;b.)我没有找到任何关于与*_size
变量接口的文档。
旁注:格式字符串中的*
告诉printf
,它应该从参数中读取字符串的长度,然后选择下一个参数作为要打印的字符串。
您可以在输出二进制文件的.comment
节中插入任何您喜欢的数据。事后,您可以使用链接器执行此操作,但可能更容易将其放置在C++代码中,如下所示:
asm (".section .comment.manifestnt"
".string "hello, this is a comment"nt"
".section .text");
int main() {
....
在本例中,asm
语句应位于任何函数之外。只要编译器在.text
节中放入正常函数,这就应该有效。如果没有,那么你应该做出明显的替代。
链接器应将所有.comment.manifest
部分收集到最终二进制文件中的一个blob中。您可以从任何.o
或可执行文件中提取它们:
objdump -j .comment.manfest -s example.o
您是否想过使用发行版的标准封装系统?在我们公司,我们有数千个软件包,每天都会自动部署数百个。
我们正在使用包含所有必要信息的debian包:
- 完整的变更日志,包括:
- 作者
- 版本
- 更改的简短描述和时间戳
- 依赖关系信息:
- 当前程序包必须安装才能正常工作的所有程序包的列表
- 为程序包设置环境的安装脚本
我认为,一旦现成的解决方案已经存在,您可能不需要以自己的方式创建清单。你可以在这里看看debian包HowTo。
- 调用具有未标识类型的类的方法
- 如何返回或护理项目清单基于几个类别
- 函数返回的 rvalue 引用(表达式)是 xvalue - 但没有标识?
- OOP 标识派生对象
- 我的堆栈和库存清单程序的结构有什么问题?
- 安卓UE4错误缺少"SteamShared"的预编译清单
- assert() 在发布模式下充当标识函数吗?
- AddressSanitizer 将 std::vector<T>::p ush_back 标识为释放后堆使用错误的原因
- 标识gdb调试器中符号的源文件名
- 如何跳过 CMake 编译器标识?
- 如何创建一个程序来标识最长的子字符串回文,而不考虑字母大小写
- 具有基于标识的相等性的有序关联容器
- 标识线程
- 将清单嵌入到 exe 文件
- 一种类型特征,标识哪个类提供通过重载解析选择的函数
- C++ - 在运行时从基类指针标识派生类
- 在 Android NDK 中使用比 Android 清单中最低 API 更高的 API 是否有效?
- C ,vs 2015,致命错误:重复资源.类型:清单,名称:1,语言:0x0409
- 标识使用的设计模式名称
- C++二进制标识(清单)