Forçage de certaines variables générées par le compilateur dans des sections ELF spécifiques (avec gcc)

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.