Заводим Git for Windows под ReactOS +64



Всем доброго времени суток! image


Меня зовут Станислав, и я люблю писать код. Это моя первая статья на Хабре, на написание которой меня сподвигло несколько факторов:


  • Недостаток статей технического плана в хабе ReactOS
  • Недавнее возвращение Geektimes на Хабр
  • Возможность собрать ReactOS в ReactOS
  • Довольно интересный случай исправления проблемы в ReactOS, в котором я принимал непосредственное участие

Позвольте представить вам виновников данного торжества (исправленного бага, который мешал запуску Git в ReactOS) — французский разработчик Hermes Belusca-Maito (далее просто Гермес, с ником hbelusca), и собственно я (с ником x86corez).


История начинается со следующих сообщений из IRC-канала разработчиков 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).

Разбор полётов


Поскольку на данный момент ReactOS нацелен на совместимость с Windows Server 2003, в качестве подопытного кролика был выбран Git версии 2.10.0 — последней, в которой заявлена поддержка Windows XP и 2003.


Тестирование осуществлялось в командной строке ReactOS, внешние симптомы проблемы были довольно неоднозначными. Например, при запуске git без указания дополнительных аргументов, он без каких-либо проблем выводил справочную информацию в консоль. Но стоило попробовать git clone или хотя бы git --version, в большинстве случаев консоль совсем ничего не выводила, либо изредка показывала битое сообщение с assertion:


git.exe clone -v "https://github.com/minoca/os.git" "C:\Documents and Settings\Administrator\Bureau\minocaos"

A s s e r t i o n   f a i l e d ! 

P r o g r a m :   C : \ P r o g r a m   F i l e s \ G i t \ m i n g w 3 2 \ b i n \ g i t . e x e 
F i l e :   e x e c _ c m d . c ,   L i n e   2 3 
E x p r e s s i o n :   a r g v 0 _ p a t h 

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)
{
    /* вырезано */

#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 задавалось данным участком кода:


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/shears/v2.10.0-rc2


Настройка тулчейна mingw32 прошла без проблем, и я запустил сборку. Но в подобных пошаговых мануалах зачастую есть недокументированные подводные камни, и на один из них я сразу же наткнулся:


image


Путём проб и ошибок, а также пользуясь подсказками из зала (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 s s e r t i o n   f a i l e d ! 
(часть вырезана, совпадает с ранее приведённой)

Казалось бы, всё нормально, 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 подсказал отличную идею, которая пролила свет на многое!


image


Исключения больше не возникало, и 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 в командной строке, и не прогадал!


image


Проблема явно была в том, что git ожидал полный путь в командной строке. Тогда я решил проверить, какие данные будут выведены в Windows. Результаты меня немного удивили.


image


В переменной 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 :)

Гермес предложил проверить, может быть проблема где-то в командной строке ReactOS...


image


Но это оказалось не так. Вариант с оболочкой cmd отпадает.


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.

Теперь оставалось лишь протестировать библиотеку msvcrt.dll из ReactOS под Windows. Я попробовал положить файл в той же директории, где лежал 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 в файле git.exe на msvcrd, и переименовал msvcrt.dll от ReactOS соответствующим образом, и вот что получилось:


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)

Теперь уже в Windows мы получили то же самое сообщение об ошибке! А это означает, что источник проблемы в реализации одной из функций msvcrt из ReactOS.


Можно также отметить, что под 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.


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

Гермес прислал ссылку на патч, я вручную применил его и пересобрал систему, после чего всё магическим образом заработало как надо!


image


Jun 06 00:34:26 <x86corez> hbelusca, IT WORKS!
Jun 06 00:35:10 <hbelusca> L O L
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), который не позволял собрать драйверы USB в ReactOS.


Я участвую в разработке многих проектов, написанных на разных языках программирования, как закрытых, так и с открытым исходным кодом. С проектом ReactOS я сотрудничаю с 2014 года, но активно помогать и писать код начал только в 2017. Работать в этой области особенно интересно, потому что это целая операционная система! Чувствуется огромный масштаб результата, в который были вложены усилия, а также приятное ощущение, что одним багом стало меньше! :)


Кто-то наверняка задастся вопросом, почему я помогаю именно ReactOS, а не Linux например. Так исторически сложилось, что в большинстве случаев я пишу программы для Windows, а мой любимый язык программирования — Delphi. Возможно именно поэтому архитектура Windows NT вместе с Win32 API мне очень интересна, а проект свободной операционной системы ReactOS воплощает в реальность давнюю мечту — на практике позволяет узнать, как всё это устроено изнутри.


Надеюсь, вам было интересно прочитать мою первую статью здесь. С нетерпением жду ваших комментариев!


Ссылки


Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (34):

  1. LynXzp
    /#18810567

    Delphi, ReactOS, IRC… закопайте стюардессу, ну хотя бы одну.
    Я шучу.., но в каждой шутке есть доля правды.

    • Error1024
      /#18810577 / +1

      Несмотря на все потуги хипстеров эти проекты продолжают жить и приносить пользу людям

      • LynXzp
        /#18810585 / -1

        Когда Delphi и IRC шли в строю, тогда те кто ничего не хотел изучать использовали дос и ассемблер называя остальных аналогом слова хипстер. (У меня такой знакомый «программист» ушел в 40 лет из программистов в эникейщики.) Я сам тоже консерватор и Delphi держался долго, но ничто не вечно под луной.

        • Yardanico
          /#18811303

          Насчёт IRC — ему пока что нет ни одного аналога с таким же количеством разных клиентов и серверов. И да, он до сих пор используется большей частью разработчиков

          • LynXzp
            /#18811369

            Все мы прекрасно понимаем почему им могут пользоваться, и почему он безнадежно устарел. Но не количество клиентов важный критерий. (Потому кстати и клиентов много что он очень простой как ftp, но вот последний недавно «забанили» в firefox и chrome.)

            • Areso
              /#18811941

              FF и Chrome рано или поздно забанят и http без сертификатов.

              • LynXzp
                /#18812091

                Сомневаюсь, как они сами говорили, это избавление от не нужного кодового груза.
                И это совсем не похоже на заботу о безопасности пользователей. FTP ничем не хуже HTTP на страницах с HTTPS. Кроме того есть FTPS, но никого не волнует. С учетом этого не факт что http запретят, просто будут ставить красный замочек рядом и все (что правильно по крайней мере для сайтов с полями ввода). К тому же как на счет локалхостов? Множество программ имеют веб интерфейсы, поднятые на коленке dev версии сайтов, а просто сохраненные страницы? Не, можно поднять https на localhost, но это мало кому нужное приключение.

                • Areso
                  /#18812195

                  Т.е. из кодовой базы в миллионы строк они решили выпилить небольшой кусочек (и при том — функциональный), типа чистку произвели? Ну ок.

                • VADemon
                  /#18814189

                  FTP не забанили (ещё), а запретили вставку элементов находящихся на ftp:// в страницы.
                  Т.е. открывать ftp-серверы в Firefox по прежнему можно, клиент никуда не делся.


                  https://bugzilla.mozilla.org/show_bug.cgi?id=1404744

                  • LynXzp
                    /#18814197

                    Ну я написал в кавычках, но обещают совсем убрать поддержку. Более того не запретили вставку элементов, а добавили опцию в настройках запрещающую вставку ftp элементов, по умолчанию выключенную.

          • DMGarikk
            /#18812619

            Время IRC уже давно прошло и популярность его падает, сейчас в сети IRC 390тысяч пользователей вообще по всему миру, много это или мало? в начале 2000х только в десятке крупнейших сетей было больше 200тыр пользователей (в каждой из)

            и про «большую часть» — это очень интересная статистика

        • Keremet_2030
          /#18812379

          Вообще-то Delphi живее всех живых. В последней Rad Studio можно написать приложение за пол часа, не написав при это ни одной строчки кода, используя LiveBindings, и откомпелировать для Andriod, iOS, OSx, Windows и Linux

          • DMGarikk
            /#18812593

            Я это слышу еще со времен VB4 (а то мож и раньше, но я с VB4 столкнулся с первым и там были такие слова что «скоро код будет писать не нужно!!»), а тут вы предлагаете как аргумент в 2018 году то что в 1995 году считалось «вау» функцией… только вот код всеравно все пишут

          • nckma
            /#18813383 / -2

            Сколько пробовал этот RAD — ничего глючней не видел.

      • VioletGiraffe
        /#18812977 / -1

        C одной стороны, вы правы. И для своего времени это были прекраксные иснтрументы. Но сейчас есть лучшие, и более динамично развивающиеся. И только по инерции матёрые (читай: старой школы) айтишники продолжают пользоваться старьём, только продлевая его агонию и плодя зоопарк технологий. Я бы здесь ещё поставил первым пунктом списка на закапывание исчадие ада под названием mailing lists (простите, наболело).

      • Paskin
        /#18814037

        В некоторых случаях — таки к сожалению. Мы работаем со всяким нестандартным оборудованием типа сенсоров веса, дальнобойных RFID-ридеров и т.п. Конфигураторы для этого добра китайцы почему-то пишут на Дельфи (или нам просто везет в последнее время). Если код на .NET обычно реверсится за час-два и к концу дня уже рожается нормальный command-line конфигуратор — то с Дельфи приходится вспоминать Ассемблер на котором я не писал лет 25.

        • Error1024
          /#18814073

          Ну это проблема не Delphi, а любого компилируемого языка, хоть Delphi, хоть C++, Rust, C и т.д.
          Напротив — хорошо что Delphi, много информации о типах содержится в rtti, формы доступны в исходниках, названия функций и т.д.

          • x86corez
            /#18814085

            {$RTTI EXPLICIT METHODS([vcPublished]) PROPERTIES([vcPublished]) FIELDS([vcPublished])}

            И всё, данных RTTI в бинарнике больше нет. :)

            • Error1024
              /#18814093

              Безусловно можно и вырезать, но во первых все не вырезает, во вторых мало кто заморачивается с этим. Особенно учитывая то, что многие прикручивают отправку сообщения об ошибках и падениях, с развернутым стеком, и т.д. где информация о типах полезна.

  2. zanac
    /#18810969

    Круто!

    • zanac
      /#18811029 / +2

      А где код с ошибкой? Или хотя бы ссылка на коммит? Финал драмы испорчен)

  3. virtu
    /#18811067 / +2

    Хорошо описано, читается как детектив

    • JEAN17RUS
      /#18812363

      Чтобы статья стала полноценным детективом, надо было написать ещё про Ватсона :D

  4. VBKesha
    /#18811761

    Всегда интересно такое почитать.

    • Jeditobe
      /#18812305

      Скоро будет еще отчет от разработчика загрузичка системы с поддержкой BTRFS

  5. Severus54
    /#18812371

    Весьма познавательно, спасибо! А это в мемориз: "недокументированные подводные камни" )))

  6. easty
    /#18813585

    Тоже мой любимый ЯП делфятина. Но если цепляться за него, можно остаться без работы). Приходится жабить на жабе в мире кровавого интерпрайза)) хотя на делфи с удовольствием беру править г@вно проекты на фрилансе, особо денег не приносит, типа хобби) спасибо за статью.

  7. berez
    /#18813701

    Так что там за глюк-то был с выводом текста ошибки? Или было решено победить его индусским методом (раз баг случается в таком-то случае — значит, меняем условия так, чтобы этот случай в этом месте не возникал)? :)

    • x86corez
      /#18813751 / +1

      С этим глюком пока никто не разбирался, по-моему ещё даже тикет в JIRA не открыли… Поскольку проблема с git разрешилась, на эту второстепенную проблему похоже забили.

      Там при возникновении исключения выводы из stdout и stderr как-то по-хитрому смешиваются между собой, и одновременно выводятся в консоль.

      Впрочем, я ведь могу собрать git, добавив assert(false); например… так и сделаю!

    • Fails
      /#18814007

      Похоже на то, что строка «assertion failed ...» является двухбайтовой (типа wchar_t), и пробелы соответствуют нулевым байтам каждого двухбайтового символа

      • x86corez
        /#18814089

        Возможно где-то есть недостающая (или лишняя) перекодировка в UCS-2.

  8. Nomad1
    /#18814039

    По-французски Hermes это разве не Эрме? Так же как Эркюль Пуаро совсем не Геркулес.

    • x86corez
      /#18814075

      Честно говоря, не знаю как правильно, т.к. из не родных языков знаю только английский. В русскоязычной части сообщества ReactOS Гермеса всегда называли именно так. :)