Le symbole de la fonction obtient le suffixe '.part' après compilation

Lors de la compilation croisée du kernel Linux 3.18.10, le compilateur ajoute un suffixe .part.<N> à la fin de certains symboles (voir un exemple ci-dessous). Le nombre <N> change lors de l'utilisation de différents defconfigs. Quelqu'un sait-il dans quelles conditions le compilateur ajoute le suffixe de partie à la fin d'un symbole?

$ arm-none-linux-gnueabi-readelf -a vmlinux | grep do_kernel_fault
donne
c03a48f8 116 FUNC LOCAL DEFAULT 2 __do_kernel_fault.part.10

Le symbole se terminant par .part est un symbole de fonction réel, pas une sorte de décoration de fonction. Plus précisément, une fonction se terminant par .part est une fonction générée par GCC à partir d'une fonction plus grande.

Parfois, GCC évalue qu'une certaine partie du stream de contrôle d'une grande fonction pourrait bien être mise en ligne, mais qu'il ne serait pas correct d'incorporer toute la fonction énorme. Par conséquent, il divise la fonction pour mettre la grande partie dans sa propre fonction, qui reçoit comme nom le nom de la fonction d'origine plus .part + .<some number> Some .<some number> , et insère le rest dans d'autres fonctions.

Cela fait partie d'une optimization décrite dans le code source de GCC, dans gcc/ipa-split.c . Dans gcc-4.8.3 au less (et probablement les versions ultérieures, je ne suis pas en mesure de vérifier en ce moment), il est dit:

 /* The purpose of this pass is to split function bodies to improve inlining. Ie for function of the form: func (...) { if (cheap_test) something_small else something_big } Produce: func.part (...) { something_big } func (...) { if (cheap_test) something_small else func.part (...); } When func becomes inlinable and when cheap_test is often true, inlining func, but not fund.part leads to performance improvement similar as inlining original func while the code size growth is smaller. The pass is organized in three stages: 1) Collect local info about basic block into BB_INFO structure and compute function body estimated size and time. 2) Via DFS walk find all possible basic blocks where we can split and chose best one. 3) If split point is found, split at the specified BB by creating a clone and updating function to call it. The decisions what functions to split are in execute_split_functions and consider_split. There are several possible future improvements for this pass including: 1) Splitting to break up large functions 2) Splitting to reduce stack frame usage 3) Allow split part of function to use values computed in the header part. The values needs to be passed to split function, perhaps via same interface as for nested functions or as argument. 4) Support for simple rematerialization. Ie when split part use value computed in header from function parameter in very cheap way, we can just recompute it. 5) Support splitting of nested functions. 6) Support non-SSA arguments. 7) There is nothing preventing us from producing multiple parts of single function when needed or splitting also the parts. */ 

Comme vous l'avez deviné, ce process est entièrement contrôlé par le compilateur. Le nouveau nom de symbole est produit par la fonction clone_function_name dans gcc/cgraphclones.c . Le nombre ajouté après .part n'a pas de signification particulière, il est utilisé uniquement pour éviter les conflits de noms. C'est un countur simple qui est incrémenté chaque fois que GCC crée une nouvelle fonction à partir d'un existant (ce que les développeurs de GCC appellent un 'clone').

Vous pouvez utiliser l'option -fdisable-ipa-fnsplit pour empêcher le compilateur d'appliquer cette optimization, ou -fenable-ipa-fnsplit pour l'activer. Par défaut, il est appliqué aux niveaux d'optimization -O2 et -O3 et désactivé dans le cas contraire.