Fazendo o Git for Windows funcionar no ReactOS

Bom dia para você! imagem


Meu nome é Stanislav e eu gosto de escrever código. Este é o meu primeiro artigo em inglês sobre Habr que fiz devido a várias razões:



Este artigo é uma versão em inglês do meu primeiro artigo sobre russo.


Deixe-me apresentar as principais figuras nesta história que realmente corrigiram o bug que impedia o Git de executar o ReactOS - o desenvolvedor francês Hermès Bélusca-Maïto (ou apenas Hermes com apelido hbelusca ) e, claro, eu (com o apelido x86corez ).


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

Debriefing


Como a plataforma de destino do ReactOS é o Windows Server 2003, a versão 2.10.0 do Git foi escolhida para a investigação - é a última que suporta o Windows XP e 2003.


Os testes foram feitos no prompt de comando do ReactOS; os sintomas externos do problema eram bastante ambíguos. Por exemplo, quando você executa o git sem parâmetros adicionais, ele exibe a mensagem de ajuda no console sem problemas. Mas uma vez que você tenta o git clone ou até mesmo o git --version na maioria dos casos, o console permanece vazio. Ocasionalmente, exibia uma mensagem quebrada com uma afirmaçã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. 

Felizmente, o git é um projeto de código aberto e ajudou muito em nossa investigação. Não foi muito difícil localizar o bloco de código real onde ocorreu a exceção sem tratamento: 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); } 

E o valor da variável argv0_path é atribuído por este 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 obter todas essas informações, enviei algumas mensagens para o 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 real é derivado de argv[0] - geralmente ele contém o nome do executável no índice zero. Mas subsequentemente 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) 

Acção


Depois disso, decidi compilar o git diretamente da fonte, para poder imprimir variáveis ​​de interesse "on the fly" diretamente no console. Eu segui este tutorial passo a passo e, para compilar a versão git compatível, selecionei este ramo: https://github.com/git-for-windows/git/tree/v2.10.0-rc2


A configuração da cadeia de ferramentas mingw32 correu bem e eu comecei a construir. No entanto, quase sempre existem armadilhas não documentadas nesses tutoriais passo a passo, e eu imediatamente encontrei um deles:


imagem


Por tentativa e erro, além de usar dicas do hall (canal IRC), todos os erros de tempo de compilação foram corrigidos. Se alguém quiser seguir meus passos, aqui está o diff para deixar o compilador feliz: https://pastebin.com/ZiA9MaKt


Para evitar chamar várias funções durante a inicialização e reproduzir o bug facilmente, decidi colocar várias impressões de depuração logo no início da função main() , localizada em common-main.c no caso do 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); } 

Eu tenho a seguinte saída:


 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) 

Pode-se assumir que está tudo bem aqui, o valor argv[0] está correto. Eu tenho uma idéia para executar o git dentro do depurador, OllyDbg, por exemplo, 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 excelente idéia que esclareceu muitas coisas!


imagem


A asserção não ocorreu mais e o git imprimiu com sucesso sua 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. 

O caso mudou do ponto morto e tentei diferentes maneiras de executar o git no prompt de comando, e encontrei o caminho certo!


imagem


O problema era claramente que o git esperava o caminho completo na linha de comando. Então eu comparei sua saída de depuração no Windows. Os resultados me surpreenderam um pouco.


imagem


Por alguma razão, o valor argv[0] continha o caminho completo para o binário 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 sugeriu verificar se o ReactOS cmd.exe é um componente culpado aqui ...


imagem


Mas esta captura de tela confirmou que o problema real está em outro lugar.


 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. 

A última opção foi testar o ReactOS msvcrt.dll no Windows. Tentei colocar o arquivo no mesmo diretório em que o git.exe está localizado, mas não ajudou. Mark sugeriu adicionar 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 alguma razão, esse método também não funcionou. Talvez o fato de eu ter feito todos os experimentos na edição para servidor do Windows (2008 R2).


A última ideia foi sugerida por Hermes:


 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! 

Então, substituí todas as ocorrências de msvcrt no git.exe por msvcrd usando o WinHex e msvcrd ReactOS msvcrt.dll de acordo. Aqui estamos:


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) 

bug tornou-seAgora atingimos a mesma afirmação, mas no Windows! E isso significa que a fonte dos nossos problemas está na função msvcrt do ReactOS.


Também é importante observar que a mensagem de asserção é exibida corretamente no 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 

Portanto, para resolver o problema real, tivemos que encontrar a função API no msvcrt, que fornece o caminho completo do aplicativo atual. Pesquisei um pouco no Google e assumi que o problema está _pgmptr 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, e depois desses movimentos o problema original desapareceu magicamente!


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


Dito isso, outro bug que impede indiretamente o ReactOS de se construir foi corrigido graças aos esforços coletivos. A engraçada coincidência é o fato de que não muito antes de outro bug ser corrigido na mesma biblioteca dinâmica msvcrt (a saber, na função qsort ) que não permitia compilar os 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. Estou contribuindo para o projeto ReactOS desde 2014, mas comecei a ajudar ativamente e a escrever código apenas em 2017. É especialmente interessante trabalhar nessa área porque é um sistema operacional inteiro! Você sente uma enorme escala do resultado em que os esforços foram investidos, bem como uma sensação agradável de que há um erro a menos! :)


Alguém pode se perguntar por que estou contribuindo com o ReactOS e não com o Linux, por exemplo. Por isso, historicamente, na maioria dos casos, escrevo programas para Windows e minha linguagem de programação favorita é o Delphi. Talvez seja por isso que a arquitetura do Windows NT junto com a API do Win32 seja muito interessante para mim, e o projeto ReactOS da alternativa gratuita para Windows torne o velho sonho realidade - ele permite que você descubra como tudo funciona na prática.


Espero que tenham gostado do meu primeiro artigo em inglês aqui. Aguardo seus comentários!



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


All Articles