Faire fonctionner Git pour Windows dans ReactOS

Bonne journée à toi! image


Je m'appelle Stanislav et j'aime écrire du code. Ceci est mon premier article en anglais sur Habr que j'ai fait pour plusieurs raisons:



Cet article est une version anglaise de mon tout premier article sur le russe.


Permettez-moi de vous présenter les personnages principaux de cette histoire qui ont corrigé le bug empêchant Git de fonctionner dans ReactOS - le développeur français Hermès Bélusca-Maïto (ou tout simplement Hermès avec le surnom hbelusca ) et bien sûr moi (avec le surnom x86corez ).


L'histoire commence avec les messages suivants du canal IRC de ReactOS Development:


 Jun 03 18:52:56 <hbelusca> Anybody want to work on some small problem? If so, can someone figure out why this problem https://jira.reactos.org/browse/CORE-12931 happens on ReactOS? :D Jun 03 18:53:13 <hbelusca> That would help having a good ROS self-hosting system with git support. Jun 03 18:53:34 <hbelusca> (the git assertion part only). 

Débriefing


Étant donné que la plate-forme cible ReactOS est Windows Server 2003, Git version 2.10.0 a été choisie pour l'enquête - c'est la dernière prenant en charge Windows XP et 2003.


Les tests ont été effectués dans l'invite de commandes ReactOS, les symptômes externes du problème étaient plutôt ambigus. Par exemple, lorsque vous exécutez git sans paramètres supplémentaires, il affiche le message d'aide dans la console sans aucun problème. Mais une fois que vous essayez git clone ou même juste git --version dans la plupart des cas, la console reste vide. Parfois, il affiche un message cassé avec une assertion:


 git.exe clone -v "https://github.com/minoca/os.git" "C:\Documents and Settings\Administrator\Bureau\minocaos" A ssertionfailed ! P rogram : C : \ P rogram F iles \ G it \ mingw 3 2 \ bin \ git . exe F ile : exec _ cmd . c , L ine 2 3 E xpression : argv 0 _ path This application has requested the Runtime to terminate in an unusual way. Please contact the application's support team for more information. 

Heureusement, git est un projet open source, et cela a beaucoup aidé dans notre enquête. Il n'a pas été trop difficile de localiser le bloc de code réel où une exception non gérée s'est produite: https://github.com/git-for-windows/git/blob/4cde6287b84b8f4c5ccb4062617851a2f3d7fc78/exec_cmd.c#L23


 char *system_path(const char *path) { /* snip */ #ifdef RUNTIME_PREFIX assert(argv0_path); // it asserts here assert(is_absolute_path(argv0_path)); /* snip */ #endif strbuf_addf(&d, "%s/%s", prefix, path); return strbuf_detach(&d, NULL); } 

Et la valeur de la variable argv0_path est affectée par ce code:


 const char *git_extract_argv0_path(const char *argv0) { const char *slash; if (!argv0 || !*argv0) return NULL; slash = find_last_dir_sep(argv0); if (slash) { argv0_path = xstrndup(argv0, slash - argv0); return slash + 1; } return argv0; } 

Une fois que j'ai obtenu toutes ces informations, j'ai envoyé des messages au canal IRC:


 Jun 03 19:04:36 <x86corez> hbelusca: https://github.com/git-for-windows/git/blob/4cde6287b84b8f4c5ccb4062617851a2f3d7fc78/exec_cmd.c#L23 Jun 03 19:04:41 <x86corez> assertion is here Jun 03 19:04:57 <hbelusca> yes I know, I've seen the code yesterday. The question is why it's FALSE on ROS but TRUE on Windows. Jun 03 19:06:02 <x86corez> argv0_path = xstrndup(argv0, slash - argv0); Jun 03 19:06:22 <x86corez> xstrndup returns NULL %-) Jun 03 19:06:44 <hbelusca> ok, so what's the values of argv0 and slash on windows vs. on ROS? :P Jun 03 19:08:48 <x86corez> good question! 

Le nom de la variable suggère que la valeur réelle est dérivée de argv[0] - généralement, elle contient le nom de l'exécutable dans un index zéro. Mais par la suite, tout n'était pas si évident ...


 Jun 03 20:15:21 <x86corez> hbelusca: surprise... git uses its own xstrndup implementation Jun 03 20:15:35 <x86corez> so I can't simply hook it xD Jun 03 20:15:56 <hbelusca> well, with such a name "xstrndup" it's not surprising it's its own implementation Jun 03 20:16:04 <x86corez> probably I would need an user-mode debugger... like OllyDbg Jun 03 20:16:09 <hbelusca> that's everything but standardized function. Jun 03 20:16:24 <hbelusca> x86corez: ollydbg should work on ROS. Jun 03 20:16:30 <mjansen> what are you breaking today? Jun 03 20:16:44 <x86corez> mjansen: https://jira.reactos.org/browse/CORE-12931 Jun 03 20:16:51 <hbelusca> (of course if you also are able to compile that git with symbols and all the stuff, it would be very nice) 

Action


Après cela, j'ai décidé de compiler git directement depuis la source, donc je pourrai imprimer les variables d'intérêt "à la volée" directement sur la console. J'ai suivi ce tutoriel étape par étape et afin de compiler une version git compatible, j'ai sélectionné cette branche: https://github.com/git-for-windows/git/tree/v2.10.0-rc2


La configuration de la chaîne d'outils mingw32 s'est bien déroulée et je viens de commencer la construction. Cependant, il y a presque toujours des pièges non documentés dans ces didacticiels pas à pas, et j'ai immédiatement rencontré l'un d'eux:


image


Par essais et erreurs, ainsi qu'en utilisant des indices provenant du hall (canal IRC), toutes les erreurs de compilation ont été corrigées. Si quelqu'un veut suivre mes étapes, voici le diff pour rendre le compilateur heureux: https://pastebin.com/ZiA9MaKt


Afin d'éviter d'appeler plusieurs fonctions lors de l'initialisation et de reproduire facilement le bogue, j'ai décidé de placer plusieurs impressions de débogage au début de la fonction main() , qui se trouve dans common-main.c dans le cas de git:


 int main(int argc, const char **argv) { /* * Always open file descriptors 0/1/2 to avoid clobbering files * in die(). It also avoids messing up when the pipes are dup'ed * onto stdin/stdout/stderr in the child processes we spawn. */ //DebugBreak(); printf("sanitize_stdfds(); 1\n"); sanitize_stdfds(); printf("git_setup_gettext(); 1\n"); git_setup_gettext(); /* * Always open file descriptors 0/1/2 to avoid clobbering files * in die(). It also avoids messing up when the pipes are dup'ed * onto stdin/stdout/stderr in the child processes we spawn. */ printf("sanitize_stdfds(); 2\n"); sanitize_stdfds(); printf("git_setup_gettext(); 2\n"); git_setup_gettext(); printf("before argv[0] = %s\n", argv[0]); argv[0] = git_extract_argv0_path(argv[0]); printf("after argv[0] = %s\n", argv[0]); restore_sigpipe_to_default(); printf("restore_sigpipe_to_default(); done\n"); return cmd_main(argc, argv); } 

J'ai la sortie suivante:


 C:\>git --version sanitize_stdfds(); 1 git_setup_gettext(); 1 sanitize_stdfds(); 2 git_setup_gettext(); 2 before argv[0] = git after argv[0] = git restore_sigpipe_to_default(); done A ssertionfailed ! (cutted a part of error message which is the same as the one above) 

On peut supposer que tout va bien ici, la valeur argv[0] est correcte. J'ai une idée pour lancer git dans le débogueur, OllyDbg par exemple, mais quelque chose s'est mal passé ...


 Jun 04 01:54:46 <sanchaez> now please try gdb/ollydbg in ROS Jun 04 01:58:11 <sanchaez> you have gdb in RosBE Jun 04 01:58:20 <sanchaez> just in case :p Jun 04 01:59:45 <x86corez> ollydbg says "nope" with MEMORY_MANAGEMENT bsod Jun 04 02:00:07 <x86corez> !bc 0x0000001A Jun 04 02:00:08 <hTechBot> KeBugCheck( MEMORY_MANAGEMENT ); Jun 04 02:00:13 <hbelusca> :/ Jun 04 02:00:49 <sanchaez> welp Jun 04 02:00:56 <sanchaez> you only have one option now :D 

Et ici, Sanchaez a suggéré une excellente idée qui a mis en lumière beaucoup de choses!


image


L'assertion ne s'est plus produite et git a réussi à imprimer sa version.


 Jun 04 02:23:40 <x86corez> it prints! Jun 04 02:23:44 <x86corez> but only in gdb Jun 04 02:23:53 <hbelusca> oh Jun 04 02:24:00 <hbelusca> C:\git/git.exe Jun 04 02:24:13 <hbelusca> I wonder whether it's the same in windows, or not. 

L'affaire s'est déplacée du point mort, et j'ai essayé différentes façons d'exécuter git dans l'invite de commande, et j'ai trouvé la bonne façon!


image


Le problème était clairement que git attendait le chemin complet sur la ligne de commande. J'ai donc comparé sa sortie de débogage sous Windows. Les résultats m'ont un peu surpris.


image


Pour une raison quelconque, la valeur argv[0] contenait le chemin complet du binaire git.exe.


 Jun 05 23:01:44 <hbelusca> x86corez: can you try to run git also by not using cmd.exe? Jun 05 23:02:05 <hbelusca> (to exclude the possibility it's cmd that doesn't call Createprocess with a complete path) Jun 05 23:02:09 <hbelusca> while I think it should... Jun 05 23:02:30 <x86corez> not using cmd... moment Jun 05 23:02:55 <hbelusca> x86corez: alternatively, on windows, try starting git using our own cmd.exe :) 

Hermes a suggéré de vérifier si ReactOS cmd.exe est un composant coupable ici ...


image


Mais cette capture d'écran a confirmé que le problème réel est ailleurs.


 Jun 05 23:04:38 <x86corez> ROS cmd is not guilty Jun 05 23:07:57 <hbelusca> If there was a possibility to consult the received path, before looking at the contents of argvs... ? Jun 05 23:08:30 <x86corez> dump contents of actual command line? Jun 05 23:08:39 <hbelusca> yeah Jun 05 23:09:39 <hbelusca> The thing you retrieve using GetCommandLineW Jun 05 23:10:03 <hbelusca> (which is, after simplifications, basically : NtCurrentPeb()->ProcessParameters->CommandLine ) Jun 05 23:10:59 <hbelusca> Also I was thinking it could be a side-effect of having (or not having) git path into the env-vars.... Jun 05 23:12:17 <x86corez> hbelusca, command line is "git --version" Jun 05 23:12:34 <hbelusca> Always? Jun 05 23:12:39 <x86corez> Yes, even on Windows Jun 05 23:15:13 <hbelusca> ok but then it would be nice if these different results are at least the same on Windows and on ROS, so that we can 100% exclude problems outside of msvcrt. 

La dernière option consistait à tester ReactOS msvcrt.dll sous Windows. J'ai essayé de placer le fichier dans le même répertoire où se trouve git.exe, mais cela n'a pas aidé. Mark a suggéré d'ajouter un fichier .local:


 Jun 05 22:59:01 <mjansen> x86corez: add .local file next to msvcrt.dll ;) Jun 05 22:59:47 <mjansen> exename.exe.local Jun 05 23:00:17 <x86corez> just an empty file? Jun 05 23:00:21 <mjansen> yea Jun 05 23:00:49 <hbelusca> mjansen: do we support these .local files? Jun 05 23:00:52 <mjansen> we dont Jun 05 23:00:54 <mjansen> windows does Jun 05 23:15:48 <x86corez> moment... I'll try with .local Jun 05 23:18:43 <x86corez> mjansen: I've created git.exe.local but it still doesn't load msvcrt.dll in this directory 

Mais pour une raison quelconque, cette méthode n'a pas fonctionné non plus. Peut-être le fait que j'ai fait toutes les expériences sur l'édition serveur de Windows (2008 R2).


La dernière idée a été suggérée par Hermès:


 Jun 05 23:19:28 <hbelusca> last solution: patch "msvcrt" name within git and perhaps other mingwe dlls ^^ Jun 05 23:20:12 <x86corez> good idea about patching! 

J'ai donc remplacé toutes les occurrences de msvcrt dans git.exe par msvcrd utilisant WinHex, et renommé ReactOS msvcrt.dll en conséquence, et nous voici:


image


 Jun 05 23:23:29 <x86corez> Yes! guilty is msvcrt :) Jun 05 23:25:37 <hbelusca> ah, so as soon as git uses our msvcrt we get the problem on windows. Jun 05 23:25:38 <x86corez> hbelusca, mjansen, https://image.prntscr.com/image/FoOWnrQ4SOGMD-66DLW16Q.png Jun 05 23:25:58 <hbelusca> aha and it asserts <3 Jun 05 23:26:03 <hbelusca> (it shows the assertion now) 

bug est devenuNous avons frappé la même assertion, mais dans Windows! Et cela signifie que la source de nos problèmes se trouve dans l'une des fonctions msvcrt de ReactOS.


Il convient également de noter que le message d'assertion s'affiche correctement dans Windows.


 Jun 05 23:26:13 <x86corez> but it prints text and correctly. Jun 05 23:26:20 <hbelusca> oh Jun 05 23:26:33 <x86corez> and on ROS it doesn't print in most cases xD Jun 05 23:26:38 <hbelusca> so also it excludes another hypothesis, namely that it could have been a bug in our msvcrt/crt Jun 05 23:26:56 <hbelusca> So possibly a strange bug in our console 

Donc, pour résoudre le problème réel, nous avons dû trouver la fonction API dans msvcrt qui donne le chemin complet de l'application actuelle. J'ai googlé un peu et j'ai supposé que le problème _pgmptr fonction _pgmptr .


 Jun 06 00:07:43 <x86corez> https://msdn.microsoft.com/en-us/library/tza1y5f7.aspx Jun 06 00:07:57 <x86corez> When a program is run from the command interpreter (Cmd.exe), _pgmptr is automatically initialized to the full path of the executable file. Jun 06 00:08:01 <x86corez> this ^^) Jun 06 00:08:50 <hbelusca> That's what GetModuleFileName does. Jun 06 00:09:04 <x86corez> yeah Jun 06 00:10:30 <hbelusca> Of course in ROS msvcrt we don't do this, but instead we initialize pgmptr to what argv[0] could be. Jun 06 00:11:08 <hbelusca> That's one thing. Jun 06 00:11:34 <hbelusca> The other thing is that nowhere it appears (in MS CRT from VS, or in wine) that argv is initialized using pgmptr. Jun 06 00:13:33 <x86corez> hbelusca, I've checked argv[0] in some ROS command line tools, running them in Windows Jun 06 00:13:56 <x86corez> they all interpret argv[0] as command line, not full path Jun 06 00:14:04 <x86corez> so... I think it's git specific behaviour Jun 06 00:14:16 <x86corez> or specific mingw compiler settings Jun 06 00:28:12 <hbelusca> x86corez: I'm making a patch for our msvcrt, would be nice if you could test it :) Jun 06 00:28:21 <x86corez> I'll test it 

Hermes a envoyé un lien vers le patch, je l'ai appliqué manuellement et reconstruit le système, et après ces mouvements, le problème d'origine a disparu comme par magie!


image


 Jun 06 00:34:26 <x86corez> hbelusca, IT WORKS! Jun 06 00:35:10 <hbelusca> LOL Jun 06 00:35:18 <hbelusca> So it seems that something uses pgmptr to rebuild an argv. Jun 06 00:35:52 <x86corez> I've even able to clone :) Jun 06 00:36:19 <hbelusca> \o/ Jun 06 00:36:21 <gigaherz> 2.10.0-rc2? not the release? Jun 06 00:36:24 <hbelusca> ok I'm gonna commit that stuff. Jun 06 00:36:43 <hbelusca> x86corez: gonna have ROS self-hosting <33 Jun 06 00:36:48 <x86corez> yeah! Jun 06 00:37:01 <x86corez> gigaherz: I've built that from sources Jun 06 00:37:37 <gigaherz> oh, for testing this bug? o_O Jun 06 00:37:50 <sanchaez> yes, you missed the fun :p Jun 06 00:39:46 <x86corez> git 2.10.0-windows.1 (release) works too! Jun 06 00:39:54 <encoded> commit!!! 

Postface


Cela dit, un autre bug qui empêche indirectement ReactOS de se construire a été corrigé grâce à des efforts collectifs. La drôle de coïncidence est le fait que peu de temps avant qu'un autre bogue ne soit corrigé dans la même bibliothèque dynamique msvcrt (à savoir, dans la fonction qsort ) qui ne permettait pas de compiler les pilotes USB dans ReactOS.


Je participe au développement de nombreux projets écrits dans différents langages de programmation, fermés et open source. Je contribue au projet ReactOS depuis 2014, mais je n'ai commencé à aider activement et à écrire du code qu'en 2017. C'est particulièrement intéressant de travailler dans ce domaine car c'est un système d'exploitation complet! Vous ressentez une énorme échelle du résultat dans lequel les efforts ont été investis, ainsi qu'un sentiment agréable qu'il y a un bug de moins! :)


Quelqu'un peut se demander pourquoi je contribue à ReactOS et non à Linux par exemple. Donc, historiquement, dans la plupart des cas, j'écris des programmes pour Windows et mon langage de programmation préféré est Delphi. C'est peut-être pour cela que l'architecture de Windows NT avec l'API Win32 est très intéressante pour moi, et le projet ReactOS de l'alternative gratuite de Windows réalise le vieux rêve - il vous permet de découvrir comment tout fonctionne sous le capot dans la pratique.


J'espère que vous avez apprécié mon premier article en anglais ici. J'attends vos commentaires avec impatience!



Source: https://habr.com/ru/post/fr439580/


All Articles