我将从最终问题开始:在带有 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/