Уважаемые коллеги ! Я сильно начинающий и наверное чего то не дочитал, но почему mov ax, 38 mov ebx, eax push ax ; это показывает invoke wsprintf, ADDR buf, ADDR ifmt, ebx invoke WriteConsole, stdout, ADDR buf, BSIZE, ADDR cWritten, NULL pop ax mov ebx, eax ; а это не показывает invoke wsprintf, ADDR buf, ADDR ifmt, ebx invoke WriteConsole, stdout, ADDR buf, BSIZE, ADDR cWritten, NULL Хотя если заменить push ax .... pop ax на push eax .... pop eax , то оба вывода появляются на экране. Что в Win32 надо помещать в стек только 32бит значения ? Вроде нигде такого не написано. Может плохо читал ? Спасибо
mkl Логичное заключение, но не совсем правильное. Всё зависит от кода. Вместо одного 32-битного push'а можно сделать два 16-битных и т.д. С другой стороны, если участок кода не содержит вызовов API и не является callback'ом, нитью и т.п., то можно спокойно кромсать стек 16-битными push'ами.
Возможно, дело не в push\pop, а в некорректной пересылке ax в ebx, т.к. после invoke в старшем ворде eax может быть любой мусор => для гарантии обнуления старшего ворда ebx нужно использовать либо Код (Text): pop ax movzx ebx,ax либо xor eax,eax pop ax mov ebx,eax
Обнуления безусловно нужны, но они не при чем. В аттаче полный пример. Самое интересное - в отладчике содержимое регистров перед вторым вызовом такое же как перед первым, но на экране - ничего, а в eax - 1, т.е. ошибка, как я понимаю. Только не понимаю какая (про get last error еще не проходили ...). 131853869__L.asm
Не знаю, как ты увидел код ответа -1, но заменив перед второй выдачей mov ebx,eax на mov ebx,27 ты поймешь что не работает 1-ая выдача. Почему ? Не знаю, но подозреваю, что стек должен идти по двойным словам ( 32-бит ) и система начинает дурить. Возможно, что это еще один из методов куда-нибудь влезть, типа переполнения буфера... Отладчик показывает, что не работает WriteConsole.
WriteConsole не нравится, то что стек не выровнен по границе двойного слова. Вот так будет работать. Код (Text): xor eax, eax mov ax, 38 mov ebx, eax push ax push ax invoke wsprintf, ADDR buf, ADDR ifmt, ebx invoke WriteConsole, stdout, ADDR buf, BSIZE, ADDR cWritten, NULL xor eax, eax pop ax pop ax mov ebx, eax invoke wsprintf, ADDR buf, ADDR ifmt, ebx invoke WriteConsole, stdout, ADDR buf, BSIZE, ADDR cWritten, NULL
Выравнивание стека здесь ни причем. Нужно внимательнее читать описание функций: "Unlike other Windows functions, wsprintf uses the C calling convention (_cdecl)... As a result, it is the responsibility of the calling process to pop arguments off the stack.. In C-language modules, the C compiler performs this task" Если после wsprintf вставить как положено add esp,12 то все будет OK и при push ax и push eax.
> Если после wsprintf вставить как положено add esp,12 то все будет OK Не вводи людей в заблуждение - invoke сам ставит add esp, 0Ch Дело именно в невыровнянности стэка.
"invoke сам ставит add esp, 0Ch " С инвоками не работаю принципиально, поэтому сказать ничего не могу. Но с add esp у меня прекрасно работает и push ax и push eax. Для проверки нужно посмотреть esp до и после вызова wsprintf, или оформить код в виде процедуры - без очистки стека мы из нее не выйдем. PS: я проверял в 9x, может в nt-шных осях и не так - ну тады не знаю...
Four-F "Возможно в 9х выравнивание по-барабану, но под nt+ точно в этом дело" Хорошо. Может тогда ответишь на исходный вопрос mkl: "Вроде нигде такого не написано. Может плохо читал ?" All Насчет выравнивания молчу. Но еще один источник ошибки - это BSIZE = 15 в то время как длина строки = 2. По правильному вместо BSIZE нужно использовать значение eax, возвращаемое wsprintf. Что может быть при выдаче на консоль разного мусора уже недавно обсуждалось (см.Смерть винде).
На мой взгляд все, что до сих пор говорилось о выравнивании стека похоже на предположения. Судя по вопросу даже для "сильно начинающих" ясно, что выравнивать "хорошо", а нарушать выравнивание "плохо" и лучше не рисковать. Вопрос то в том, а что будет если мы поступим плохо и вызовем API с невыровненным стеком. В 9х для данного примера ничего страшного не происходит, "но под nt+ точно в этом дело". А какое дело так никто и не уточнил. Контролирует система выравнивание или нет ? Что означает фраза Four-F "WriteConsole не нравится.." ? "Система начинает дурить" и "это еще один из методов куда-нибудь влезть" как подозревает valterg или мы дурим, не делая проверки результата WriteConsole на 0 и GetLastError <> 0 и не устанавливая свой SEH-обработчик. Что выдает отладчик после вызова WriteConsole ? Или может не срабатывает первый вызов, возвращая 0, а срабатывает второй после pop ax. PS: сам бы проверил, да не на чем - 9х никак умирать не хочет.
ESP в NT не только должен быть выровнен по DWORD, но так же должен находиться в пределах, хранящихся в FS:[4] //StackBase Upper Address FS:[8] //StackLimit Lower Address не знаю, где написано про это всё явно, но вроде бы это одно из основных правил, вроде "API сохраняет значения регистров ESP, EBP, ESI, EDI и EBX, а EAX, EDX и ECX портит; и подразумевает, что флаг DF сброшен" Кстати, про необходимость выравнивания стека в 32разрядных режимах пишет intel в optimization reference manual. А то, что мастдаю это не проверяет - дык, система не является 32разрядной
S_T_A_S_ То, о чем ты говоришь - ясно и понятно. Если мы выйдем за границы стека, то наверное получим #SS или #GP. А вот выравнивание по dword в том числе у intel выглядит как рекомендация для улучшения performance, а ошибки выравнивания в процессоре генерятся только при установленном флаге AC. Я уже повторяюсь, но вопрос в том что будет если если кто-то тупой и безграмотный нарушит эти рекомендации. Система может или 1) контролировать выравнивание (например по #AC или test esp,..) и выдавать ошибку или 2) тупо надеятся на "сознательность" кодеров, что чревато последствиями. Я все-таки надеюсь на первое и поэтому хотелось бы до конца разобраться где возникает ошибка в приведенном примере (неужели это так долго и трудно ...).
[ leo: <font color="indigo]Может тогда ответишь на исходный вопрос</font><!--color--> ] S_T_A_S_ вобщем уже ответил. Все эти тонкости с созранением регистров и выравниванием стека мало интересуют рядового программиста на ЯВУ. Этим занимаются разработчики компиляторов. А поскольку доки пишут именно для ЯВУ-программистов, то всю эту низкоуровневую хрень пытаются опустить. И программист на ЯВУ на эти грабли никогда не наступит. Про выравнивание довольно много написано в DDK в разделе по 64-битному кодингу. Касательно косяка с WriteConsole, дело обстоит так: kernel32!WriteConsole -> kernel32!WriteConsoleInternal -> ntdll!CsrClientCallServer -> ntdll!ZwRequestWaitReplyPort -> переход в режим ядра -> ntoskrnl!ZwRequestWaitReplyPort Код (Text): :0049BA97 _NtRequestWaitReplyPort@12 proc near . . . :0049BA97 RequestMessage= dword ptr 0Ch ; IN PPORT_MESSAGE . . . ; ; ставим SEH ; :0049BA9A push -1 :0049BA9C push offset dword_402DE0 :0049BAA1 push offset __except_handler3 :0049BAA6 mov eax, large fs:0 :0049BAAC push eax :0049BAAD mov large fs:0, esp . . . ; ; проверяем выравнивание ; :0049BAF5 mov ecx, [ebp+RequestMessage] ; IN PPORT_MESSAGE <font color="red]:0049BAF8 test cl, 3 ; ВОТ ПРОВЕРКА НА КРАТНОСТЬ DWORD</font><!--color--> :0049BAFB jnz loc_4ED687 ... ; ; вбрасываем исключение STATUS_DATATYPE_MISALIGNMENT, с которым разбирается обработчик SEH ; :004ED687 loc_4ED687: :004ED687 call _ExRaiseDatatypeMisalignment
Four-F И последняя точка. Из описания WrriteConsole : <font color="red]Return Values If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError. </font><!--color--> Прогнал еще раз в MS Visual Studio. Действительно в первом случае код ответа 0, а во втором - 1. Вот к чему приводит пренебрежение к документации
Four_F & valterg Спасибо за исчерпывающий ответ. PS: "и все таки она вертится", в том смысле, что выполняет явный контроль, а не виснет по тихому.
Уважаемые коллеги, огромное спасибо за исчерпывающие ответы. Все стало понятно, сам бы дошел не скоро. А много ли в низком уровне такого, о чем написано только в местах, подобных DDK в разделе по 64-битному(?) кодингу или intel в optimization reference manual ?