Obtenir Git pour Windows sous ReactOS

Bonne journée à tous! image


Je m'appelle Stanislav et j'aime écrire du code. Ceci est mon premier article sur Habré, pour lequel plusieurs facteurs m'ont poussé à écrire:



Permettez-moi de vous présenter les héros de cette célébration (un bug corrigé qui empêchait le lancement de Git dans ReactOS) - le développeur français Hermès Bélusca-Maïto (ci-après simplement Hermès, avec le surnom hbelusca ), et en fait moi (avec le surnom x86corez ).


L'histoire commence avec les messages suivants du canal IRC des développeurs ReactOS:


 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 ReactOS vise actuellement la compatibilité avec Windows Server 2003, Git version 2.10.0 a été choisi comme lapin expérimental, le dernier qui prétend prendre en charge Windows XP et 2003.


Les tests ont été effectués sur la ligne de commande ReactOS, les symptômes externes du problème étaient plutôt mitigés. Par exemple, lorsque vous démarrez git sans spécifier d'arguments supplémentaires, il affiche sans problème les informations d'aide dans la console. Mais cela valait la peine d'essayer git clone ou du moins git --version , dans la plupart des cas, la console n'affichait rien du tout, ou affichait parfois un message cassé avec 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. 

Il a été très utile d'étudier le problème selon lequel le client git était open source et trouver la ligne sur laquelle l'exception s'est produite n'a pas été difficile: https://github.com/git-for-windows/git/blob/4cde6287b84b8f4c5ccb4062617851a2f3d7fc78/exec_cm. c # L23


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

Et la valeur de la variable argv0_path définie par cette section de 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; } 

Après avoir clarifié ces détails, je me suis désabonné sur le 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, pour ainsi dire, indique que la valeur a été obtenue à partir d' argv[0] - généralement dans l'index zéro de ce tableau contient une chaîne avec le nom de la commande avec laquelle le programme en cours a été appelé. Mais à l'avenir, 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) 

Passez à l'action


Après cela, j'ai décidé de compiler git à partir de la source afin qu'il soit plus facile d'afficher «à la volée» les valeurs des variables d'intérêt directement dans la console. J'ai sélectionné ce manuel étape par étape , et pour créer une version compatible, j'ai choisi 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 déroulée sans problème et j'ai commencé la génération. Mais de tels manuels pas à pas comportent souvent des pièges non documentés, et j'ai immédiatement rencontré l'un d'eux:


image


Par essais et erreurs, ainsi qu'en utilisant les invites du public (canal IRC), tous les problèmes de compilation ont été résolus. Si quelqu'un veut répéter mon chemin, je partage le diff prêt pour une construction réussie: https://pastebin.com/ZiA9MaKt


Afin d'éliminer l'influence de nombreuses fonctions lors de l'initialisation, j'ai décidé d'afficher plusieurs messages de débogage juste au début de la fonction main() , qui dans le cas de git se trouve dans le fichier common-main.c :


 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); } 

La conclusion était la 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 ! ( ,    ) 

Il semblerait que tout va bien, argv[0] devrait être comme ça. L'idée est venue d'exécuter git dans le débogueur, par exemple dans OllyDbg, 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 beaucoup éclairé!


image


Il n'y avait plus d'exceptions, et git a réussi à imprimer son numéro de 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. 

Les choses ont décollé, et j'ai décidé d'essayer différemment d'exécuter git sur la ligne de commande, et je n'ai pas perdu!


image


Le problème était clairement que git attendait le chemin complet sur la ligne de commande. J'ai alors décidé de vérifier quelles données seront affichées dans Windows. Les résultats m'ont un peu surpris.


image


Pour une raison quelconque, la variable argv[0] avait le chemin d'accès complet à l'application.


 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 s'il pouvait y avoir un problème quelque part sur la ligne de commande ReactOS ...


image


Mais ce n'était pas le cas. L'option shell cmd disparaît.


 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. 

Il ne restait plus qu'à tester la bibliothèque msvcrt.dll de ReactOS pour Windows. J'ai essayé de mettre le fichier dans le même répertoire où git.exe se trouvait, mais cela n'a pas aidé. Mark a suggéré un moyen avec 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 option n'a pas fonctionné non plus. Peut-être que le fait d'avoir mené toutes les expériences sur la version serveur de Windows Server 2008 R2 a été affecté.


Hermès a suggéré la dernière idée:


 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! 

En utilisant WinHex, j'ai remplacé toutes les occurrences de la sous-chaîne msvcrt dans le fichier msvcrd par msvcrd , et renommé msvcrt.dll de ReactOS en conséquence, et c'est ce qui s'est produit:


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) 

Maintenant, sous Windows, nous avons le même message d'erreur! Cela signifie que la source du problème est l'implémentation d'une des fonctions msvcrt de ReactOS.


Il peut également être noté que sous Windows, le texte d'exception s'affiche correctement.


 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 

Il ne restait plus qu'à savoir quelle fonction dans msvcrt fournit le chemin complet vers le fichier exécutable de l'application en cours d'exécution. Googler un peu, j'ai suggéré que le point est dans la 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, après quoi tout a fonctionné 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


Ainsi, un autre bug qui gênait indirectement l'auto-assemblage de ReactOS à l'intérieur de ReactOS a été corrigé grâce à des efforts collectifs. Une drôle de coïncidence est le fait que peu de temps avant, un autre bug a été corrigé dans le même msvcrt (à savoir, dans la fonction qsort ), qui ne permettait pas d'assembler 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 travaille avec le 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! On sent l'ampleur énorme du résultat, dans lequel des efforts ont été investis, ainsi que le sentiment agréable qu'un bug est devenu moins! :)


Quelqu'un se demandera probablement pourquoi j'aide ReactOS, et non Linux par exemple. Il est arrivé historiquement que 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 pourquoi l'architecture de Windows NT, avec l'API Win32, est très intéressante pour moi, et le projet du système d'exploitation gratuit ReactOS fait revivre un vieux rêve - en pratique, il vous permet de découvrir comment tout cela fonctionne de l'intérieur.


J'espère que vous avez trouvé intéressant de lire mon premier article ici. J'attends vos commentaires avec impatience!


Les références


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


All Articles