我将从最终的问题开始:在C中使用gcc,是否可以获得__func__
(或等效地__FUNCTION__
)存储在除.rodata
(或其中任何-mrodata=
点)或子部分之外的部分的值(?)?
完整的解释:
假设我有一个记录宏:
#define LOG(fmt, ...) log_internal(__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
(##
当且仅当__VA_ARGS__
列表为空时,在该一元上下文中使用的字符串连接运算符使用前面的逗号,从而允许使用带或不带参数的格式字符串.)
然后我可以正常使用宏:
void my_function(void) {
LOG("foo!");
LOG("bar: %p", &bar);
}
Run Code Online (Sandbox Code Playgroud)
可能打印(显然取决于实施log_internal
):
foo.c:201(my_function) foo!
foo.c:202(my_function) bar: 0x12345678
Run Code Online (Sandbox Code Playgroud)
在这种情况下,格式字符串("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__)
Run Code Online (Sandbox Code Playgroud)
你不能放一个__attribute__
匿名字符串,所以ROM_STR
宏给它一个临时名称,将它粘贴到一个特定的部分,然后计算到起始地址,所以它可以干净地替换.如果您尝试将char *
变量LOG
作为格式字符串传递,则此方法无效,但我愿意排除该用例.
通常,碰巧相同的匿名字符串会被编译器合并到一个存储位置,因此__FILE__
一个文件中的每个实例都将共享相同的运行时地址.使用显式命名ROM_STR
,每个实例都将获得自己的存储位置,因此使用它可能实际上没有意义__FILE__
.
但是,我想用它__func__
.问题是,__func__
这与魔法不同__FILE__
.从gcc手册,"函数名称为字符串":
标识符
__func__
由翻译器隐式声明,就像紧跟在每个函数定义的左括号之后的声明一样
static const char __func__[] = "function-name";
Run Code Online (Sandbox Code Playgroud)
出现了,其中function-name是词法封闭函数的名称.此名称是函数的简单名称....这些标识符不是预处理器宏.在GCC 3.3和更早,并且在仅C,
__FUNCTION__
并且__PRETTY_FUNCTION__
被视为字符串文字; 它们可用于初始化char数组,它们可以与其他字符串文字连接.GCC 3.4及更高版本将它们视为变量,如__func__
.
因此,如果你换__func__
用ROM_STR
,你
error: invalid initializer
Run Code Online (Sandbox Code Playgroud)
如果你试图在使用之前或之后放置一个section属性__func__
,你得到
error: expected expression before ‘__attribute__’
Run Code Online (Sandbox Code Playgroud)
要么
error: expected ‘)’ before ‘__attribute__’
Run Code Online (Sandbox Code Playgroud)
因此,我们回到开头问题:是否有可能__func__
存储在我选择的部分中?也许我可以使用-fdata-sections
并做一些链接器脚本魔法.rodata.__func__.*
从其他部分排除.rodata
?如果是这样,在链接描述文件中使用排除的globbing的语法是什么?换句话说,你有一个地方*(.rodata*)
- 我可以把它*(.rodata.__func__*)
放在其他地方,但我需要修改原始的glob来排除它,所以我没有得到两个副本.
看来我最后用业务回答了我自己的问题-fdata-sections
,我只是对 GNU Linker 不够了解,无法看到它。只要我*(.rodata.__func__*)
先指定该位,我实际上并不需要排除的通配符。glob 匹配的任何部分都将被标记为已使用,因此稍后的 glob*(.rodata*)
不会重复计算它们并将它们复制到其他地方。ROM_STR
我根本不需要给它们贴上标签。凉爽的!
值得注意的是,它-fdata-sections
实际上将每个函数字符串放入其自己的.rodata.__func__.1234
部分(我不确定数字遵循什么模式)。我不知道匿名字符串是否也有自己的部分;如果是这样,我可以使用相同的链接器技巧来捕获所有匿名字符串,而不是ROM_STR
节属性宏,但这可能是一个坏主意。 ROM_STR
在宏中使用LOG
,因此保证仅应用于日志记录格式字符串。如果我使用链接器技巧将所有匿名字符串强制写入 ROM,那么这将包括正常的消息数据,并且我将付出运行时性能损失来从闪存访问它。所以我不知道这是否可能,但其建议取决于您的具体系统要求。
归档时间: |
|
查看次数: |
4152 次 |
最近记录: |