Qu'est-ce qui cause ces plantages après la compilation croisée?

8

J'essaie de compiler de manière croisée une grande bibliothèque (TensorFlow) en utilisant gcc sur Ubuntu. J'ai installé la chaîne d'outils g ++ - arm-linux-gnueabihf, et j'ai réussi à créer mon binaire. Le processus que j'utilise pour créer est documenté ici: https://github.com/petewarden4prs/tensorflow/tree/master/tensorflow/contrib/makefile#raspberry-pi

Au début, j'ai rencontré une erreur indiquant que pthreading était désactivé («Activer le multithreading pour utiliser std :: thread: opération non autorisée») lorsque j'ai essayé d'exécuter l'exécutable résultant sur mon Pi 3. J'ai recompilé avec -pthread activé comme option de compilation, et maintenant, le programme se bloque apparemment au hasard avec des défauts de segmentation. En l'exécutant dans gdb, ils semblent souvent liés à l'appel de free () avec de mauvais pointeurs, et les piles d'appels semblent corrompues, donc je suppose qu'il y a un décalage de mémoire.

Quelqu'un a-t-il des suggestions sur les choses que je peux essayer de découvrir ce qui ne va pas ici?

Voici quelques détails supplémentaires de mon Pi:

pi@raspberrypi ~ $ uname -a
Linux raspberrypi 4.1.19-v7+ #858 SMP Tue Mar 15 15:56:00 GMT 2016 armv7l GNU/Linux
pi@raspberrypi ~ $ file benchmark 
benchmark: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x5043384f5d0003f8074b07dfdd38cdc20315143f, not stripped

Voici un exemple de session typique dans gdb:

[New Thread 0x76cf5450 (LWP 6011)]
*** glibc detected *** /home/pi/benchmark: free(): invalid pointer: 0x018e2e89 ***

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x76cf5450 (LWP 6011)]
0x76f98e40 in std::string::c_str() const () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
(gdb) thread apply all bt

Thread 2 (Thread 0x76cf5450 (LWP 6011)):
#0  0x76f98e40 in std::string::c_str() const () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
#1  0x00bad996 in tensorflow::thread::ThreadPool::Impl::WorkerLoop() ()
#2  0x00bad5de in tensorflow::thread::ThreadPool::Impl::Impl(tensorflow::Env*, tensorflow::ThreadOptions const&, std::string const&, int)::{lambda()#1}::operator()() const ()
#3  0x00badec2 in std::_Function_handler<void (), tensorflow::thread::ThreadPool::Impl::Impl(tensorflow::Env*, tensorflow::ThreadOptions const&, std::string const&, int)::{lambda()#1}>::_M_invoke(std::_Any_data const&) ()
#4  0x0029aaf4 in std::function<void ()>::operator()() const ()
#5  0x00b53e1e in _ZNSt12_Bind_simpleIFSt8functionIFvvEEvEE9_M_invokeIJEEEvSt12_Index_tupleIJXspT_EEE ()
#6  0x00b53d90 in std::_Bind_simple<std::function<void ()> ()>::operator()() ()
#7  0x00b53d4a in std::thread::_Impl<std::_Bind_simple<std::function<void ()> ()> >::_M_run() ()
#8  0x76f91848 in ?? () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
#9  0x76f91848 in ?? () from /usr/lib/arm-linux-gnueabihf/libstdc++.so.6
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

Thread 1 (Thread 0x76ff6000 (LWP 6010)):
#0  0x76dfc61c in ?? () from /lib/arm-linux-gnueabihf/libc.so.6
#1  0x76fff048 in ?? () from /lib/ld-linux-armhf.so.3
Cannot access memory at address 0x158
#2  0x76fff048 in ?? () from /lib/ld-linux-armhf.so.3
Cannot access memory at address 0x158
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
Pete Warden
la source
1
Votre code est-il 32 bits ou 64 bits? Moi aussi, j'ai un projet que je veux faire fonctionner mais j'obtiens un vidage similaire "Impossible d'accéder à la mémoire ....." Nous l'avons tracé à une incompatibilité d'environnement 32 bits.
Dan V
2
Tout comme une mise à jour, j'ai fini par abandonner la compilation croisée, car elle semble moins bien utilisée que la compilation native et il était plus difficile de déboguer des problèmes comme celui-ci.
Pete Warden

Réponses:

3

Le moyen le plus simple pour une compilation croisée compatible binaire est d'installer la chaîne d'outils utilisée par les développeurs Raspbian. Il peut être trouvé ici . Il est essentiel d'utiliser cette chaîne d'outils si vous souhaitez créer le noyau et les pilotes, car les objets du noyau nécessitent une compatibilité ABI parfaite, mais une compatibilité parfaite ne nuira pas si vous créez également des binaires de l'espace utilisateur.

Selon la documentation , cette chaîne d'outils est compatible avec Ubuntu actuel, à la fois 32 bits et 64 bits.

Dmitry Grigoryev
la source
3

J'obtenais une pure virtual method calledexception lors de la compilation croisée. @ La réponse de JeremyBarnes n'a pas vraiment fonctionné pour moi. Au lieu de cela, j'ai utilisé:

-U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 -U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 -U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8

Explication :

Comme l'a souligné @JeremyBarnes, pour garantir la compatibilité ABI de votre application avec le stdc ++ installé, ils doivent tous les deux être compilés avec les mêmes SYNCindicateurs.

Sur Raspbian:

$ g++ -dM -E - < /dev/null | grep SYNC
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1

Sans le correctif dockcross/linux-armv6et dockcross/linux-armv7:

$ g++ -dM -E - < /dev/null | grep SYNC
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1

Avec le correctif activé dockcross/linux-armv6et dockcross/linux-armv7:

$ g++ -U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 -U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2  -E - < /dev/null | grep SYNC
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
user1202136
la source
2

FWIW, cela peut être corrigé en ajoutant -D__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 -D__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 -D__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8aux drapeaux du compilateur.

Pourquoi? Dans /usr/include/c++/4.{8,9}/bits/concurrency.h, la stratégie de verrouillage par défaut dépend de ces définitions:

#if (défini (__ GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) \
     && défini (__ GCC_HAVE_SYNC_COMPARE_AND_SWAP_4))

L'ABI d'un pointeur partagé dépend de la façon dont ces indicateurs sont définis, car il hérite d'une classe de base qui utilise un argument de modèle par défaut pour la stratégie de verrouillage. Par conséquent, la modification de ces indicateurs modifie la disposition (car elle modifie la disposition de la classe de base) des objets std :: shared_ptr <...> dans la bibliothèque C ++ standard.

Dans le compilateur fourni avec le Pi, avec lequel Raspbian a été construit, ils sont définis comme suit:

g ++ -dM -E - </ dev / null | grep SYNC
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1

C'est raisonnable pour le Pi 1, mais c'est une grande honte pour le Pi 3 qui peut très bien utiliser des pointeurs partagés atomiques.

Sur Ubuntu, ils sont configurés comme ceci:

arm-linux-gnueabihf-g ++ -dM -E - </ dev / null | grep SYNC
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1

Les drapeaux de ligne de commande ci-dessus les réinitialisent comme ils sont par défaut sur le Pi.

La compilation croisée en vaut la peine; Tensorflow est déjà lent à s'appuyer sur un serveur costaud; doit prendre un temps incroyablement long pour s'appuyer sur le Pi!

Jeremy Barnes
la source