将某些编译器生成的变量强制转换为特定的ELF节(使用gcc)

时间:2021-03-26 07:00:52

I'll start with the ultimate question: In C with gcc, is it possible to get the value(s) of __func__ (or equivalently, __FUNCTION__) stored in a section other than .rodata (or wherever -mrodata= points) or subsection thereof?

我将从最终的问题开始:在C中使用gcc,是否有可能获得__func __(或等效地,__ FUNCTION__)的值存储在除.rodata(或者任何地方-mrodata = points)或子部分之外的部分中其?

The full explanation:

完整的解释:

Say I have a logging macro:

假设我有一个记录宏:

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

(The string concatenation operator ## used in that unary context consumes the preceding comma if and only if the __VA_ARGS__ list is empty, thereby allowing use of a format string with or without arguments.)

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

I can then use the macro normally:

然后我可以正常使用宏:

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

might print (obviously depending on the implementation of log_internal):

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

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

In this case, the format strings ("foo" and "bar: %p") and the preprocessor strings ("foo.c" and "my_function") are anonymous read only data, and they get placed into the .rodata section automatically.

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

But say I want them to go to a different place (I'm on an embedded platform running almost everything from RAM for speed, but memory constraints are pushing for moving some things into ROM). It's "easy" to move __FILE__ and the format string:

但是说我希望他们去另一个地方(我在一个嵌入式平台上运行几乎所有内存来自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__)

You can't put an __attribute__ on an anonymous string, so the ROM_STR macro gives it a transient name, affixes it to a specific section, then evaluates to the starting address, so it can substitute cleanly. This doesn't work if you try to pass a char * variable to LOG as your format string, but I'm willing to exclude that use case.

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

Normally, anonymous strings that happen to be identical get combined by the compiler into a single storage location, so every instance of __FILE__ in one file would share the same runtime address. With the explicit naming in ROM_STR, each instance will get its own storage location, so it probably doesn't actually make sense to use it on __FILE__.

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

However, I would like to use it on __func__. The problem is that __func__ is not the same kind of magic as __FILE__. From the gcc manual, "Function Names as Strings":

但是,我想在__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

标识符__func__由翻译器隐式声明,就像紧跟在每个函数定义的左大括号后面的声明一样

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__.

出现了,其中function-name是词法封闭函数的名称。这个名字是该功能的简单名称。 ...这些标识符不是预处理器宏。在GCC 3.3及更早版本中,仅在C中,__ FUNCTION__和__PRETTY_FUNCTION__被视为字符串文字;它们可以用来初始化char数组,它们可以与其他字符串文字连接起来。 GCC 3.4及更高版本将它们视为变量,如__func__。

Thus, if you wrap __func__ with ROM_STR, you get

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

error: invalid initializer

and if you try to put a section attribute before or after the use of __func__, you get

如果你试图在使用__func__之前或之后放置一个section属性,你就得到了

error: expected expression before ‘__attribute__’

or

error: expected ‘)’ before ‘__attribute__’

And thus we loop back to the opening question: Is it possible to get __func__ stored in a section of my choosing? Maybe I can use -fdata-sections and do some linker script magic to get .rodata.__func__.* excluded from the rest of .rodata? If so, what is the syntax for globbing with exclusion in a linker script? In other words, somewhere you have a *(.rodata*) - I could put a *(.rodata.__func__*) somewhere else, but I would need to modify the original glob to exclude it so I don't get two copies.

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

2 个解决方案

#1


3  

One approach, which may be hackier than you'd like, would be to interpose a script to change the section names between compilation and assembly. Eg:

一种可能比你想要的更黑的方法是插入一个脚本来改变编译和汇编之间的段名。例如:

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

You could also try writing a clang transformation pass to place the __func__ declarations in a section of your choosing, or writing an object file manipulation program using libbfd.

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

#2


2  

It looks like I answered my own question at the end with the -fdata-sections business, I just didn't understand the GNU Linker enough to see it. I don't actually need the globbing with exclusion as long as I specify the *(.rodata.__func__*) bit first. Any sections that glob matches will get marked as used, so a later glob for *(.rodata*) won't double count them and copy them somewhere else. I don't need to tag them with ROM_STR at all. Cool!

看起来我最后用-fdata-sections业务回答了我自己的问题,我只是不明白GNU Linker足以看到它。只要我先指定*(。rodata .__ func __ *)位,我实际上并不需要排除全局。 glob匹配的任何部分都将标记为已使用,因此*(。rodata *)的后续glob不会对它们进行双重计数并将它们复制到其他位置。我根本不需要用ROM_STR标记它们。凉!

It's important to note that -fdata-sections does actually put each function string into its own .rodata.__func__.1234 section (I'm not sure what pattern the numbers follow). I don't know if anonymous strings also get their own sections; if so, I could use the same linker tricks to capture all of the anonymous strings instead of the ROM_STR section attribute macro, but it would probably be a bad idea. ROM_STR gets used in the LOG macro, so it's guaranteed only to be applied to logging format strings. If I forced all anonymous strings into ROM with a linker trick, that would include normal message data, and I would pay a runtime performance penalty to access it from flash. So I don't know if it's even possible, but its advisability would depend on your specific system requirements.

重要的是要注意-fdata-sections确实将每个函数字符串放入它自己的.rodata .__ func __。1234部分(我不确定数字跟随的模式)。我不知道匿名字符串是否也有自己的部分;如果是这样,我可以使用相同的链接器技巧捕获所有匿名字符串而不是ROM_STR部分属性宏,但它可能是一个坏主意。 ROM_STR在LOG宏中使用,因此保证仅应用于日志记录格式字符串。如果我使用链接器技巧强制所有匿名字符串进入ROM,那将包括正常的消息数据,并且我将支付运行时性能损失以从闪存访问它。所以我不知道它是否可能,但它的可行性取决于您的具体系统要求。

#1


3  

One approach, which may be hackier than you'd like, would be to interpose a script to change the section names between compilation and assembly. Eg:

一种可能比你想要的更黑的方法是插入一个脚本来改变编译和汇编之间的段名。例如:

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

You could also try writing a clang transformation pass to place the __func__ declarations in a section of your choosing, or writing an object file manipulation program using libbfd.

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

#2


2  

It looks like I answered my own question at the end with the -fdata-sections business, I just didn't understand the GNU Linker enough to see it. I don't actually need the globbing with exclusion as long as I specify the *(.rodata.__func__*) bit first. Any sections that glob matches will get marked as used, so a later glob for *(.rodata*) won't double count them and copy them somewhere else. I don't need to tag them with ROM_STR at all. Cool!

看起来我最后用-fdata-sections业务回答了我自己的问题,我只是不明白GNU Linker足以看到它。只要我先指定*(。rodata .__ func __ *)位,我实际上并不需要排除全局。 glob匹配的任何部分都将标记为已使用,因此*(。rodata *)的后续glob不会对它们进行双重计数并将它们复制到其他位置。我根本不需要用ROM_STR标记它们。凉!

It's important to note that -fdata-sections does actually put each function string into its own .rodata.__func__.1234 section (I'm not sure what pattern the numbers follow). I don't know if anonymous strings also get their own sections; if so, I could use the same linker tricks to capture all of the anonymous strings instead of the ROM_STR section attribute macro, but it would probably be a bad idea. ROM_STR gets used in the LOG macro, so it's guaranteed only to be applied to logging format strings. If I forced all anonymous strings into ROM with a linker trick, that would include normal message data, and I would pay a runtime performance penalty to access it from flash. So I don't know if it's even possible, but its advisability would depend on your specific system requirements.

重要的是要注意-fdata-sections确实将每个函数字符串放入它自己的.rodata .__ func __。1234部分(我不确定数字跟随的模式)。我不知道匿名字符串是否也有自己的部分;如果是这样,我可以使用相同的链接器技巧捕获所有匿名字符串而不是ROM_STR部分属性宏,但它可能是一个坏主意。 ROM_STR在LOG宏中使用,因此保证仅应用于日志记录格式字符串。如果我使用链接器技巧强制所有匿名字符串进入ROM,那将包括正常的消息数据,并且我将支付运行时性能损失以从闪存访问它。所以我不知道它是否可能,但它的可行性取决于您的具体系统要求。