Summary: | Падает wine при попытке сохранить batch-файл. | ||
---|---|---|---|
Product: | WINE@Etersoft | Reporter: | Sergey Lebedev <lebedev.v.sergey> |
Component: | Общее | Assignee: | BUGS@Etersoft <bugs> |
Status: | CLOSED FIXED | QA Contact: | |
Severity: | normal | ||
Priority: | P5 | CC: | baraka, kondratyuk, lav, mibori, vostok |
Version: | 1.0.7 | ||
Target Milestone: | --- | ||
Hardware: | PC | ||
OS: | Linux | ||
Whiteboard: | |||
Заявки RT: | Связано с: | ||
Дата напоминания: | |||
Bug Depends on: | 447, 952 | ||
Bug Blocks: | 450, 1838, 8500 |
Description
Sergey Lebedev
2007-10-22 19:30:06 MSD
Очень похоже на 447. Думаю, причины одни и те же. 90% - август Не всегда воспроизводится. Не занимаюсь. Баг возникает не всегда. После того как File -> Save as batch..., вводишь любое имя, нажимаешь на кнопку Save, в консоле появляется ошибка с примерно следующим бэктрейсом (каждый раз немного разный): Backtrace: =>1 0x011ae8a9 (0x0032fcd8) 2 0x2382cbe1 in awl (+0x2cbe1) (0x0032fd04) 3 0x2382cea3 in awl (+0x2cea3) (0x00000000) Если тестировать несколько раз, то строка 1 в бэктрейсе обычно всегда разная. Зато строки 2 0x2382cbe1 in awl (+0x2cbe1) (0x0032fd04) 3 0x2382cea3 in awl (+0x2cea3) (0x00000000) с каждым разом всегда одинаковы. ---------------------------------------------------------------------- Дизассемблируем Awl.dll (находится в c:\Program Files\ABBY ... Reader, или куда поставишь) Адрес 0x2382cbe1 соответствует вызову ... .text:2382CBDE call dword ptr [eax+8] ;падает .text:2382CBE1 mov ecx, [ebp+var_C] ;2 строчка в бэктрейсе ... Этот участок кода представляет из себя кусок функции CMessageLoop::TryOnIdle(long). Вот она полностью: .text:2382CBB0 ; protected: bool __thiscall AWL::CMessageLoop::TryOnIdle(long) .text:2382CBB0 public ?TryOnIdle@CMessageLoop@AWL@@IAE_NJ@Z .text:2382CBB0 ?TryOnIdle@CMessageLoop@AWL@@IAE_NJ@Z proc near .text:2382CBB0 .text:2382CBB0 var_10 = dword ptr -10h .text:2382CBB0 var_C = dword ptr -0Ch .text:2382CBB0 var_4 = dword ptr -4 .text:2382CBB0 arg_0 = dword ptr 8 .text:2382CBB0 .text:2382CBB0 push ebp .text:2382CBB1 mov ebp, esp .text:2382CBB3 push 0FFFFFFFFh .text:2382CBB5 push offset loc_2387DD50 .text:2382CBBA mov eax, large fs:0 .text:2382CBC0 push eax .text:2382CBC1 mov large fs:0, esp .text:2382CBC8 sub esp, 8 .text:2382CBCB mov edx, [ebp+arg_0] .text:2382CBCE mov eax, [ecx] ; первый адрес из объекта .text:2382CBD0 push ebx .text:2382CBD1 push esi .text:2382CBD2 push edi .text:2382CBD3 mov [ebp+var_10], esp .text:2382CBD6 push edx .text:2382CBD7 mov [ebp+var_4], 0 .text:2382CBDE call dword ptr [eax+8] ; передается управление в рандомную область .text:2382CBE1 mov ecx, [ebp+var_C] ; (2) в бэктрейсе .text:2382CBE4 mov large fs:0, ecx .text:2382CBEB pop edi .text:2382CBEC pop esi .text:2382CBED pop ebx .text:2382CBEE mov esp, ebp .text:2382CBF0 pop ebp .text:2382CBF1 retn 4 .text:2382CBF1 ?TryOnIdle@CMessageLoop@AWL@@IAE_NJ@Z endp таким образом получается, что по адресу dword ptr [eax+8] должна находится некоторая, успешно отрабатывающая функция. Но получается, что туда помещается указатель куда-то не туда. Очень похоже, что dword ptr [eax+8], это третье поле какой-то структуры указатель на которую находится в eax. Смотрим, что меняет eax перед этим: ... .text:2382CBCE mov eax, [ecx] ... В еax сваливается что-то, что находится по адресу, на который указывает ecx. И перед этим ecx ничего не меняет. Смотрим, на первую строчку .text:2382CBB0 ; protected: bool __thiscall AWL::CMessageLoop::TryOnIdle(long) ... из нее видим, что вызов CMessageLoop::TryOnIdle(long) придерживается конвенции thiscall (о конвенциях можно почитать по ссылке http://en.wikibooks.org/wiki/Reverse_Engineering/Calling_Conventions ) Согласно конвенции thiscall, перед вызовом метода объекта в ecx заносится указатель на него. Значит в ecx находится указатель на объект класса CMessageLoop... ... далее обращаемся к 3-й строчке бэктрейса: 3 0x2382cea3 in awl (+0x2cea3) (0x00000000) Смотрим, что находится по адресу 0x2382cea3 в дизассемблированном Awl.dll: ... .text:2382CE9E call ?TryOnIdle@CMessageLoop@AWL@@IAE_NJ@Z ; AWL::CMessageLoop::TryOnIdle(long) .text:2382CEA3 inc ebp ; (3) в бэктрейсе ... Этот участок кода принадлежит функции CMessageLoop::DoMessageLoop(void), вот ее код полностью: .text:2382CE10 ; public: void __thiscall AWL::CMessageLoop::DoMessageLoop(void) .text:2382CE10 public ?DoMessageLoop@CMessageLoop@AWL@@QAEXXZ .text:2382CE10 var_24 = dword ptr -24h .text:2382CE10 var_14 = dword ptr -14h .text:2382CE10 var_C = dword ptr -0Ch .text:2382CE10 var_4 = dword ptr -4 .text:2382CE10 .text:2382CE10 push 0FFFFFFFFh .text:2382CE12 push offset loc_2387DD78 .text:2382CE17 mov eax, large fs:0 .text:2382CE1D push eax .text:2382CE1E mov large fs:0, esp .text:2382CE25 sub esp, 8 .text:2382CE28 push ebx .text:2382CE29 push ebp .text:2382CE2A push esi .text:2382CE2B push edi .text:2382CE2C mov esi, ecx .text:2382CE2E push esi .text:2382CE2F lea eax, [esi+4] .text:2382CE32 push eax .text:2382CE33 push offset ?currentMessageLoop@CMessageLoop@AWL@@1PAV12@A ; AWL::CMessageLoop * AWL::CMessageLoop::currentMessageLoop .text:2382CE38 lea ecx, [esp+30h+var_14] .text:2382CE3C call sub_2382C960 .text:2382CE41 xor ebp, ebp .text:2382CE43 mov [esp+24h+var_4], ebp .text:2382CE47 mov bl, 1 .text:2382CE49 lea esp, [esp+0] .text:2382CE50 .text:2382CE50 loc_2382CE50: .text:2382CE50 mov ecx, esi .text:2382CE52 call ?PumpMessage@CMessageLoop@AWL@@IAE_NXZ ; AWL::CMessageLoop::PumpMessage(void) .text:2382CE57 test al, al .text:2382CE59 jz short loc_2382CEAC .text:2382CE5B lea edi, [esi+8] .text:2382CE5E push edi .text:2382CE5F mov ecx, esi .text:2382CE61 call ?TryIsIdleMessage@CMessageLoop@AWL@@IAE_NAAUtagMSG@@@Z ; AWL::CMessageLoop::TryIsIdleMessage(tagMSG &) .text:2382CE66 test al, al .text:2382CE68 jz short loc_2382CE6E .text:2382CE6A mov bl, 1 .text:2382CE6C xor ebp, ebp .text:2382CE6E .text:2382CE6E loc_2382CE6E: .text:2382CE6E push 0 .text:2382CE70 push 0 .text:2382CE72 push 0 .text:2382CE74 push 0 .text:2382CE76 push edi .text:2382CE77 call off_238E11D0 .text:2382CE7D test eax, eax .text:2382CE7F jnz short loc_2382CE50 .text:2382CE81 test bl, bl .text:2382CE83 jz short loc_2382CE50 .text:2382CE85 lea edi, [esi+8] .text:2382CE88 .text:2382CE88 loc_2382CE88: .text:2382CE88 push 0 .text:2382CE8A push 0 .text:2382CE8C push 0 .text:2382CE8E push 0 .text:2382CE90 push edi .text:2382CE91 call off_238E11D0; PeekMessageW(edi,0,0,0,0) .text:2382CE97 test eax, eax .text:2382CE99 jnz short loc_2382CE50 .text:2382CE9B push ebp .text:2382CE9C mov ecx, esi .text:2382CE9E call ?TryOnIdle@CMessageLoop@AWL@@IAE_NJ@Z ; AWL::CMessageLoop::TryOnIdle(long) .text:2382CEA3 inc ebp; (3) в бэктрейсе .text:2382CEA4 test al, al .text:2382CEA6 jnz short loc_2382CE88 .text:2382CEA8 xor bl, bl .text:2382CEAA jmp short loc_2382CE50 .text:2382CEAC loc_2382CEAC: .text:2382CEAC lea ecx, [esp+24h+var_14] .text:2382CEB0 call sub_2382C980 .text:2382CEB5 mov ecx, [esp+24h+var_C] .text:2382CEB9 pop edi .text:2382CEBA pop esi .text:2382CEBB pop ebp .text:2382CEBC pop ebx .text:2382CEBD mov large fs:0, ecx .text:2382CEC4 add esp, 14h .text:2382CEC7 retn .text:2382CEC7 ?DoMessageLoop@CMessageLoop@AWL@@QAEXXZ endp ------------------------------------------------------------------------ Перед вызовом вызовом CMessageLoop::TryOnIdle(long) указатель на объект CMessageLoop заносится из esi, а единственный long-аргумент поднимается в стек из ebp (т. е. в псевдокоде esi->TryOnIdle(ebp) ): .text:2382CE9B push ebp .text:2382CE9C mov ecx, esi Строчки .text:2382CE97 test eax, eax .text:2382CE99 jnz short loc_2382CE50 можно понять как if(eax != 0) goto loc_382CE50 /* очевидно переход на начало цикла */ Последовательность .text:2382CE88 push 0 .text:2382CE8A push 0 .text:2382CE8C push 0 .text:2382CE8E push 0 .text:2382CE90 push edi .text:2382CE91 call off_238E11D0 означает ни что иное как PeekMessageW(edi, 0, 0, 0, 0), поскольку по адресу off_238E11D0 находится указатель на блок кода: .text:23879D79 sub_23879D79 proc near .text:23879D79 push offset loc_2387AA05 ; Value .text:23879D7E push dword_238E22FC ; int .text:23879D84 push offset off_238E11D0 ; Target .text:23879D89 push offset aPeekmessagew ; "PeekMessageW" .text:23879D8E push offset aUser32_dll_0 ; "user32.dll" .text:23879D93 call sub_2387A74A .text:23879D98 jmp off_238E11D0 .text:23879D98 sub_23879D79 endp который (упрощенно) вызовом call sub_2387A74A через loadLibrary и GetProcAddr получает адрес на PeekMessageW и передает туда часть стека с параметрами от PeekMessageW (которые заносятся в стек в DoMessageLoop)... Возможно дело не в PeekMessageW, но есть одна догадка... Функция BOOL WINAPI PeekMessageW( MSG *msg_out, HWND hwnd, UINT first, UINT last, UINT flags ) ловит сообщения из очереди сообщений. Кроме того, что она возвращает TRUE/FALSE (пришло/не пришло сообщение в очередь), она еще и в случае TRUE модифицирует переданную в первом аргументе структуру MSG *msg_out. Эта структура отражает содержимое пойманного сообщения. Вот она: typedef struct tagMSG{ HWND hwnd; // окошко UINT message; // код сообщения WPARAM wParam; // wParam специфичный данному сообщению LPARAM lParam; // lParam специфичный данному сообщению DWORD time; // идентификатор времени POINT pt; // координаты мышиного курсора } MSG, *PMSG, *LPMSG; Каждый раз, перед тем как wine рушится, функция PeekMessageW вызывается в цикле 32 раза (обнаружено через дебаглог). Среди тех случаев, когда она ловит какое-либо сообщение, появляется сообщение с кодом message=0x113, что есть сообщение WM_TIMER. Для него следующие специфичные: wParam - идентификатор времени, lParam - типа (TIMERPROC *) - указатель на заданную пользователем функцию void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) (см. http://msdn.microsoft.com/en-us/library/aa932778.aspx ) Возможно приводящий к ошибке call dword offset [eax + 8] есть как раз вызов по lParam из MSG, который неправильно установлен через SetTimer (см. http://msdn.microsoft.com/en-us/library/ms644906(VS.85).aspx ) отрабатываю пока эту версию. это не MSG где третьим элементом является функция, и вот почему... Рассмотрим в методе CMessageLoop::TryOnIdle(long) следующие строчки: ... ... ecx содержит ссылку на объект CMessageLoop ... ... ... регистр ecx нигде не меняется ... ... .text:2382CBCE mov eax, [ecx] ; в eax кладется первое поле из объекта CMessageLoop ... ... eax нигде не меняется .text:2382CBDE call dword ptr [eax+8] ; вызов, который падает ... ... следующая строчка заночится в бэктрейс По на форуме васма ( http://www.wasm.ru/forum/viewtopic.php?id=28409 ) подсказывают, что первое поле объекта вообще-то говоря ссылка на таблицу виртуальных функций. Очевидно предположить, что первое поле инициализируется в конструкторе . Вот полный код CMessageLoop::CMessageLoop(void): .text:2382CA80 ; public: __thiscall AWL::CMessageLoop::CMessageLoop(void) .text:2382CA80 xor eax, eax .text:2382CA82 push esi .text:2382CA83 mov esi, ecx .text:2382CA85 push eax .text:2382CA86 push eax .text:2382CA87 lea ecx, [esi+28h] .text:2382CA8A mov dword ptr [esi], offset ??_7CMessageLoop@AWL@@6B@ ; const AWL::CMessageLoop::`vftable' .text:2382CA90 mov [esi+4], eax .text:2382CA93 mov [esi+24h], eax .text:2382CA96 call ds:??0CPoint@FObj@@QAE@HH@Z ; FObj::CPoint::CPoint(int,int) .text:2382CA9C mov eax, esi .text:2382CA9E pop esi .text:2382CA9F retn .text:2382CA9F ??0CMessageLoop@AWL@@QAE@XZ endp После строчки .text:2382CA83 mov esi, ecx, указатель на объект попадает в esi. В строчке .text:2382CA8A mov dword ptr [esi], offset ??_7CMessageLoop@AWL@@6B@ ; const AWL::CMessageLoop::`vftable', происходит инициализация таблицы витруальных функций. Заглянем в таблицу виртуальных функций по смещению offset ??_7CMessageLoop@AWL@@6B@ и увидим следующую таблицу: .rdata:23889F60 ;const AWL::CMessageLoop::`vftable' .rdata:23889F60 ??_7CMessageLoop@AWL@@6B@ dd offset ?PreTranslateMessage@CMessageLoop@AWL@@UAE_NAAUtagMSG@@@Z .rdata:23889F60 ;DATA XREF: AWL::CMessageLoop::CMessageLoop(void)+Ao .rdata:23889F60 ;AWL::CMessageLoop::~CMessageLoop(void)o ... .rdata:23889F60 ;AWL::CMessageLoop::PreTranslateMessage(tagMSG &) .rdata:23889F64 dd offset ?IsIdleMessage@CMessageLoop@AWL@@UAE_NAAUtagMSG@@@Z ;AWL::CMessageLoop::IsIdleMessage(tagMSG &) .rdata:23889F68 dd offset ?OnIdle@CMessageLoop@AWL@@UAE_NJ@Z ;AWL::CMessageLoop::OnIdle(long) .rdata:23889F6C dd offset ?ValidateData@CWindow@AWL@@MAE_NXZ ;AWL::CWindow::ValidateData(void) .rdata:23889F70 dd offset ?OnQuit@CMessageLoop@AWL@@UAEXH@Z ;AWL::CMessageLoop::OnQuit(int) .rdata:23889F74 dd offset ?UpdateControls@CDialog@AWL@@EAE_NXZ ;AWL::CDialog::UpdateControls(void) [eax + 8] в CMessageLoop::OnIdle соответствует, 3-му элементу в этой таблице. Т. е. call dword ptr [eax + 8] есть вызов AWL::CWindow::ValidateData(void). Вот ее код: .text:2380B8E0 ; protected: virtual bool __thiscall AWL::CWindow::ValidateData(void) .text:2380B8E0 public ?ValidateData@CWindow@AWL@@MAE_NXZ .text:2380B8E0 ?ValidateData@CWindow@AWL@@MAE_NXZ proc near .text:2380B8E0 mov al, 1 ; bool __cdecl .text:2380B8E2 retn .text:2380B8E2 ?ValidateData@CWindow@AWL@@MAE_NXZ endp Очевидно, что внутри это функции падать ничего не может, она просто возвращает TRUE. Есть догадка о том, что что-то нарушает таблицу виртуальных функций, тем самым вызывается не CWindow::ValidateData, а что-то по ошибочному адресу... *** Bug 952 has been marked as a duplicate of this bug. *** Откладываем, bugs@ в ближайшее время делать ничего не будет. На wine-etersoft 2.0.3 отлично работет Fine Reader 8.0, всё сохраняется и не падает. Закрываю. |