MS-DOS для программиста© Александр Фролов, Григорий ФроловТом 18, М.: Диалог-МИФИ, 1995, 254 стр. 6.7. Особенности отладки драйверовДрайверы достаточно сложны для отладки. На этапе инициализации драйвера (при выполнении команды инициализации) загрузка операционной системы еще не завершена, и воспользоваться обычным отладчиком невозможно. Отладчик Turbo Debugger позволяет вам отлаживать драйверы в режиме удаленной отладки, но для этого потребуется второй компьютер. Прикладная программа не вызывает драйвер напрямую, а делает это через функции прерывания MS-DOS, поэтому вам придется очень долго "добираться" до программы прерывания драйвера. Малейшие ошибки в программе инициализации могут привести к невозможности завершения загрузки операционной системы. Программа стратегии обычно очень проста и проблем не вызывает. Для отладки обработчика команды инициализации можно подготовить специальные функции, отображающие на экране содержимое наиболее важных переменных и областей памяти. Такие же функции можно использовать и для отладки обработчиков других команд драйвера. В качестве примера приведем текст функции ntrace, которая выводит на экран содержимое всех регистров процессора. После вывода выполнение программы приостанавливается до тех пор, пока пользователь не нажмет любую клавишу. Текст этой функции следует поместить в ту часть драйвера, которая останется резидентной. В этом случае вы сможете вызывать ее не только при инициализации, но и при выполнении других команд. В листинге 6.2 вы найдете исходный текст функции ntrace, а также исходный текст небольшой com-программы, которая вызывает эту функцию. Листинг 6.2. Файл ntrace\ntrace.asm @@out_ch MACRO c1,c2,c3,c4,c5,c6,c7,c8,c9,c10 mov ah,02h IRP chr,<c1,c2,c3,c4,c5,c6,c7,c8,c9,c10> IFB <chr> EXITM ENDIF mov dl,chr int 21h ENDM ENDM @@out_str MACRO mov ah,9 int 21h ENDM .MODEL tiny .CODE .STARTUP mov ax, 1234h mov bx, 5678h call ntrace .EXIT 0 ;============================================== ; Функция выводит на экран содержимое ; всех регистров и приостанавливает выполнение ; программы до тех пор, пока пользователь не ; нажмет на любую клавишу. ; После возвращения из процедуры ; все регистры восстанавливаются. ;============================================== ntrace proc near ; Сохраняем в стеке регистры, ; содержимое которых будет изменяться pushf push ax push bx push cx push dx push ds push bp push cs pop ds mov bp, sp ; Выводим сообщение об остановке mov dx, offset cs:trace_msg @@out_str ; Выводим содержимое всех регистров mov ax, cs ; cs call Print_word @@out_ch ':' mov ax, [bp]+14 ; ip call Print_word @@out_ch 13,10,13,10,'A','X','=' mov ax, [bp]+10 call Print_word @@out_ch ' ','B','X','=' mov ax, [bp]+8 call Print_word @@out_ch ' ','C','X','=' mov ax, [bp]+6 call Print_word @@out_ch ' ','D','X','=' mov ax, [bp]+4 call Print_word @@out_ch ' ','S','P','=' mov ax, bp add ax, 16 call Print_word @@out_ch ' ','B','P','=' mov ax, [bp] call Print_word @@out_ch ' ','S','I','=' mov ax, si call Print_word @@out_ch ' ','D','I','=' mov ax, di call Print_word @@out_ch 13,10,'D','S','=' mov ax, [bp]+2 call Print_word @@out_ch ' ','E','S','=' mov ax, es call Print_word @@out_ch ' ','S','S','=' mov ax, ss call Print_word @@out_ch ' ','F','=' mov ax, [bp]+12 call Print_word lea dx, cs:hit_msg @@out_str ; Ожидаем, когда пользователь нажмет ; на любую клавишу mov ax, 0 int 16h ; Восстанавливаем содержимое регистров pop bp pop ds pop dx pop cx pop bx pop ax popf ret trace_msg db 13,10,'----> Break аt address ','$' hit_msg db 13,10,'Press any key...','$' ntrace endp ;============================================== ; Вывод на экран содержимого регистра AX ;============================================== Print_word proc near push ax push bx push dx push ax mov cl, 8 rol ax, cl call Byte_to_hex mov bx, dx @@out_ch bh @@out_ch bl pop ax call Byte_to_hex mov bx, dx @@out_ch bh @@out_ch bl pop dx pop bx pop ax ret Print_word endp ;============================================== ; Преобразование байта в два символа ; AL - преобразуемый байт ; DX - его символьное представление ;============================================== Byte_to_hex proc near push ds push cx push bx lea bx, tabl mov dx, cs mov ds, dx push ax and al, 0fh xlat mov dl, al pop ax mov cl, 4 shr al, cl xlat mov dh, al pop bx pop cx pop ds ret tabl db '0123456789ABCDEF' Byte_to_hex endp END Для ассемблирования и редактирования этой программы используйте пакетный файл, приведенный в листинге 6.3. Листинг 6.3. Файл ntrace\mk.bat tasm ntrace tlink ntrace /t При использовании другого метода отладки драйвера вы можете построить макет драйвера в виде com-программы. Используя свой любимый отладчик, вы сможете проверить работу большинства входящих в драйвер функций. Если у вас есть отладчик Advanced Fullscreen Debugger, можно использовать его способность оставаться резидентным. Отладчик активизируется в тот момент, когда пользователь нажмет комбинацию клавиш <Ctrl+Esc>. В интересующее вас место драйвера поместите вызов прерывания INT 16h , ожидающий ввод с клавиатуры, например: push ax mov ax,0 int 16h pop ax В этом фрагменте кода можно сохранить в стеке и регистр флагов, если его изменение нежелательно. После того как драйвер остановится, ожидая ввод, активизируйте отладчик, нажав комбинацию клавиш <Ctrl+Esc>. Вы окажетесь в теле обработчика прерывания INT 16h . Выполняя программу по шагам, довольно скоро вы достигнете выхода из этого обработчика - команды IRET. После выполнения команды IRET управление будет передано команде, следующей за командой INT 16h. Эта команда (в приведенном примере - pop ax) принадлежит вашему драйверу! Используя известное теперь значение адреса заголовка запроса (регистры ES:BX), можно просмотреть запрос и определить, какая команда выполняется драйвером. Выполнив все необходимые отладочные действия, запустите программу на выполнение без отладки. В нужный момент времени вы снова сможете вызвать отладчик тем же способом. И еще одно замечание. Драйвер использует системный стек, имеющий довольно небольшой размер. Поэтому при необходимости организуйте свой стек в области памяти, принадлежащей драйверу. Очень важно, чтобы драйвер перед началом работы сохранил содержимое всех регистров, включая регистр флагов, а перед возвращением управления операционной системе восстановил старое содержимое регистров. Критичные участки драйвера, связанные с обращением к портам периферийных устройств, должны выполняться с замаскированными прерываниями. |