Je commencerai par la dernière question: en C avec gcc, est-il possible d’obtenir la ou les valeurs de __func__
(ou de manière équivalente, __FUNCTION__
) stockées dans une section autre que .rodata
(ou où -mrodata=
points) ou une sous-section de cela?
L’explication complète:
Disons que j’ai une macro de journalisation:
#define LOG(fmt, ...) log_internal(__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)
(L’opérateur de concaténation de chaînes ##
utilisé dans ce contexte unaire utilise la virgule précédente si et seulement si la liste __VA_ARGS__
est vide, autorisant ainsi l’utilisation d’une chaîne de format avec ou sans arguments.)
Je peux alors utiliser la macro normalement:
void my_function(void) { LOG("foo!"); LOG("bar: %p", &bar); }
pourrait imprimer (évidemment en fonction de l’implémentation de log_internal
):
foo.c:201(my_function) foo! foo.c:202(my_function) bar: 0x12345678
Dans ce cas, les chaînes de format ( "foo"
et "bar: %p"
) et les chaînes de préprocesseur ( "foo.c"
et "my_function"
) sont des données anonymes en lecture seule et elles sont automatiquement placées dans la section .rodata
. .
Mais disons que je veux qu’ils aillent dans un endroit différent (je suis sur une plate-forme intégrée qui exécute presque tout, de la RAM à la vitesse, mais des contraintes de mémoire poussent à déplacer certaines choses dans la ROM). Il est “facile” de déplacer __FILE__
et la chaîne de formatage:
#define ROM_STR(str) (__extension__({static const __atsortingbute__((__section__(".rom_data"))) char __c[] = (str); (const char *)&__c;})) #define LOG(fmt, ...) log_internal(ROM_STR(__FILE__), __LINE__, __func__, ROM_STR(fmt), ##__VA_ARGS__)
Vous ne pouvez pas mettre un __atsortingbute__
sur une chaîne anonyme. ROM_STR
macro ROM_STR
lui atsortingbue un nom transitoire, l’ ROM_STR
à une section spécifique, puis est évaluée à l’adresse de départ afin qu’elle puisse s’y substituer proprement. Cela ne fonctionne pas si vous essayez de transmettre une variable char *
à LOG
tant que chaîne de format, mais je suis disposé à exclure ce cas d’utilisation.
Normalement, les compilateurs anonymes combinent des chaînes anonymes identiques dans un seul emplacement de stockage. Ainsi, chaque instance de __FILE__
dans un fichier partage la même adresse d’exécution. Avec la dénomination explicite dans ROM_STR
, chaque instance aura son propre emplacement de stockage, il n’a donc probablement aucun sens à l’utiliser sur __FILE__
.
Cependant, je voudrais l’utiliser sur __func__
. Le problème est que __func__
n’est pas du même type de magie que __FILE__
. Dans le manuel gcc, “Noms de fonctions en tant que chaînes”:
L’identificateur
__func__
est implicitement déclaré par le traducteur comme si, immédiatement après l’accolade ouvrante de chaque définition de fonction, la déclaration
static const char __func__[] = "function-name";
est apparu, où nom_fonction est le nom de la fonction englobant lexicalement. Ce nom est le nom sans fioritures de la fonction. … Ces identificateurs ne sont pas des macros de préprocesseur. Dans GCC 3.3 et
__FUNCTION__
antérieures, et uniquement dans C,__FUNCTION__
et__PRETTY_FUNCTION__
ont été traitées comme des littéraux de chaîne; ils pourraient être utilisés pour initialiser des tableaux de caractères et pourraient être concaténés avec d’autres littéraux de chaîne. GCC 3.4 et les__func__
ultérieures les traitent comme des variables, comme__func__
.
Ainsi, si vous enveloppez __func__
avec ROM_STR
, vous obtenez
error: invalid initializer
et si vous essayez de mettre un atsortingbut de section avant ou après l’utilisation de __func__
, vous obtenez
error: expected expression before '__atsortingbute__'
ou
error: expected ')' before '__atsortingbute__'
Et nous passons donc à la question d’ouverture: est-il possible d’enregistrer __func__
dans une section de mon choix? Peut-être que je peux utiliser -fdata-sections
et faire un peu de magie pour l’éditeur de liens pour obtenir .rodata.__func__.*
Exclu du rest de .rodata
? Si tel est le cas, quelle est la syntaxe de la suppression avec exclusion dans un script de l’éditeur de liens? En d’autres termes, quelque part vous avez un *(.rodata*)
– je pourrais mettre un *(.rodata.__func__*)
ailleurs, mais j’aurais besoin de modifier le glob original pour l’exclure afin de ne pas obtenir deux copies. .
Une approche, qui peut être plus compliquée que vous ne le souhaiteriez, serait d’interposer un script pour changer les noms de section entre compilation et assemblage. Par exemple:
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
Vous pouvez également essayer d’écrire une passe de transformation Clang pour placer les déclarations __func__
dans une section de votre choix ou d’écrire un programme de manipulation de fichier object à l’aide de libbfd .
On dirait que j’ai répondu à ma propre question à la fin avec le secteur -fdata-sections
, je n’avais tout simplement pas compris le GNU Linker suffisamment pour le voir. En fait, je n’ai pas besoin du globbing avec exclusion tant que je spécifie le *(.rodata.__func__*)
premier. Toutes les sections dans lesquelles les correspondances globales seront marquées comme utilisées, de sorte qu’une globale ultérieure pour *(.rodata*)
ne les comptera pas en double et ne les copiera pas ailleurs. Je n’ai pas du tout besoin de les taguer avec ROM_STR
. Cool!
Il est important de noter que -fdata-sections
réellement chaque chaîne de fonction dans sa propre section .rodata.__func__.1234
(je ne suis pas sûr du modèle suivi par les nombres). Je ne sais pas si les chaînes anonymes ont aussi leurs propres sections; Si tel est le cas, je pourrais utiliser les mêmes astuces de l’éditeur de liens pour capturer toutes les chaînes anonymes au lieu de la macro d’atsortingbut de section ROM_STR
, mais ce serait probablement une mauvaise idée. ROM_STR
est utilisé dans la macro LOG
, il est donc garanti qu’il est appliqué uniquement aux chaînes de format de journalisation. Si je forçais toutes les chaînes anonymes dans la ROM à l’aide d’un lieur, cela inclurait les données de message normales et je payerais une pénalité de performances d’exécution pour y accéder à partir de Flash. Donc, je ne sais pas si c’est même possible, mais son utilité dépend des exigences spécifiques de votre système.