在链接时插入字符串

Posted

技术标签:

【中文标题】在链接时插入字符串【英文标题】:Insert string at linking time 【发布时间】:2019-05-10 10:21:33 【问题描述】:

我想定义一个程序使用的外部全局符号(const char*)。在链接时添加具有给定值的符号。这对于例如提交哈希或构建时间很有用。

我发现--defsym 做了别的事情。 Go 链接器通过 -X 选项支持此功能。 (是的,我知道,Go 字符串是托管的,但我说的是普通的旧零终止 c 字符串)

例如:

extern const char *git_commit;

int main(int argc, char *argv[]) 
    puts(git_commit);
    return 0;

gcc main.o -Wl<something here that adds git_commit and set it to '84e5...'>

我知道config.h 方法和构建包含这些字符串的目标文件。但它的2019年知道。这样一个简单的任务应该很容易。

编辑更精确的问题 在 gcc/binutils 中是否有 Go Linker 的 -X 选项的等效选项。

【问题讨论】:

如何将字符串放入config.h 并不容易?相比之下,找到符合您要求的链接器参数(包括正确编码)似乎是一场噩梦。 不,这很愚蠢。您必须重建使用此config.h 文件的每个文件。由于包括地狱,这可能很多。 Go Linker 支持它是有充分理由的。 如果你不想调用编译器或任何东西,你总是可以写一个程序,直接写一个带有合适符号定义的目标文件…… 我的问题是这是否可能。这里的每个人都在解决不同的问题。我以 git_commit 为例,简单说明结果应该是什么。 综上所述,来自 VCS 的工件(SVN 修订版、GIT 哈希等)是一种糟糕的软件版本控制方式。您的软件应该有自己的版本控制,它独立于其源代码的版本控制。 【参考方案1】:

在编译/预处理时间有一个改变,考虑一下:

#include <stdio.h>

const char * git_commit = GIT_COMMIT;

int main(int argc, char ** argv) 
        puts(git_commit);
        return 0;

在命令行中:

gcc -o test test.c -DGIT_COMMIT="\"This is a test\""

假设您使用的是 GCC 编译器。

【讨论】:

忍者。 +1,这是最“自然”(即最不“棘手”)的方法。也很便携。 @nwp: 哪个重新编译问题? 想要更改字符串而不需要重新编译所有内容的问题。 @nwp:这不是头文件。这是一个源文件。在某些标头中声明extern const char * git commit;,您可以从源代码中的任何位置引用此源文件中的定义。 没有重新编译问题。您必须重新链接,但您也必须使用链接时解决方案来做到这一点。 啊,我明白了。您只需构建一个链接的目标文件,与其他答案相同。【参考方案2】:

一种方式:

echo "char const* git_commit = \"$(git rev-parse HEAD)\";" > git_commit.c
gcc -c -o git_commit.o git_commit.c
gcc -o main main.o git_commit.o

当您在 makefile 中实现此功能时,您可能希望仅在修订更改时重新创建 git_commit.c,这样它就不会在每次 make 调用时重新链接它。

【讨论】:

不提供问题的答案,而只是解决用作示例的问题。 @LarsM。公平地说,您的帖子中只有陈述,没有问题。 @LarsM。似乎你需要一个更好的例子。解决示例也可以解决问题。 @LarsM.:经常有这样的情况,人们有一个xy problem——他们有一个问题X,他们试图用Y的方式解决它,然后发布一个关于Y的问题。许多有经验的 SO 用户/开发人员认识到这一点,并实际上解决了 X 问题而不是 Y 问题。考虑一下这是否真的可以解决您的 X 问题...【参考方案3】:

我不知道有任何方法可以通过使用常用链接器之一的标志直接执行您想要执行的操作。一般来说,如果您想将此类对象的定义链接到您的程序中,您必须提供一个包含合适符号定义的对象文件。

到达那里的最简单的方法可能是调用编译器来编译变量的定义,其中的内容是从通过命令行定义的宏提供的内容,就像其他答案中已经建议的那样。如果你想避免创建临时源文件,gcc 也可以直接从标准输入接收输入:

echo "const char git_commit[] = GIT_COMMIT;" | gcc -DGIT_COMMIT=\"asdf\" -c -o git_commit.obj -xc++ -

在您的代码中,您只需将其声明为

extern const char git_commit[];

注意:我使用的是const char git_commit[] 而不是const char* git_commit。这样,git_commit 将直接成为一个大小合适的数组,初始化为保存提交哈希的内容。另一方面,const char* git_commit 将创建一个全局指针对象,该对象已初始化以保存单独的字符串文字对象的地址,这意味着您引入了不必要的间接寻址。并不是说它在这里真的很重要,但是跳过低效率也不会花费您任何费用,无论它可能多么微小......

还有objcopy 实用程序,可用于将任意二进制内容包装在目标文件中,例如,参见How do I embed the contents of a binary file in an executable on Mac OS X? 甚至可以通过标准输入直接将输入传递给objcopy。最后,您也可以编写自己的工具,直接编写包含合适符号定义的目标文件。然而,考虑一下,在一天结束时,您正在寻求生成一个可以与构成您的程序的其他目标文件链接的目标文件。简单地使用您用来编译其余代码的相同编译器可能是最可靠的方法。对于其他解决方案,您始终必须手动确保目标文件在目标架构、内存布局等方面兼容……

【讨论】:

【参考方案4】:

您可以将其嵌入为资源文件,例如:

GitInfohtml               HTML                    "GitInfo.html"

这将由链接器(在链接时间)放入可执行文件中,然后可以加载。

有关详细信息,请参阅: https://docs.microsoft.com/en-us/cpp/windows/resource-files-visual-studio?view=vs-2019

【讨论】:

以上是关于在链接时插入字符串的主要内容,如果未能解决你的问题,请参考以下文章

将 Next.js 链接插入 html 字符串

mongoose 在 express 中使用 _id 链接两个集合,并将 id 作为数字或字符串插入

sqlserver数据库批量插入-SqlBulkCopy

超链接可以多长?

在 MySQL 表中同时插入和更新转义字符、单引号和双引号

c# 操作mysql数据库的时候会出现 插入中文汉字变成问号?