祝您有美好的一天! 
我叫Stanislav,我喜欢写代码。 这是我在Habr上发表的第一篇英文文章,基于以下几个原因:
本文是我第一篇关于俄语的文章的英文版。
让我介绍这个故事中的主要人物,他们实际上修复了阻止Git在ReactOS上运行的错误-法国开发人员HermèsBélusca-Maïto(或者只是具有hbelusca
昵称的Hermes),当然还有我(具有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_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); }
并且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,这样我就可以“即时”将感兴趣的变量直接打印到控制台。 我遵循了此分步教程,并且为了编译兼容的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 ! (cutted a part of error message which is the same as the one above)
可以假设这里一切都很好, 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]
值包含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 :)
爱马仕建议在此处检查ReactOS cmd.exe是否有罪...

但是此屏幕截图确认实际问题出在其他地方。
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.
最后一个选择是在Windows中测试ReactOS 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(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
替换为msvcrt
,并相应地重命名了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中找到提供当前应用程序完整路径的API函数。 我用_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构建自身的错误已得到修复。 有趣的巧合是,不久前,另一个错误被修复在同一个msvcrt动态库(即qsort
函数)中,该库不允许在ReactOS中编译USB驱动程序。
我参与了许多使用不同编程语言编写的项目的开发,包括封闭和开放源代码。 自2014年以来,我一直在为ReactOS项目做贡献,但是直到2017年,我才开始积极提供帮助和实际编写代码。从事这一领域的工作特别有趣,因为它是整个操作系统! 您将付出巨大的努力成果,并感到减少了一个错误,这是令人愉快的感觉! :)
有人可能想知道为什么我为ReactOS而不是Linux做贡献。 因此,从历史上看,大多数情况下我都是为Windows编写程序,而我最喜欢的编程语言是Delphi。 也许这就是为什么Windows NT和Win32 API的体系结构令我非常感兴趣的原因,而免费的Windows替代品的ReactOS项目使古老的梦想成真-它使您可以了解实际情况下所有工作原理。
希望您喜欢我的第一篇英文文章。 期待您的评论!
友情链接