Ассемблерные извращения - натягиваем стек


Листинг 6 передача и использование аргументов при раздельных стеках


"Рукотворный" стек с локальными переменными и аргументами растер сверху вниз (т.е. в направлении противоположном росту обычного стека) и это неспроста. Во-первых, подсистема памяти IBM PC и операционная система Windows оптимизированы именно под такое выделение памяти и мы получаем выигрыш в производительности. Во-вторых, внизу рукотворного стека находится неинициализированная область памяти, что делает ошибки переполнения неактуальными. Затираются лишь локальные переменные текущей функции, да и то лишь те, которые лежат ниже переполняющегося буфера.

Адреса возврата хранятся вообще в другом месте и на них эти переполнения вообще не распространяются (если, конечно, "натуральный" стек расположен выше рукотворного, т.е. лежит в более младших адресах).

Основную трудность, конечно, представляет засылка аргументов в рукотворный стек. Это под MS-DOS мы могли выделить отдельный сегмент и использовать PUSH с префиксом "GS:", а под Windows приходится использовать MOV [EBP+XXh], YYYY и это при том, что адресации типа "память - память" в x86 процессорах не было и нет. В практическом плане это означает, что нам придется использовать промежуточные регистры: MOV EAX, [YYYY]/MOV [EBP+XXh], EAX. Впрочем, это можно оптимизировать, если использовать команду STOSD, занимающую в машинном представлении всего один байт и копирующую содержимое EAX в ячейку на которую указывает EDI одновременно с увеличением последнего на размер двойного слова. Стаскивать аргументы с рукотворного стека можно командой LODSD.

Окончательно расхулиганившись, можно создать целых три стека — один, "стандартный" для хранения адресов возврата, другой — для аргументов и третий для локальных переменных. Чтобы не расходовать регистры понапрасну, можно хранить указатели на вершины двух "рукотворных" стеков в оперативной памяти, загружая их то в регистр EBP, то в ESI/EDI в зависимости от того, какой из них окажется удобнее в данный конкретный момент. Падения производительности можно не опасаться. Большую часть своего времени указатели будут проводить в кэш-памяти, извлекаясь всего за один-два такта.




Начало  Назад  Вперед



Книжный магазин