| 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, всё сохраняется и не падает. Закрываю. |