在ReactOS下获取Windows的Git

祝大家有美好的一天! 图片


我叫Stanislav,我喜欢写代码。 这是我关于哈布雷的第一篇文章,其中有几个因素促使我写了以下文章:



让我向您介绍这次庆祝活动的英雄(阻止在ReactOS中启动Git的固定错误)-法国开发人员HermèsBélusca-Maïto(以下简称为Hermes,绰号为hbelusca ),实际上是我(昵称为x86corez )。


故事始于ReactOS开发人员IRC频道的以下消息:


 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). 

汇报


由于ReactOS当前旨在与Windows Server 2003兼容,因此选择了Git版本2.10.0作为实验兔子, 最后一个声称支持Windows XP和2003的兔子。


测试是在ReactOS命令行上完成的,问题的外部症状颇为复杂。 例如,在不指定其他参数的情况下启动git时,在控制台中显示帮助信息没有任何问题。 但是值得尝试git clone或至少git --version ,在大多数情况下,控制台根本不显示任何内容,或者偶尔显示带有断言的错误消息:


 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. 

调查git客户端是开源的问题非常有帮助,并且发现发生异常的行并不困难: 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); } 

argv0_path变量的值argv0_path通过以下代码部分设置的:


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

在澄清了这些细节之后,我取消了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! 

变量名称实际上暗示了该值是从argv[0] -通常在此数组的零索引中包含一个字符串,该字符串具有调用当前程序所用命令的名称。 但是在将来,一切都不会那么明显...


 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) 

行动起来


之后,我决定从源代码编译git,这样可以更轻松地“即时”在控制台中直接显示感兴趣的变量的值。 我选择了此分步手册 ,并构建了一个兼容版本,我选择了此分支: https : //github.com/git-for-windows/git/tree/v2.10.0-rc2


设置mingw32工具链没有问题,我开始构建。 但是,这些分步的手册通常存在未记录的陷阱,我立即遇到其中之一:


图片


通过反复试验,以及使用听众的提示(IRC频道),解决了所有编译问题。 如果有人想重复我的路,我可以分享一个成功构建的差异: https : //pastebin.com/ZiA9MaKt


为了消除初始化过程中许多功能的影响,我决定在main()函数的开头显示一些调试消息,对于git而言,它位于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); } 

结论如下:


 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 ! ( ,    ) 

看起来一切都很好, argv[0]应该是这样。 想到了在调试器中运行git的想法,例如在OllyDbg中运行,但是出了点问题...


 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 

在这里, sanchaez提出了一个很好的主意,这个主意很多!


图片


不再有例外,并且git成功打印了其版本号。


 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. 

事情开始了,我决定以其他方式尝试在命令行上运行git,但我没有输!


图片


问题很明显是git在命令行中期望完整路径。 然后,我决定检查Windows中将显示哪些数据。 结果让我有些惊讶。


图片


由于某些原因,变量argv[0]具有应用程序的完整路径。


 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建议检查ReactOS命令行上的某处是否有问题...


图片


但是事实并非如此。 cmd shell选项消失。


 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. 

现在剩下的只是测试来自ReactOS for Windows的msvcrt.dll库。 我试图将文件放在git.exe所在的目录中,但这无济于事。 马克建议使用.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 

但是由于某种原因,该选项也无法正常工作。 也许我对Windows Server 2008 R2的服务器版本进行了所有实验的事实受到了影响。


爱马仕提出了最后一个想法:


 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! 

使用WinHex,我将msvcrt文件中所有出现的msvcrt子字符串msvcrtmsvcrd ,并相应地从ReactOS重命名了msvcrt.dll,这就是发生的情况:


图片


 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) 

现在在Windows上,我们得到了相同的错误消息! 这意味着问题的根源是ReactOS的msvcrt函数之一的实现。


还应注意,在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 

仅需找出msvcrt中的哪个函数可以提供正在运行的应用程序的可执行文件的完整路径。 _pgmptr搜索一下,我建议要点在_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发送了一个指向补丁的链接,我手动应用了它并重建了系统,然后一切都神奇地工作了!


图片


 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!!! 

后记


因此,通过集体努力解决了另一个间接阻碍ReactOS在ReactOS内部自组装的错误。 一个有趣的巧合是,不久之前,另一个错误已在同一msvcrt中修复(即在qsort函数中),该错误不允许在ReactOS中组装USB驱动程序。


我参与了许多使用不同编程语言编写的项目的开发,包括封闭和开放源代码。 自2014年以来,我一直在从事ReactOS项目的工作,但是直到2017年,我才开始积极地帮助和编写代码。在这方面的工作特别有趣,因为它是整个操作系统! 人们感到付出了巨大的努力,付出了巨大的努力,也感到一种虫子变少了的愉悦感! :)


也许有人会想知道为什么我要帮助ReactOS,而不是Linux。 从历史上看,在大多数情况下,我都为Windows编写程序,而我最喜欢的编程语言是Delphi。 也许这就是为什么Windows NT以及Win32 API的体系结构对我来说非常有趣的原因,而免费操作系统ReactOS的项目使梦想成真-在实践中,它使您可以从内部了解所有工作原理。


希望您觉得在这里阅读我的第一篇文章很有趣。 我期待您的评论!


参考文献


Source: https://habr.com/ru/post/zh-CN414947/


All Articles