Obtendo o Git para Windows no ReactOS

Bom dia a todos! imagem


Meu nome é Stanislav e eu gosto de escrever código. Este é o meu primeiro artigo sobre Habré, para o qual vários fatores me levaram a escrever:



Deixe-me apresentar os heróis desta celebração (um bug corrigido que impediu o lançamento do Git no ReactOS) - o desenvolvedor francês Hermès Bélusca-Maïto (daqui em diante simplesmente Hermes, com o apelido hbelusca ) e, na verdade, eu (com o apelido x86corez ).


A história começa com as seguintes mensagens do canal IRC para desenvolvedores do 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). 

Debriefing


Como o ReactOS atualmente visa a compatibilidade com o Windows Server 2003, o Git versão 2.10.0 foi escolhido como o coelho experimental, o último que reivindica suporte para o Windows XP e 2003.


O teste foi realizado na linha de comando do ReactOS, os sintomas externos do problema foram bastante variados. Por exemplo, ao iniciar o git sem especificar argumentos adicionais, ele sem problemas exibe informações de ajuda no console. Mas valia a pena tentar o git clone ou pelo menos git --version , na maioria dos casos o console não exibia nada, ou ocasionalmente mostrava uma mensagem quebrada com asserção:


 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. 

Foi muito útil investigar o problema de que o cliente git era de código aberto e não foi difícil encontrar a linha na qual a exceção ocorreu: 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); } 

E o valor da variável argv0_path definido por esta seção do código:


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

Depois de esclarecer esses detalhes, cancelei a inscrição no canal de 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! 

O nome da variável sugere que o valor foi obtido de argv[0] - geralmente no índice zero desta matriz contém uma sequência com o nome do comando com o qual o programa atual foi chamado. Mas no futuro, tudo não era tão óbvio ...


 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) 

Ir para a ação


Depois disso, decidi compilar o git a partir da fonte, para que fosse mais fácil exibir "on the fly" os valores das variáveis ​​de interesse diretamente no console. Selecionei este manual passo a passo e, para criar uma versão compatível, escolhi este ramo: https://github.com/git-for-windows/git/tree/v2.10.0-rc2


A configuração da cadeia de ferramentas mingw32 ocorreu sem problemas e eu iniciei a compilação. Mas esses manuais passo a passo geralmente têm armadilhas não documentadas, e eu imediatamente me deparei com um deles:


imagem


Por tentativa e erro, além de usar avisos da audiência (canal IRC), todos os problemas de compilação foram resolvidos. Se alguém quiser repetir meu caminho, eu compartilho o diff pronto para uma compilação bem-sucedida: https://pastebin.com/ZiA9MaKt


Para eliminar a influência de muitas funções durante a inicialização, decidi exibir várias mensagens de depuração logo no início da função main() , que no caso do git está localizada no arquivo 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); } 

A conclusão foi a seguinte:


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

Parece que está tudo bem, argv[0] deve ser assim. A ideia surgiu para rodar o git dentro do depurador, por exemplo, no OllyDbg, mas algo deu errado ...


 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 

E aqui sanchaez sugeriu uma ótima idéia que esclareceu muito!


imagem


Não havia mais exceções, e o git imprimiu com sucesso seu número de versão.


 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. 

As coisas decolaram e eu decidi tentar de forma diferente rodar o git na linha de comando, e não perdi!


imagem


O problema era claramente que o git esperava o caminho completo na linha de comando. Decidi verificar quais dados serão exibidos no Windows. Os resultados me surpreenderam um pouco.


imagem


Por alguma razão, a variável argv[0] tinha o caminho completo para o aplicativo.


 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 sugeriu verificar se há algum problema em algum lugar na linha de comando do ReactOS ...


imagem


Mas não foi esse o caso. A opção cmd shell desaparece.


 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. 

Agora, tudo o que restava era testar a biblioteca msvcrt.dll do ReactOS para Windows. Tentei colocar o arquivo no mesmo diretório em que o git.exe estava, mas isso não ajudou. Mark sugeriu uma maneira com um arquivo .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 

Mas, por algum motivo, essa opção também não funcionou. Talvez o fato de eu ter realizado todos os experimentos na versão do servidor do Windows Server 2008 R2 tenha sido afetado.


Hermes sugeriu a última ideia:


 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! 

Usando o WinHex, substituí todas as ocorrências da substring msvcrt no arquivo msvcrd por msvcrd e msvcrt o msvcrt do ReactOS de acordo. msvcrd que aconteceu:


imagem


 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) 

Agora no Windows, recebemos a mesma mensagem de erro! Isso significa que a origem do problema é a implementação de uma das funções msvcrt do ReactOS.


Também é possível observar que no Windows o texto da exceção é exibido corretamente.


 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 

Restava apenas descobrir qual função no msvcrt fornece o caminho completo para o arquivo executável do aplicativo em execução. Pesquisando um pouco, sugeri que o ponto está na função _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 enviou um link para o patch, eu o apliquei manualmente e reconstruí o sistema, após o qual tudo funcionou como deveria!


imagem


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

Posfácio


Assim, outro bug que dificultava indiretamente a auto-montagem do ReactOS dentro do ReactOS foi corrigido por meio de esforços coletivos. Uma coincidência engraçada é o fato de que pouco antes desse outro bug foi corrigido no mesmo msvcrt (a saber, na função qsort ), que não permitia a montagem de drivers USB no ReactOS.


Participo do desenvolvimento de muitos projetos escritos em diferentes linguagens de programação, tanto de código aberto quanto de código aberto. Trabalho com o projeto ReactOS desde 2014, mas comecei a ajudar e escrever código ativamente apenas em 2017. É especialmente interessante trabalhar nessa área, porque é um sistema operacional inteiro! Sente-se a enorme escala do resultado, na qual foram investidos esforços, bem como a agradável sensação de que um bug se tornou menor! :)


Alguém provavelmente se perguntará por que ajudo o ReactOS, e não o Linux, por exemplo. Aconteceu historicamente que na maioria dos casos eu escrevo programas para Windows, e minha linguagem de programação favorita é o Delphi. Talvez seja por isso que a arquitetura do Windows NT, juntamente com a API do Win32, seja muito interessante para mim, e o projeto do sistema operacional gratuito ReactOS traz à vida um sonho antigo - na prática, permite descobrir como tudo funciona por dentro.


Espero que você tenha achado interessante ler meu primeiro artigo aqui. Aguardo seus comentários!


Referências


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


All Articles