Ассемблер и программирование для IBM PC

       

Программный загрузчик


------------------------------------------------------------

Программный загрузчик

Цель: Раскрыть особенности загрузки выполнимых модулей в память для выполнения.

ВВЕДЕНИЕ ------------------------------------------------------------

В данной главе описана организация базовой версии DOS и операции, которые выполняет DOS для загрузки выполнимых модулей в память для выполнения. DOS состоит из четырех основных программ, которые обеспечивают конкретные функции:

1. Блок начальной загрузки находится на первом секторе нулевой дорожки дискеты DOS, а также на любом диске, форматированном командой FORMAT /S. Когда вы иницииру ете систему (предполагается, что DOS расположен на дисководе A или C) происходит автоматическая загрузка с диска в память блока начальной загрузки. Этот блок представляет собой программу, которая затем загружает с диска в память три программы, описанные ниже.

2. Программа IBMBIO.COM обеспечивает интерфейс низкого уровня с программами BIOS в ROM; она загружается в память, начиная с адреса шест.00600. При инициализации программа IBMBIO.COM определяет состояние всех устройств и оборудования, а затем загружает программу COMMAND.COM. Программа IBMBIO.COM управляет операциями ввода-вывода между памятью и внешними устройствами, такими как видеомонитор и диск.

3. Программа IBMDOS.COM обеспечивает интерфейс высокого уровня с программами и загружается в память, начиная с адреса шест.00B00. Эта программа управляет оглавлениями и файлами на диске, блокированием и деблокированием дисковых записей, функциями INT 21H, а также содержит ряд других сервисных функций.

4. Программа COMMAND.COM выполняет различные команды DOS, такие как DIR или CHKDSK, а также выполняет COM, EXE и BAT-программы. Она состоит из трех частей: небольшая резидентная часть, часть инициализации и транзитная часть. Программа COMMAND.COM, подробно расмотренная в следующем разделе, отвечает за загрузку выполняемых программ с диска в память.

На рис.22.1 показана карта распределения памяти. Некото рые элементы могут отличаться в зависимости от модели компьютера.


Ассемблер для IBM PC. Глава 22 34
------------------------------------------------------------ Начальный Программа адрес 00000 Векторная таблица прерываний (см.гл.23) 00400 Область связи с ROM (ПЗУ) 00500 Область связи с DOS 00600 IBMBIO.COM XXXX0 IBMDOS.COM Буфер каталога Дисковый буфер Таблица параметров дисковода или таблица распределения файлов (FAT, по одной для каждого дисковода) XXXX0 Резидентная часть COMMAND.COM XXXX0 Внешние команды или утилиты (COM или EXE-файлы) XXXX0 Пользовательский стек для COM-файлов (256 байтов) XXXX0 Транзитная часть COMMAND.COM, записывается в самые старшие адреса памяти. ------------------------------------------------------------ Рис.22.1. Карта распределения DOS в памяти.


КОМАНДНЫЙ ПРОЦЕССОР COMMAND.COM ------------------------------------------------------------
Система загружает три части программы COMMAND.COM в память во время сеанса работы постоянно или временно. Ниже описано назначение каждой из трех чатей COMMAND.COM:
1. Резидентная часть непосредственно следует за программой IBMDOS.COM (и ее области данных), где она находится на протяжении всего сеанса работы. Резидентная часть обрабатывает все ошибки дисковых операций ввода-вывода и управляет следующими прерываниями:
INT 22H Адрес программы обработки завершения задачи. INT 23H Адрес программы реакции на Ctrl/Break. INT 24H Адрес программы реакции на ошибоки дисковых операций чтения/записи или сбойный участок памяти в таблице распределения файлов (FAT). INT 27H Завершение работы, после которого программа остается резидентной.
2. Часть инициализации непосредственно следует за резидент ной чатью и содержит средства поддержки AUTOEXEC- файлов. В начале работы системы данная часть первой получает управление. Она выдает запрос на ввод даты и определяет сегментный адрес, куда система должна загружать программы для выполнения. Ни одна из этих программ инициализации не потребуются больше во время сеанса работы. Поэтому первая же команда вводимая с клавиатуры и вызывающая загрузку некоторой программы с диска перекрывают часть инициализации в памяти.


Ассемблер для IBM PC. Глава 22 35
3. Транзитная часть загружается в самые старшие адреса памяти. "Транзит" обозначает, что DOS может перекрыть данную область другими программами, если потребуется. Транзитная часть программы COMMAND.COM выводит на экран приглашение DOS A> или C>, вводит и выполняет запросы. Она содержит настраивающий загрузчик и предназначена для загрузки COM- или EXE-файлов с диска в память для выполнения. Если поступил запрос на выполнение какой-либо программы, то транзитная часть строит префикс программного сегмента (PSP) непосредственно вслед за резидентной частью COMMAND.COM. Затем она загружает запрошенную программу с диска в память по смещению шест.100 от начала программного сегмента, устанавливает адреса выхода и передает управление в загруженную программу. Ниже приведена данная последовательность:
IBMBIO.COM IBMDOS.COM COMMAND.COM (резидент) Префикс программного сегмента Выполняемая программа ... COMMAND.COM (транзитная часть, может быть перекрыта).
Выполнение команды RET или INT 20H в конце программы приводит к возврату в резидентную часть COMMAND.COM. Если транзитная часть была перекрыта, то резидентная часть перезагружает транзитную часть с диска в память.
ПРЕФИКС ПРОГРАММНОГО СЕГМЕНТА ------------------------------------------------------------
Префикс программного сегмента (PSP) занимает 256 (шест. 100) байт и всегда предшествует в памяти каждой COM- или EXE-программе, которая должна быть выполнена. PSP содержит следующие поля:
00 Команда INT 20H (шест.CD20). 02 Общий размер доступной памяти в формате хххх0. Напри мер, 512K указывается как шест.8000 вместо шест.80000. 04 Зарезервировано. 05 Длинный вызов диспетчера функций DOS. OA Адрес подпрограммы завершения. OE Адрес подпрограммы реакции на Ctrl/Break. 12 Адрес подпрограммы реакции на фатальную ошибку. 16 Зарезервировано. 2C Сегментный адрес среды для хранения ASCIIZ строк. 50 Вызов функций DOS (INT 21H и RETF). 5C Параметрическая область 1, форматированная как стандарт ный неоткрытый блок управления файлов (FCBЭ1).


Ассемблер для IBM PC. Глава 22 36
6C Параметрическая область 2, форматированная как стандарт ный неоткрытый блок управления файлом (FCBЭ2); перекры вается, если блок FCBЭ1 открыт. 80-FF Буфер передачи данных (DTA).
Буфер передачи данных DTA
Данная часть PSP начинается по адресу шест.80 и представляет собой буферную область ввода-вывода для текущего дисковода. Она содержит в первом байте число, указывающее сколько раз были нажаты клавиши на клавиатуре непосредственно после ввода имени программы. Начиная со второго байта, находятся введенные символы (если таковые имеются). Далее следует всевозможный "мусор", оставшийся в памяти после работы предыдущей программы. Следующие примеры демонстрируют назначение буфера DTA:
Пример 1. Команда без операндов. Предположим, что вы выз вали программу CALCIT.EXE для выполнения с помощью команды CALCIT [return]. После того, как DOS построит PSP для этой программы, он установит в буфере по адресу шест.80 значение шест.000D. Первый байт содержит число символов, введенных с клавиатуры после имени CALCIT, исключая символ "возврат каретки". Так как кроме клавиши Return не было нажато ни одной, то число символов равно нулю. Второй байт содержит символ возврата каретки, шест.0D. Таким образом, по адресам шест.80 и 81 на ходятся 000D.
Пример 2. Команда с текстовым операндом. Предположим, что после команды был указан текст (но не имя файла), например, COLOR BY, обозначающий вызов программы COLOR и передачу этой программе параметра "BY" для установки голубого цвета на желтом фоне. В этом случае, начиная с адреса шест.80, DOS установит следующие значения байт:
80: 03 20 42 59 0D
Эти байты обозначают длину 3, пробел, "BY" и возврат каретки.
Пример 3. Команда с именем файла в операнде. Программы типа DEL (удаление файла) предполагают после имени программы ввод имени файла в качестве параметра. Если будет введено, например, DEL B:CALCIT.OBJ [return], то PSP, начиная с адресов шест.5C и шест.80, будет содержать:


5C: 02 43 41 4C 43 49 54 20 20 4F 42 4A C A L C I T O B J 80: 0D 20 42 3A 43 41 4C 43 49 54 2E 4F 42 4A 0D B : C A L C I T . 0 B J
Ассемблер для IBM PC. Глава 22 37
Начиная с адреса шест.5C, находится неоткрытый блок FCB, содержащий имя файла, который был указан в параметре, CALCIT.OBJ, но не имя выполняемой программы. Первый символ указывает номер дисковода (02=B в данном случае). Следом за CALCIT находятся два пробела, которые дополняют имя файла до восьми символов, и тип файла, OBJ. Если ввести два параметра, например:
progname A:FILEA,B:FILEB
тогда DOS построит FCB для FILEA по смещению шест.5C и FCB для FILEB по смещению шест.6C. Начиная с адреса шест.80 в этом случае содержится число введенных символов (длина параметров) - 16, пробел (шест.20) A:FILEA,B:FILEB и символ возврат каретки (OD). Так как PSP непосредственно предшествует вашей программе, то возможен доступ к области PSP для обработки указанных файлов или для предпринятия определенных действий. Для локализации буфера DTA COM-программа может просто поместить шест.80 в регистр SI и получить доступ следующим образом:
MOV SI,80H ;Адрес DTA CMP BYTE PTR [SI],0 ;В буфере нуль? JE EXIT
Для EXE-программы нельзя с уверенностью утверждать, что кодовый сегмент непосредственно располагается после PSP. Однако, здесь при инициализации регистры DS и ES содержат адрес PSP, так что можно сохранить содержимое регистра ES после загрузки регистра DS:
MOV AX,DSEG MOV DS,AX MOV SAVEPSP,ES
Позже можно использовать сохраненный адрес для доступа к буферу PSP:
MOV SI,SAVEPSP CMP BYTE PTR [SI+ 80H],0 ;В буфере нуль? JE EXIT
DOS версии 3.0 и страше содержит команду INT 62H, загружаю щую в регистр BX адрес текущего PSP, который можно использо вать для доступа к данным в PSP.
ВЫПОЛНЕНИЕ COM-ПРОГРАММЫ ------------------------------------------------------------
В отличие от EXE-файла, COM-файл не содержит заголовок на диске. Так как организация COM-файла намного проще, то для DOS необходимо "знать" только то, что тип файла - COM.


Ассемблер для IBM PC. Глава 22 38
Как описано выше, загруженным в память COM- и EXE-файлам предшествует префикс программного сегмента. Первые два байта этого префикса содержат команду INT 20H (возврат в DOS). При загрузке COM-программы DOS устанавливает в четырех сегментных регистрах адрес первого байта PSP. Затем устанавливается указатель стека на конец 64 Кбайтового сегмента (шест.FFFE) или на конец памяти, если сегмент не достаточно большой. В вершину стека заносится нулевое слово. В командный указатель помещается шест.100 (размер PSP). После этого управление передается по адресу регистровой пары CS:IP, т.е. на адрес непосредственно после PSP. Этот адрес является началом выполняемой COM-программы и должен содержать выполнимую команду. При выходе из программы команда RET заносит в регистр IP нулевое слово, которое было записано в вершину стека при инициализации. В этом случае в регистровой паре CS:IP получается адрес первого байта PSP, где находится команда INT 20H. При выполнении этой команды управление передается в резидентную часть COMMAND.COM. (Если программа завершается по команде INT 20H вместо RET, то управление непосредственно передается в COMMAND.COM).
ВЫПОЛНЕНИЕ EXE-ПРОГРАММЫ ------------------------------------------------------------
EXE-модуль, созданный компановщиком, состоит из следующих двух частей: 1) заголовок - запись, содержащая информацию по управлению и настройке программы и 2) собственно загрузоч ный модуль. В заголовке находится информация о размере выполняемого модуля, области загрузки в памяти, адресе стека и относитель ных смещениях, которые должны заполнить машинные адреса в соответствии с относительными шест. позициями:
00 Шест.4D5A. Компановщик устанавливает этот код для иден тификации правильного EXE-файла. 02 Число байтов в последнем блоке EXE-файла. 04 Число 512 байтовых блоков EXE-файла, включая заголо вок. 06 Число настраиваемых элементов. 08 Число 16-тибайтовых блоков (параграфов) в заголовке, (необходимо для локализации начала выполняемого модуля, следующего после заголовка). OA Минимальное число параграфов, которые должны находится после загруженной программы. OC Переключатель загрузки в младшие или старшие адреса. При компановке программист должен решить, должна ли его программа загружаться для выполнения в младшие адреса памяти или в старшие. Обычным является звгрузка в млад шие адреса. Значение шест.0000 указывает на загрузку в старшие адреса, а шест.FFFF - в младшие. Иные значения определяют максимальное число параграфов, которые должны находиться после загруженной программы.


Ассемблер для IBM PC. Глава 22 39
OE Относительный адрес сегмента стека в выполняемом модуле. 10 Адрес, который загрузчик должен поместить в регистр SP перед передачей управления в выполнимый модуль. 12 Контрольная сумма - сумма всех слов в файле (без учета переполнений) используется для проверки потери данных. 14 Относительный адрес, который загрузчик должен поместить в регистр IP до передачи управления в выполняемый модуль. 16 Относительный адрес кодового сегмента в выполняемом модуле. Этот адрес загрузчик заносит в регистр CS. 18 Смещение первого настраиваемого элемента в файле. 1A Номер оверлейного фрагмента: нуль обозначает, что заго ловок относится к резидентной части EXE-файла. 1C Таблица настройки, содержащая переменное число настраиваемых элементов, соответствующее значению по смещению 06.
Заголовок имеет минимальный размер 512 байтов и может быть больше, если программа содержит большое число настраи ваемых элементов. Позиция 06 в заголовке указывает число элементов в выполняемом модуле, нуждающихся в настройке. Каждый элемент настройки в таблице, начинающейся в позиции 1C заголовка, состоит из двухбайтовых величин смещений и двухбайтовых сегментных значений. Система строит префикс программного сегмента следом за резидентной часью COMMAND.COM, которая выполняет операцию загрузки. Затем COMMAND.COM выполняет следующие действия:
ъ Считывает форматированную часть заголовка в память. ъ Вычисляет размер выполнимого модуля (общий размер файла в позиции 04 минус размер заголовка в позиции 08) и загружает модуль в память с начала сегмента. ъ Считывает элементы таблицы настройки в рабочую область и прибавляет значения каждого элемента таблицы к началу сегмента (позиция OE). ъ Устанавливает в регистрах SS и SP значения из заголовка и прибавляет адрес начала сегмента. ъ Устанавливает в регистрах DS и ES сегментный адрес префикса программного сегмента. ъ Устанавливает в регистре CS адрес PSP и прибавляет вели чину смещения в заголовке (позиция 16) к регистру CS. Если сегмент кода непосредственно следует за PSP, то смещение в заголовке равно 256 (шест.100). Регистровая пара CS:IP содержит стартовый адрес в кодовом сегменте, т.е. начальный адрес программы.


После инициализации регистры CS и SS содержат правильные адреса, а регистр DS (и ES) должны быть установлены в программе для их собственных сегментов данных:
1. PUSH DS ;Занести адрес PSP в стек 2. SUB AX,AX ;Занести нулевое значение в стек
Ассемблер для IBM PC. Глава 22 40
3. PUSH AX ; для обеспечения выхода из программы 4. MOV AX,datasegname ;Установка в регистре DX 5. MOV DS,AX ; адреса сегмента данных
При завершении программы команда RET заносит в регистр IP нулевое значение, которое было помещено в стек в начале выполнения программы. В регистровой паре CS:IP в этом случае получается адрес, который является адресом первого байта PSP, где расположена команда INT 20H. Когда эта команда будет выполнена, управление перейдет в DOS.
ПРИМЕР EXE-ПРОГРАММЫ ------------------------------------------------------------
Рассмотрим следующую таблицу компановки (MAP) программы:
Start Stop Length Name Class 00000H 0003AH 003BH CSEG CODE 00040H 0005AH 001BH DSEG DATA 00060H 0007FH 0020H STACK STACK Program entry point at 0000:0000
Таблица MAP содержит относительные (не действительные) адреса каждого из трех сегментов. Символ H после каждого значения указывает на шестнадцатиричный формат. Заметим, что компановщик может организовать эти сегменты в последователь ности отличного от того, как они были закодированы в програм ме. В соответствии с таблицей MAP кодовый сегмент CSEG нахо дится по адресу 00000 - этот относительный адрес является началом выполняемого модуля. Длина кодового сегмента составляет шест.003B байтов. Следующий сегмент по имени DSEG начинается по адресу шест.00040 и имеет длину шест.001B. Адрес шест.00040 является первым после CSEG адресом, выров ненным на границу параграфа (т.е. это значение кратно шест.10). Последний сегмент, STACK, начинается по адресу шест.00060 - первому после DSEG, адресу выровненному на границу параграфа. С помощью отладчика DEBUG нельзя проверить содержимое заголовка, так как при загрузке программы для выполнения DOS замещает заголовок префиксом программного сегмента. Однако, на рынке программного обеспечения имеются различные сервис ные утилиты (или можно написать собственную), которые позво ляют просматривать содержимое любого дискового сектора в шестнадцатиричном формате. Заголовок для рассматриваемого примера программы содержит следующую информацию (содержимое слов представлено в обратной последовательности байтов).


00 Шест.4D5A. 02 Число байтов в последнем блоке: 5B00. 04 Число 512 байтовых блоков в файле, включая заголовок: 0200 (шест.0002х512=1024).
Ассемблер для IBM PC. Глава 22 41
06 Число элементов в таблице настройки, находящейся после форматированной части заголовка: 0100, т.е. 0001. 08 Число 16 байтовых элементов в заголовке: 2000 (шест.0020=32 и 32х16=512). 0C Загрузка в младшие адреса: шест.FFFF. 0E Относительный адрес стекового сегмента: 6000 или шест. 60. 10 Адрес для загрузки в SP: 2000 или шест.20. 14 Смещение для IP: 0000. 16 Cмещение для CS: 0000. 18 Cмещение для первого настраиваемого элемента: 1E00 или шест.1E.
После загрузки программы под управлением отладчика DEBUG регистры получают следующие значения:
SP = 0020 DS = 138F ES = 138F SS = 13A5 CS = 139F IP = 0000
Для EXE-модулей загрузчик устанавливает в регистрах DS и ES адрес префикса программного сегмента, помещенного в доступной области памяти, а в регистрах IP, SS и SP - значения из заголовка программы.
Регистр SP
Загрузчик использует шест.20 из заголовка для инициализа ции указателя стека значением длины стека. В данном примере стек был определен, как 16 DUP (?), т.е. 16 двухбайтовых полей общей длиной 32 (шест.20) байта. Регистр SP указывает на текущую вершину стека.
Регистр CS
В соответствии со значением в регистре DS после загрузки программы, адрес PSP равен шест.138F(0). Так как PSP имеет длину шест.100 байтов, то выполняемый модуль, следующий непо средственно после PSP, находится по адресу шест.138F0+100= 139F0. Это значение устанавливается загрузчиком в регистре CS. Таким образом, регистр CS определяет начальный адрес кодовой части программы (CSEG). С помощью команды D CS:0000 в отладчике DEBUG можно просмотреть в режиме дампа машинный код в памяти. Обратите внимание на идентичность дампа и шестнадцатиричной части ассемблерного LST файла кроме операндов, отмеченных символом R.
Регистр SS
Для установки значения в регистре SS загрузчик также использует информацию из заголовка:
Начальный адрес PSP (см.DS) 138F0 Длина PSP 100


Ассемблер для IBM PC. Глава 22 42
Относительный адрес стека 60 Адрес стека 13A50
Регистр DS
Загрузчик использует регистр DS для установки начального адреса PSP. Так как заголовок не содержит стартового адреса, то регистр DS необходимо инициализировать в программе следую щим образом:
0004 B8 ---- R MOV AX,DSEG 0007 8E D8 MOV DS,AX
Ассемблер оставляет незаполненным машинный адрес сегмента DSEG, который становится элементом таблицы настройки в заго ловке. С помощью отладчика DEBUG можно просмотреть завершен ную команду в следующем виде:
B8 A313
Значение A313 загружается в регистр DS в виде 13A3. В результате имеем
Регистр Адрес Смещение CS 139F0 00 DS 13A30 40 SS 13A50 60
В качестве упражнения выполните трассировку любой вашей скомпанованной программы под управлением отладчика DEBUG и обратите внимание на изменяющиеся значения в регистрах:
Команда Изменяющиеся регистры PUSH DS IP и SP SUB AX,AX IP и AX (если был не нуль) PUSH AX IP и SP MOV AX,DSEG IP и AX MOV DS,AX IP и DS
Регистр DS содержит теперь правильный адрес сегмента данных. Можно использовать теперь команду D DS:00 для просмотра содержимого сегмента данных DSEG и команду D SS:00 для просмотра содержимого стека.
ФУНКЦИИ ЗАГРУЗКИ И ВЫПОЛНЕНИЯ ПРОГРАММЫ ------------------------------------------------------------
Рассмотрим теперь, как можно загрузить и выполнить программу из другой программы. Функция шест.4B дает возможность одной программе загрузить другую программу в память и при необходимости выполнить. Для этой функции необходимо загрузить адрес ASCIIZ-строки в регистр DX, а
Ассемблер для IBM PC. Глава 22 43
адрес блока параметров в регистр BX (в действительности в регистровую пару ES:BX). В регистре AL устанавливается номер функции 0 или 3:
AL=0. Загрузка и выполнение. Данная операция устанавлива ет префикс программного сегмента для новой программы, а также адрес подпрограммы реакции на Cntrl/Break и адрес передачи управления на следующую команду после завершения новой программы. Так как все регистры, включая SP, изменяют свои значения, то данная операция не для новичков. Блок параметров, адресуемый по ES:BX, имеет следующий формат:


Смещение Назначение 0 Двухбайтовый сегментный адрес строки параметров для передачи. 2 Четырехбайтовый указатель на командную строку в PSP+80H. 6 Четырехбайтовый указатель на блок FCB в PSP+5CH. 10 Четырехбайтовый указатель на блок FCB в PSP+6CH.
AL=3. Оверлейная загрузка. Данная операция загружает программу или блок кодов, но не создает PSP и не начинает выполнение. Таким образом можно создавать оверлейные программы. Блок параметров адресуется по регистровой паре ES:BX и имеет следующий формат:
Смещение Назначение 0 Двухбайтовый адрес сегмента для загрузки файла. 2 Двухбайтовый фактор настройки загрузочного модуля.
Возможные коды ошибок, возвращаемые в регистре AX: 01, 02, 05, 08, 10 и 11. Программа на рис.22.2 запрашивает DOS выполнить команду DIR для дисковода D. Выполните эту программу, как EXE-модуль. (Автор благодарен журналу PC Magazine за эту идею).
------------------------------------------------------------ ------------------------------------------------------------ Рис.22.2. Выполнение команды DIR из программы.
Ассемблер для IBM PC. Глава 23 55


    Содержание раздела