c - 强制某些编译器生成的变量进入特定的 ELF 部分(使用 gcc)

标签 c gcc linker elf

我将从最终问题开始:在带有 gcc 的 C 中,是否有可能获取存储的 __func__(或等效的 __FUNCTION__)的值在 .rodata 以外的部分(或 -mrodata= 指向的任何地方)或其子部分?

完整解释:

假设我有一个日志记录宏:

#define LOG(fmt, ...) log_internal(__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)

(当且仅当 __VA_ARGS__ 列表为空时,该一元上下文中使用的字符串连接运算符 ## 消耗前面的逗号,从而允许使用格式字符串有或没有参数。)

然后我就可以正常使用宏了:

void my_function(void) {
    LOG("foo!");
    LOG("bar: %p", &bar);
}

可能会打印(显然取决于 log_internal 的实现):

foo.c:201(my_function) foo!
foo.c:202(my_function) bar: 0x12345678

在这种情况下,格式字符串("foo""bar: %p")和预处理器字符串("foo.c""my_function") 是匿名只读数据,它们会自动放入 .rodata 部分。

但是假设我希望他们去不同的地方(我在一个嵌入式平台上运行几乎所有从 RAM 以提高速度的平台,但内存限制正在插入将一些东西移动到 ROM 中)。移动 __FILE__ 和格式字符串很“容易”:

#define ROM_STR(str) (__extension__({static const __attribute__((__section__(".rom_data"))) char __c[] = (str); (const char *)&__c;}))
#define LOG(fmt, ...) log_internal(ROM_STR(__FILE__), __LINE__, __func__, ROM_STR(fmt), ##__VA_ARGS__)

你不能把 __attribute__ 放在匿名字符串上,所以 ROM_STR 宏给它一个临时名称,把它附加到一个特定的部分,然后求值到起始地址,所以它可以干净地替换。如果您尝试将 char * 变量作为格式字符串传递给 LOG,这将不起作用,但我愿意排除该用例。

通常情况下,碰巧相同的匿名字符串会被编译器组合到一个存储位置,因此一个文件中的每个 __FILE__ 实例都将共享相同的运行时地址。通过 ROM_STR 中的显式命名,每个实例都将获得自己的存储位置,因此在 __FILE__ 上使用它实际上可能没有意义。

但是,我想在 __func__ 上使用它。问题是 __func____FILE__ 不是同一种魔法。来自 gcc 手册,“函数名称作为字符串”:

The identifier __func__ is implicitly declared by the translator as if, immediately following the opening brace of each function definition, the declaration

static const char __func__[] = "function-name";

appeared, where function-name is the name of the lexically-enclosing function. This name is the unadorned name of the function. ... These identifiers are not preprocessor macros. In GCC 3.3 and earlier, and in C only, __FUNCTION__ and __PRETTY_FUNCTION__ were treated as string literals; they could be used to initialize char arrays, and they could be concatenated with other string literas. GCC 3.4 and later treat them as variables, like __func__.

因此,如果你用 ROM_STR 包装 __func__,你会得到

error: invalid initializer

如果您尝试在使用__func__ 之前或之后放置一个section 属性,您会得到

error: expected expression before ‘__attribute__’

error: expected ‘)’ before ‘__attribute__’

因此我们回到开头的问题:是否有可能将 __func__ 存储在我选择的部分中?也许我可以使用 -fdata-sections 并做一些链接器脚本魔术来将 .rodata.__func__.*.rodata 的其余部分中排除?如果是这样,在链接描述文件中排除通配符的语法是什么?换句话说,在某个地方你有一个 *(.rodata*) - 我可以在其他地方放一个 *(.rodata.__func__*) ,但我需要修改原始 glob 将其排除,这样我就不会得到两个副本。

最佳答案

一种方法可能比您想要的更复杂,它是插入一个脚本来更改编译和汇编之间的部分名称。例如:

gcc -fdata-sections -S -o test.s test.c
sed 's/^\t.section\t\.rodata\.__func__\.[0-9]*/\t.section .rom_data/' -i test.s
gcc -c test.s

您还可以尝试编写一个 clang 转换过程以将 __func__ 声明放在您选择的部分中,或者使用 libbfd 编写目标文件操作程序。 .

关于c - 强制某些编译器生成的变量进入特定的 ELF 部分(使用 gcc),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6447463/

相关文章:

c - 在循环内使用指针后,文件中的行未被读取

c - 当某个条件为假时如何打破 while 循环

c 线程和资源锁定

c++ - GCC:使用 -Werror 但将特定错误降级为警告(在命令行中)

c++ - 使用 bjam 编译的 C++ 中的 undefined symbol

iphone - 删除XCode中针对不同目标的代码链接-iAds

c - 为什么 execvp 接受 2 个参数

c - 为什么这个程序输出8?

c - 从 fgets() 输入中删除换行符

c++ - 如何在外部目标文件中调用重命名的符号?