ORC est un outil permettant de détecter les violations de la règle de définition unique de C++ sur la chaîne d'outils OSX.
ORC est un jeu sur DWARF qui est un jeu sur ELF. ORC est un acronyme ; tandis que le O signifie ODR, dans un accès d'ironie, le R et le C représentent plusieurs mots (éventuellement contradictoires).
Il existe de nombreux articles sur la règle de définition unique (ODR), y compris la norme C++ elle-même. L'essentiel de la règle est que si un symbole est défini dans un programme, il ne peut être défini qu'une seule fois. Certains symboles bénéficient d'une exception à cette règle et peuvent être définis plusieurs fois. Cependant, ces symboles doivent être définis par des séquences de jetons identiques .
Notez que certains paramètres du compilateur peuvent également affecter les séquences de jetons - par exemple, l'activation ou la désactivation de RTTI peut modifier la définition d'un symbole (dans ce cas, la table virtuelle d'une classe.)
Tout symbole qui enfreint la règle ci-dessus constitue une violation ODR (ODRV). Dans certains cas, l'éditeur de liens peut détecter la définition du symbole en double et émettre un avertissement ou une erreur. Cependant, la norme indique qu'un éditeur de liens n'est pas tenu de le faire. Andy G le décrit bien :
pour des raisons de performances, la norme C++ stipule que si vous violez la règle de définition unique en ce qui concerne les modèles, le comportement est tout simplement indéfini. Puisque l'éditeur de liens s'en fiche, les violations de cette règle sont silencieuses. source
Des ODRV sans modèle sont possibles, et l'éditeur de liens peut également rester silencieux à leur sujet.
Un ODRV signifie généralement que vous disposez d'un symbole dont la disposition binaire diffère selon l'unité de compilation qui l'a construit. Cependant, en raison de la règle, lorsqu'un éditeur de liens rencontre plusieurs définitions, il est libre d'en choisir une et de l'utiliser comme disposition binaire pour un symbole. Lorsque la disposition sélectionnée ne correspond pas à la disposition binaire interne du symbole dans une unité de compilation, le comportement n'est pas défini.
Souvent, le débogueur est inutile dans ces scénarios. Lui aussi utilisera une définition unique d'un symbole pour l'ensemble du programme, et lorsque vous essayez de déboguer un ODRV, le débogueur peut vous donner des données incorrectes ou pointer vers un emplacement dans un fichier qui ne semble pas correct. En fin de compte, le débogueur semblera vous mentir, mais ne fournira silencieusement aucun indice sur le problème sous-jacent.
Comme tous les bogues, la correction des ODRV prend du temps, alors pourquoi devriez-vous corriger une violation ODR dans un code testé (et probablement fonctionnel) ?
ORC est un outil qui effectue les tâches suivantes :
Sauf bug dans l’outil, ORC ne génère pas de faux positifs. Tout ce qu'il rapporte est un ODRV.
À l’heure actuelle, ORC ne détecte pas toutes les violations possibles de la règle de définition unique. Nous espérons étendre et améliorer ce qu’il peut capturer au fil du temps. En attendant, cela signifie que même si ORC constitue un contrôle précieux, une analyse propre ne garantit pas qu'un programme est exempt d'ODRV.
ORC peut trouver :
Une note sur les vtables : ORC détectera les méthodes virtuelles qui se trouvent dans différents emplacements. (Ce qui est une sorte de programme corrompu.) À ce stade, il ne détectera pas une classe dont les méthodes virtuelles sont un "sur-ensemble" d'une classe en double violant l'ODR.
En plus des principales sources ORC, nous essayons de fournir une multitude d'exemples d'applications contenant des ODRV que l'outil doit détecter.
ORC a été initialement conçu sur macOS. Bien que sa mise en œuvre actuelle soit concentrée là-bas, elle ne doit pas nécessairement être limitée à cette chaîne d'outils.
ORC est géré par cmake et est construit en utilisant les conventions de construction typiques d'un projet géré par CMake :
mkdir build
cd build
cmake -GXcode ..
Il existe une poignée d'exemples d'applications dans lesquelles ORC est intégré à des fins de test. Ceux-ci peuvent être sélectionnés via la fenêtre contextuelle des cibles dans Xcode.
ORC utilise Tracy comme outil de profilage de choix et il est activé par défaut. Pour désactiver Tracy, spécifiez la ligne de commande cmake comme ceci :
cmake .. -GXcode -DTRACY_ENABLE=OFF
La dépendance Tracy est requise même si le profilage est désactivé (il sera compilé hors du runtime.) Notez que cette option est mise en cache, vous devez donc l'activer explicitement OFF
ou ON
. Réexécuter l’invocation de la ligne de commande avec l’option manquante entraînera l’utilisation de sa valeur précédente.
ORC peut être appelé directement depuis la ligne de commande ou inséré dans la chaîne d'outils lors de l'étape de liaison. La sortie est inchangée ; c'est simplement une question de commodité dans votre flux de travail.
Ce mode est utile si vous disposez de la commande linker et de ses arguments, et que vous souhaitez rechercher des ODRV distincts de la version réelle.
Fichier de configuration (voir ci-dessous)
'forward_to_linker' = false
'standalone_mode' = false
Vous avez besoin des arguments de ligne de commande ld
de XCode. Construisez avec Xcode (si vous ne pouvez pas créer de lien, ORC ne peut pas vous aider), copiez la commande de lien et collez-la après l'invocation d'ORC. Quelque chose comme :
/path/to/orc /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -target ... Debug/lem_mac
(C'est une énorme ligne de commande, abrégée ici.)
ORC s'exécutera et enregistrera les violations ODR sur la console.
Si vous disposez d'une liste de fichiers de bibliothèque à traiter par ORC, il peut également le faire.
Fichier de configuration (voir ci-dessous)
'forward_to_linker' = false
'standalone_mode' = true
Dans ce mode, transmettez simplement une liste de fichiers de bibliothèque à ORC pour qu'il les traite.
Fichier de configuration (voir ci-dessous)
'forward_to_linker' = true
'standalone_mode' = false
Pour utiliser ORC dans votre projet de build Xcode, remplacez les variables suivantes par un chemin complet vers les scripts ORC :
"LIBTOOL": "/absolute/path/to/orc",
"LDTOOL": "/absolute/path/to/orc",
"ALTERNATE_LINKER": "/absolute/path/to/orc",
Avec ces paramètres en place, Xcode devrait utiliser ORC comme outil pour les phases libtool
et ld
de la construction du projet. En raison du paramètre forward_to_linker
, ORC invoquera l'outil de lien approprié pour produire un fichier binaire. Une fois cette opération terminée, ORC lancera son analyse.
Entre autres paramètres, ORC peut être configuré pour quitter ou simplement avertir lorsqu'un ODRV est détecté.
ORC parcourra le répertoire actuel, à la recherche d'un fichier de configuration nommé :
.orc-config
, ou_orc-config
S'ils sont trouvés, de nombreux commutateurs peuvent contrôler la logique d'ORC. Veuillez consulter _orc_config
dans le référentiel pour des exemples. ORC préférera .orc-config
, il est donc simple de copier le _orc_config
original et de modifier les valeurs localement dans le .orc-config
.
Par exemple:
error: ODRV (structure:byte_size); conflict in `object`
compilation unit: a.o:
definition location: /Volumes/src/orc/extras/struct0/src/a.cpp:3
calling_convention: pass by value; 5 (0x5)
name: object
byte_size: 4 (0x4)
compilation unit: main.o:
definition location: /Volumes/src/orc/extras/struct0/src/main.cpp:3
calling_convention: pass by value; 5 (0x5)
name: object
byte_size: 1 (0x1)
structure:byte_size
est connue sous le nom de catégorie ODRV et détaille exactement le type de violation que représente cette erreur. Les deux unités de compilation en conflit sont ensuite sorties, ainsi que les informations DWARF qui ont entraîné la collision.
struct object { ... }
In ao:
et In main.o
sont les 2 fichiers objets ou archives qui ne correspondent pas. L'ODR est probablement dû à des paramètres de compilation ou #define incompatibles lors de la compilation de ces archives. byte_size
est la valeur réelle provoquant une erreur.
definition location: /Volumes/src/orc/extras/struct0/src/a.cpp:3
Dans quelle ligne et dans quel fichier l'objet a été déclaré. Donc la ligne 3 de a.cpp
dans cet exemple.
Pour la même version d'ORC et la même entrée, ORC écrira toujours la même sortie. Où "le même" est identique en octets et un outil de comparaison ne montrera aucune différence.
Obtenir (et probablement maintenir) une sortie cohérente est étonnamment difficile dans une application hautement multithread.
Veuillez toutefois garder à l'esprit que cela ne s'applique PAS aux différentes versions d'ORC. Les modifications apportées à ORC entraîneront presque certainement des modifications de sortie.
Il n'y a également aucune garantie qu'un « petit » changement dans les fichiers d'entrée garantira un « petit » changement dans la sortie ORC. Ce comportement est souhaitable et fera probablement l’objet d’améliorations futures.
orc_test
) Une application de test unitaire est fournie pour garantir qu'ORC détecte ce qui est censé capturer. orc_test
introduit un « système de construction » miniature pour générer des fichiers objets à partir de sources connues afin de produire des violations ODR connues. Il traite ensuite les fichiers objets à l'aide du même moteur que l'outil de ligne de commande ORC et compare les résultats à une liste de rapports ODRV attendue.
Chaque test unitaire de la batterie est discret et contient :
odrv_test.toml
, un fichier TOML de haut niveau décrivant les paramètres du testEn général, un seul test devrait donner lieu à une seule violation du ODR, mais cela n’est pas nécessairement possible dans tous les cas.
Ces fichiers sont des fichiers sources C++ standard. Leur quantité et leur taille doivent être très petites – juste assez grandes pour provoquer l’ODRV prévu.
odrv_test.toml
Le fichier de paramètres décrit à l'application de test quelle(s) source(s) doivent être compilées, quels indicateurs de compilation doivent être utilisés pour le test et quels ODRV le système doit surveiller suite à la liaison du(des) fichier(s) objet(s) généré(s). ) ensemble.
Les sources de test sont spécifiées avec une directive [[source]]
:
[[ source ]]
path = " one.cpp "
obj = " one "
flags = [
" -Dfoo=1 "
]
Le champ path
décrit un chemin d'accès au fichier relatif à odrv_test.toml
. C'est le seul champ obligatoire.
Le champ obj
précise le nom du fichier objet (temporaire) à créer. Si ce nom est omis, un nom pseudo-aléatoire sera utilisé.
Le champ flags
spécifie les indicateurs de compilation qui seront utilisés spécifiquement pour cette unité de compilation. En utilisant ce champ, il est possible de réutiliser le même fichier source avec différents indicateurs de compilation pour obtenir un ODRV.
Les ODRV sont spécifiés avec la directive [[odrv]]
:
[[ odrv ]]
category = " subprogram:vtable_elem_location "
linkage_name = " _ZNK6object3apiEv "
Le champ category
décrit le type spécifique de violation ODR que l’application de test doit s’attendre à trouver.
Le champ linkage_name
décrit le symbole spécifique à l’origine de l’ODRV. Il est actuellement inutilisé, mais sera appliqué à mesure que l’application de test mûrira.
Les indicateurs suivants ne sont pas actuellement utilisés ou subiront de lourdes modifications à mesure que l'application de test unitaire continue de mûrir.
[compile_flags]
: Une série d'indicateurs de compilation qui doivent être appliqués à chaque fichier source du test unitaire.
[orc_test_flags]
: une série de paramètres d'exécution à transmettre à l'application de test pour ce test.
[orc_flags]
: Une série de paramètres d'exécution à transmettre au moteur ORC pour ce test.