Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Флоренсов А.Н. УП Системное программное обеспечение.docx
Скачиваний:
46
Добавлен:
28.06.2021
Размер:
148.95 Кб
Скачать

5.3. Программный доступ к системным функциям Win32

Программирование на ассемблере ОС Windows, согласно ограничительной технической политике коммерческого производителя (Microsoft), для прямого доступа через прерывания или аналогичной машинной инструкции в современных машинных архитектурах не документировано производителем и поэтому практически недоступно. Но какие-то нижнеуровневые средства такого доступа должны быть, иначе невозможны разработки трансляторов с языков высокого уровня, которые строят машинный код, работающий с вызовами системных функций ОС.

Практическое решение построено на использовании библиотечных наборов программ, называемых Dynamic Library Linking (DLL), и вспомогательных библиотечных наборов библиотек импорта.

Системные функции Win32, в частности такие, как функции вывода для текстового режима и работы с файлами, доступны через библиотеку, названную разработчиками kernel32.DLL. Соответствующая библиотека импорта в программных системах разработки программ от Microsoft называется KERNEL32.LIB. Заметим, что в альтернативных системах разработки программ для Windows, которые еще 10–15 лет назад использовались и даже появлялись как новые предложения, библиотека импорта аналогичного назначения могла называться иначе (в Borland – USER32.LIB, у разработчика свободно распространяемого компоновщика ALINK.EXE как Win32.LIB и т. п.).

Техническое творчество Microsoft, несмотря на ее технико-коммер-ческие успехи, иногда вызывает что-то среднее между удивлением и восхищением. Так в системе ее разработок программу, интегрированную в виде комплексной системы Visual Studia, для доступа через библиотеку импорта KERNEL32.LIB необходимо использовать не собственные имена системных функций WINAPI, а специально «декорированные» производные от них имена. Так для доступа к функции WriteFile, хорошо известной по системной документации, ориентированной на использование языка Си, следует на уровне ассемблера применять имя WriteFile@20; для доступа к функции GetStdHandle – имя _GetStdHandle@4 и т. п. Общее правило здесь такое – исходное «родное в DLL» имя предваряется символом подчеркивания, а за исходным именем после служебного символа @ записывается число байтов, занимаемых суммарно значениями параметров функции. Отметим, что все альтернативные Microsoft разработки не используют ничего подобного указанным усложнениям.

Доступ к системным функциям операционной системы MS Windows организован через специализированные библиотеки этой системы, которые содержат подпрограммы, вызываемые с помощью машинных команд CALL. Для конкретного доступа библиотеки предоставляют специализированные имена этих функций. Такие имена с подробным описанием аргументов, включая их типы, задаются документацией на API MS Windows с учетом особенностей системы программирования. Следует иметь в виду, что стандартные для документации имена API предоставляются для языка Си, а на уровне фактических имен для ассемблера может быть определенное отличие от этих имен, что будет рассмотрено дальше.

Обращение к API функциям в MS Windows осуществляется по соглашению Си для укладки аргументов в стек и по соглашению Паскаля для освобождения стека от этих аргументов.

Обращение на уровне ассемблера к функциям WINAPI в 32-битных системах использует передачу аргументов через системный стек путем выполнения команд PUSH, соглашение по порядку укладки аргументов типа PASCAL (укладку аргументов в стек по порядку от первого до последнего аргумента) и очистку стека выполняемой процедурой. Так что никаких явных действий над регистром указателя стека ESP выполнять не надо (и не надо задумываться над соответствующими проблемами). В результате вызов функции WriteFile для вывода 10 байтов из области buf запишется на NASM следующей последовательностью команд

;--- WriteFile(hstdout, buf, 10, &actlen, NULL)

push dword 0; NULL

push dword actlen ; address of actual byte number

push dword 10

push dword buf ; address of txt

push dword [hstdout] ; N handle=hstdout

call _WriteFile@20

При таком построении имя _WriteFile@2 должно быть обязательно указано в директиве EXTERN.

Как следствие, простейшая демонстрационная программа на NASM, которая вводит текст и выдает его с измененным на восклицательный знак, будет иметь вид, приведенный в листинге 5.3.1.

GLOBAL _start

EXTERN _GetStdHandle@4, _WriteFile@20, _ExitProcess@4

EXTERN _ReadFile@20

segment .text

_start: push dword STD_OUTPUT_HANDLE

call _GetStdHandle@4

mov [hstdout],eax

push dword STD_INPUT_HANDLE

call _GetStdHandle@4

mov [hstdin],eax

;---ReadFile(hstdin, buf, 80, &actlen, NULL)

push dword 0

push dword actlen

push dword 80 ; number of bytes

push dword buf ; address of txt

push dword [hstdin] ; N handle=hstdout

call _ReadFile@20

mov byte [buf+1],'!'

;---WriteFile(hstdout, buf, actlen, &actlen, NULL)

push dword 0; NULL

push dword actlen ; address of actual byte number

push dword [actlen]

push dword buf ; address of txt

push dword [hstdout]; N handle=hstdout

call _WriteFile@20

; ExitProcess(0)

push dword 0

call _ExitProcess@4

segment .data

buf times 80 db 0

hstdin dd 0

hstdout dd 0

actlendd 0

STD_INPUT_HANDLE equ –10

STD_OUTPUT_HANDLE equ –11

Листинг 5.3.1. Листинг простейшей программы на ассемблере для Windows

Заметим, что символические константы, используемые в WINAPI, должны быть взяты из соответствующих заголовочных файлов. Простейший способ для этого – найти конкретное числовое значение этих констант в соответствующем файле с расширением .h в каталоге INCLUDE системы разработки на языке Си или использовать аналогичные файлы для включения заголовочной информации для языка NASM. На последнем, хотя и более эффективном, варианте останавливаться не будем, в стремлении сократить до минимума требуемый объем начальной информации для программирования (использовать только уже имеющийся и освоенный набор профессиональной информации).

Трансляция такой и подобных ассемблерных программ требует ключа формата -f win32 в качестве параметра вызова программы nasm. После получения объектного файла, который автоматически при этом приобретает расширение .obj, если не задается явно через опцию -o, требуется вызов компоновщика от Microsoft. Этот компоновщик входит в состав систем разработки Visual Studia и имеет имя link.exe. Относительным неудобством при программировании извне этой системы разработки оказывается то, что вызов из командной строки вне функционирования этой системы требует предварительных настроек дополнительных параметров доступа к исполняемым файлам. Эти настройки можно выполнять либо через надстроечные графические окна системы в «Управляющей панели» («Система» и «Параметры системы»), либо прямой коррекцией (дополнением) системной переменной PATH для текущего пользователя. С учетом многофункциональности Visual Studia и возможности ее использования для построения как 32-битных, так и набора различных 64-битных приложений (и x86_64 и IA64), в простейших случаях может оказаться проще и удобней просто переместить нужный файлlink.exe в текущий каталог разработки программ на ассемблере программиста. При этом совершенно необходимо вместе с ним переместить файл разделяемой исполнительной библиотеки с именем mspdbБ0.DLL, где вместо символа Б следует записать номер версии Visual Studia. Так для Visual Studia 10 следует найти и скопировать файл mspdb100.DLL, для Visual Studia 8 – файл mspdb80.DLL. (При отсутствии указанной DLL компоновщик при запуске сообщает об отсутствии этой библиотеки.)

Компоновщик link.exe имеет множество потенциальных параметров вызова, задаваемых прямо в командной строке, краткую информацию о которых можно получить, запустив эту программу совсем без параметров и без имени обрабатываемого файла.

Для наших целей построения простейшей программы на основе листинга 5.3.1 нужен следующий вариант вызова:

link /ENTRY:start /SUBSYSTEM:CONSOLEимя_файла.obj kernel32.lib

Оба действия – трансляция и компоновка – могут быть объединены в общем командном файле с именем, например tranwc, содержимое которого дано в листинге5.3.2:

nasm –f win32% 1.asm

link /ENTRY:start /SUBSYSTEM:CONSOLE %1.obj kernel32.lib

Листинг 5.3.2. Командный файл tranwc.cmd для разработки на ассемблере

Его следует вызывать из командной строки в виде

./tranwc.cmd имя_файла

Как видим, для компоновщика указывается создание консольного приложения (параметр /SUBSYSTEM:CONSOLE), альтернативой может служит создание графического приложения (значение параметра windows). В командном вызове задается использование объектного файла и библиотеки импорта kernel32.lib. Некоторое недоумение может вызвать указание точки входа программы (параметр ENTRY) как имени start, хотя в исходной программе на ассемблере имя точки входа указано как_start. Дело в том, что по решению разработчиков Microsoft указанное в параметре ENTRY имя автоматически дополняется символом подчеркивания, приписываемым спереди от заданного.

С учетом широкого распространения Win32 рассмотрим вспомогательный прием, позволяющий использовать стандартную форму обращения к системным функциям ОС MS Windows. Он заключается в применении специальных файлов, имеющих обычно расширение INC и содержащих фактически переименование от простого наименования к полному. Например, для доступа к системной функции GetStdHandle, имеющей полное имя _GetStdHandle@4 в COFF, используется директива ассемблера

%define GetStdHandle EQU_GetStdHandle@4

совместно с директивой

extern _GetStdHandle@4

Применение таких файлов, содержимое которых включается в свою очередь в исходный текст разрабатываемой программы директивой

%include "имя_файла"

позволяет в тексте ассемблерных программ по-прежнему писать простое имя системной функции, но это имя будет заменяться для объектного файла требуемой формой полного имени [6].