4

我们有大量 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...

所以它应该显示二进制文件本身及其所有依赖项的所有信息。

目前我们的做法是:

  1. 在每个产品的编译过程中,我们生成 Manifest.h 和 Manifest.cpp,然后将 Manifest.o 注入二进制文件

  2. ident 脚本解析目标二进制文件,在那里找到生成的东西并打印此信息

但是,对于不同版本的 gcc,这种方法并不总是可靠的。我想问一下 SO 社区 - 有没有更好的方法来解决这个问题?

感谢您的任何建议

4

3 回答 3

5

在源代码(您的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文件被黑客入侵的行为就像普通可执行文件一样)从外壳运行时)。

上面的 make 文件没有考虑依赖项——或者更确切地说,它不会帮助您以任何方式创建依赖项列表。如果您清楚地为每个目标(即静态库等)表达您的依赖关系,您可能会强迫 GNU make 帮助您。但走那条路可能不值得……

还请看:


如果您想要从数据生成的符号的特定名称(在您的情况下为清单),您需要使用稍微不同的路线并使用 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: %*s\n", (int)len, &_binary_hello_manifest_start);
}

符号是确切的字符/字节。您也可以将它们声明为char[],但这会导致以后出现问题。例如printf通话。

我自己计算大小的原因是因为 a.) 我不知道缓冲区是否保证为零终止,并且 b.) 我没有找到任何关于与*_size变量接口的文档。

旁注:*格式字符串中的 告诉printf它应该从参数中读取字符串的长度,然后选择下一个参数作为要打印的字符串。

于 2012-09-28T08:13:18.680 回答
2

您可以将您喜欢的任何数据插入到.comment输出二进制文件的部分中。您可以在事后使用链接器执行此操作,但将其放置在您的 C++ 代码中可能更容易,如下所示:

 asm  (".section .comment.manifest\n\t"
       ".string \"hello, this is a comment\"\n\t"
       ".section .text");

 int main() {
   ....

在这种情况下,该asm语句应该超出任何函数。只要您的编译器将正常功能放在该.text部分中,这应该可以工作。如果没有,那么您应该进行明显的替换。

链接器应将所有部分收集.comment.manifest到最终二进制文件中的一个 blob 中。.o您可以使用以下命令从任何或可执行文件中提取它们:

objdump -j .comment.manfest -s example.o
于 2012-09-28T08:45:53.887 回答
0

您是否考虑过使用发行版的标准包装系统?在我们公司,我们有数千个包,每天都会自动部署数百个包。

我们正在使用包含所有必要信息的 debian 软件包:

  • 完整的变更日志,包括:
    • 作者;
    • 版本;
    • 更改的简短描述和时间戳。
  • 依赖信息:
    • 必须安装以使当前软件包正常工作的所有软件包的列表。
  • 为包设置环境的安装脚本。

我认为,一旦现成的解决方案已经存在,您可能不需要以自己的方式创建清单。您可以在此处查看 debian 包 HowTo

于 2012-09-28T06:39:42.560 回答