Forcing certain compiler-generated variables into specific ELF sections (with gcc)
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?
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.)
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
):
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.
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:
#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.
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 o开发者_StackOverflow社区n __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":
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__
.
Thus, if you wrap __func__
with ROM_STR
, you get
error: invalid initializer
and if you try to put a section attribute before or after the use of __func__
, you get
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.
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.
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!
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.
精彩评论