понедельник, 25 февраля 2013 г.

Shim-ы и RT

С развитием эмулятора x86 Windows на Windows RT - практически каждый день сталкиваюсь с новыми багами в эмулируемых приложениях. Причем, причина некоторых "злостных" багов - не недостатки эмулятора, а кривость рук или природная лень авторов данных приложений.
То, что программы обращаются к блоку памяти после его освобождения или возвращают указатели на строки, выделенные на стеке - это еще мелочи. К выходу за пределы блока памяти  и к тому, что некоторые программы используют исключения Access Violation в своей нормальной работе вместо проверки параметров функций - я уже привык. Но вот некоторые конструкции заставляют переделывать большие участки существующего кода, просто чтобы заставить подобные программы хоть как-то работать. В итоге все-равно получается "не красиво", так как инече просто не реализуемо при использвоании имеющихся x86 движков (это я, например, про использование SuspendThread для синхронизации потоков). Так что вопрос о создании своего х86 движка, неуниверсального, но зато "заточенного" под конкретные нужды - давно решен утвердительно. Осталось только выделить на него время.

Чем больше занимаюсь проектом - тем больше начинаю уважать микрософт в плане поддержки старого софта на новых версиях Windows. И тем больше понимаю, что для осуществления запуска x86 программ на RT банального враппера поверх "родного" API и трансляции x86 команд будет недостаточно. В RT микрософт наконец выкинуло большие куски legacy кода, в некоторых случаях оставшегося еще со времен Windows 1.0, и реализация всех этих "костылей" теперь ложится на меня. Можно просто сравнить размер shim-ов в папке C:\Windows\AppPatch на RT и на настольной Windows 8. В релизе RT есть только несколько тестовых, в то время как в настольной - папка занимает более 10 мб. К счастью, формат базы shim-ов хоть и не документирован официально, но информации по нему предостаточно, так что можно "позаимствовать" данную базу в моем проекте.

По поводу application compatibility, и вообще по историческим апектам того, почему в windows некоторые вещи реализованы именно так, а не иначе - есть неплохой блог the old new thing. Его автор и книгу написал (найти ее скан не проблема), и выложил пару бесплатных глав. Одна из таких глав как раз посвящена историям про совместимость. И, читая данную книгу, я нахожу некоторые ошибки у себя. Как пример такой ошибки - вызов программой функции GetWindowLong(hwnd,DWL_DLGPROC) совершенно не означает, что hwnd == диалог, и программа хочет получить адрес его диалоговой процедуры. Ведь DWL_DLGPROC это просто константа 4, и в случае обычного окна - программа может хотеть прочитать второе слово из своих cbWndExtra, а храниться там может вообще все что угодно. Про это написано по приведенной выше ссылке в разделе "If it has eight bytes, it must be a dialog box".
Приходится определять класс окна (RealGetWindowClass) и смотреть диалог ли это. И только если диалог - добавлять спец обработку для эмуляции x86 диалоговых процедур.

вторник, 19 февраля 2013 г.

Citrix Streaming клиент и некомпилрующийся проект в Visual Studio 2012

В обед на работе "озарила" идея, которую захотелось сразу проверть в своем проекте эмуляторе.  Для проверки - собираю проект студией, стоящей на рабочем ПК, и получаю странные ошибки:

15>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(249,5): error MSB4018: The "CL" task failed unexpectedly.
15>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(249,5): error MSB4018: System.AggregateException: One or more errors occurred. ---> System.ArgumentOutOfRangeException: Not a valid Win32 FileTime.
15>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(249,5): error MSB4018: Parameter name: fileTime
15>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(249,5): error MSB4018:    at System.DateTime.FromFileTimeUtc(Int64 fileTime)

либо:

1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(611,5): warning MSB6008: Forcing a rebuild of all sources due to an error with the tracking logs. Not a valid Win32 FileTime.
1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(611,5): warning MSB6008: Parameter name: fileTime
1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(611,5): error MSB6001: Invalid command line switch for "link.exe". Not a valid Win32 FileTime.
1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppCommon.targets(611,5): error MSB6001: Parameter name: fileTime

Ошибки плавают, возникают то в одном, то в другом месте, но во всех них идет жалоба на "неправильный" FileTime. Причем, Rebuild All - работает, "поломалась" только обычная компиляция.
Как оказалось виноват клиент Citrix (вот на что бы никогда не подумал - так именно на него). Подробнее тут:
http://connect.microsoft.com/VisualStudio/feedback/details/635940/invalid-command-line-switch-for-link-exe-when-building

Мы как раз на работе "играемся" с стримингом приложений цитрикса, чтобы упростить контроль за использованием ПО на рабочих станциях. Я на днях решил протестировать как оно работает на себе, поставил стриминг клиента - и поплатился.
Вылечилось удалением streaming клиента citrix. Вариант с правкой файлов проекта не проверял, так как у меня их более 70 штук в эмуляторе.

понедельник, 28 января 2013 г.

Борюсь с дедлоком в своем проекте эмулятора. Вещь неприятная - возникает рандомно при включении звука в HOMM3. Оказалась причина дедлока - библиотека Miles Sound System. Для синхронизации работы с основным потоком они не используют критические секции и подобные примитивы, а нашли более "оригинальный метод".
Как написано на их сайте, "John Miles first released MSS in 1991 in the early days of PC gaming". Во времена ДОС для синхронизации было популярно использовать команды CLI (запретить прерывания), сделать свои дела и потом вызвать STI (разрешить прерывания). Видать стояла перед Женькой задача "быстро портировать под windows", вот он взял и заменил CLI на SuspendThread, а STI на ResumeThread для основного потока программы. А о том, что основной поток может быть не единственным, или то, что этот поток может "держать" критическую секцию, которая может понадобится самому MSS, да и то, что использовать данные функции не оптимально хотя бы с точки зрения производительности - думать не надо. И работало все до тех пор, пока не пришел я со своим эмулятором. И засуспендил MSS основной поток в тот момент, когда эмулятор держал критическую секцию, и попробовал MSS начать дальше выполняться - а фиг вам, для этого надо взять ту же секцию, а остановленный поток ее держит и не отдает. И пришлось мне делать свою реализацию SuspendThread, учитывающую подобные "особенности" криворуких программистов, не умеющих пользоваться примитивами синхронизации и изобретающих свой велосипед. "Miles is super robust. Since we ship in so many games, Miles just gets better and better - it just doesn't crash", да, он не крашится. Он просто наглухо вешает вашу программу. Хотя я сужу по библиотеке более чем 10-летней давности, может быть в последних версиях, наконец, действуют более корректно.

И еще один перл от того же автора. На этот раз в библиотеке binkw32.dll:
          loc_100014C2:                          
                push    0              
                call    Sleep
                mov     eax, [esi+4Ch]
                test    eax, eax
                jnz     short loc_100014C2
Зачем нам евенты - есть же поллинг, так хорошо зарекомендовавший себя под ДОС! Будем опрашивать переменную до посинения обнуления. Хотя бы Sleep(0) догадался добавить, чтобы квант времени потока отдать. Только не сильно оно помогло, у меня в эмуляторе этот цикл превратился в вечный. Но это уже баг на моей стороне, "переоптимизировал".

mamaich, 28.01.2013

воскресенье, 27 января 2013 г.

Занимаюсь проектом - запуск x86 Windows программ на планшетнике под Windows RT. Нужно было найти наиболее "тормозящие" места в коде, а Visual Studio 2012 не имеет в себе профайлера для АРМ, там даже profile-guided optimization падает на более-менее больших объемах кода. В итоге пришлось в очередной раз писать свой профайлер. Раньше уже раза 3 делал такой же код, еще под Windows Mobile в порте QEMU, и как на зло - код нигде не сохранился.
Поэтому оставляю код тут, на будущее :)

CSet Counters;
bool volatile DumpProfiler=false;
bool ForEachFunc(unsigned int Idx, void *Data, void *Param)
{
 if(Data)
  LogInfo("%08X - %d\n",Idx,Data);
 return true;
}

HANDLE ProfThread=0;
DWORD WINAPI Profiler(
  _In_  LPVOID lpParameter
)
{
 CONTEXT Ctx;
 while(1)
 {
  SuspendThread(ProfThread);
  Ctx.ContextFlags=CONTEXT_CONTROL;
  GetThreadContext(ProfThread,&Ctx);
  ResumeThread(ProfThread);
  Counters.Set(Ctx.Pc,(void*)(1+(DWORD)Counters.Get(Ctx.Pc)));
  if(DumpProfiler)
  {
   Counters.ForEach(ForEachFunc,0);
  }
  Sleep(20);
 }
}

void InitProfiler()
{
 DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),&ProfThread,0,FALSE,DUPLICATE_SAME_ACCESS);
 CreateThread(0,0,Profiler,0,0,0);
}

Код - прост до ужаса. Параллельный поток останавливает интересующий нас поток программы, получает его PC и фиксирует сколько раз на этом месте мы были. Далее - в отладчике стопорим программу, ставим DumpProfiler=true, сортируем/фильтруем полученный лог в Excel-е по адресам - и анализируем в отладчике что где за функция вызывалась.

mamaich, 27.01.2013

четверг, 24 января 2013 г.

Ровно месяц назад у меня родилась дочка. Сегодня отмечаем ее первый мини день рождения.
Пока что она, как все мелкие дети только и делает что орет, спит, ест, и производит из еды что-то обратное. Однако в месяц уже видны отличия от зверька - пытается тренировать родителей, показывает что ей уже что-то не нравится, и, что удивило, на нее очень хорошо действует успокаивающая музыка. В частности поиском по "музыка для новорожденных" находится неплохой сборник от "Happy Baby" с мелодиями и колыбельными, записанными на фоне сердцебиения. Засыпает - сразу же как только ставлю такую мелодию, так что рекомендую, ссылка. Можно сказать, я хакнул дочь, заставив ее уходить в sleep mode по своему желанию :)
На торрентах можно найти и другие мелодии для детей от тех же авторов. Особенно вставили битлы переделанные под звуки как-бы из детского мобиля.

mamaich, 24.01.2012
Это русскоязычная часть моего блога.
Английский вариант находится тут: http://mamaich-eng.blogspot.com

Основное назначение русского блога - место для хранения мыслей "за жизнь", "о компах" и так далее, в то время, как англоязычная часть планируется больше для технической информации о reverse engineering и подобных увлечениях, и как место для размещения ссылок на мои файлы и комментариев к ним.

mamaich, 24.01.2012