Редактирование межмодульных связей
Такая корректировка заключается в замене внешних имен, использовавшихся в модулях, на соответствующие адреса. Делается это так.
Напомним, что в заголовке каждого ОМ есть таблица общих имен (ТОБ), в которой для каждого общего имени данного модуля указано само имя и его адрес внутри модуля. Компоновщик выделяет из заголовков эти таблицы и объединяет их в общую таблицу общих имен (ОТОБ). Например, если в программе имеется два модуля М1
и М2 с такими ТОБ:
модуль М1: модуль М1:
общее имя адрес общее имя адрес
------------------ ------------------
B S2:2 X Q1:20
P Q2:0
тогда ОТОБ будет выглядеть так:
Общая ТОБ: общее имя адрес
---------------------
B S2:2
X Q1:20
P Q2:0
Теперь вспомним, что общее имя одного модуля - это внешнее имя другого модуля. Значит, ОТОБ - это одновременно и таблица всех внешних имен с указанием их адресов. Поэтому компоновщик теперь может сделать то, что в свое время не удалось сделать ассемблеру, - заменить все внешние ссылки во всех модулях на соответствующие им адреса.
Для этого компоновщик использует таблицы вхождений внешних имен (ТВН) из объектных модулей. Напомним, что в такой таблице указаны сведения о каждом вхождении в модуль каждого внешнего имени, а именно: само имя, адрес ячейки, в которую надо записать адрес имени, и то, какую часть адреса имени надо использовать. Компоновщик проходится по всем этим таблицам и делает замены.
Пусть, к примеру, в модуле М1 была такая ТВН:
внеш.имя адрес вхождения тип вхождения
------------------------------------------
X S2:0 ofs
X S2:2 seg
P S2:6 segofs
Первая ее строка указывает, что смещение имени X
надо записать в ячейку с адресом S2:0, т.е. в 0- ю ячейку сегмента S2. Смещение имени X компоновщик узнает по ОТОБ, оно равно 20h, а начальный адрес сегмента узнает по ОТС, он равен 1000h, поэтому число 20h он заносит в ячейку с адресом 1000h+0=1000h, отсчитанным от начала программы.
Следующая строка таблицы указывает, что в ячейку с адресом S2:2 надо записать номер сегмента (начальный адрес без последнего 0), в котором находится ячейка с именем X. По ОТОБ компоновщик узнает, что сегментом имени X
является Q1. Однако компоновщик не знает настоящий начальный адрес этого сегмента: он знает только его адрес относительно начала программы, а настоящий же адрес зависит от того, с какого места памяти будет расположена вся программа при счете, а это пока неизвестно. Что делать компоновщику? Напомним, что с такой же проблемой сталкивается и ассемблер. Как поступает ассемблер? Он ничего не записывает в соответствующую ячейку, но в таблице перемещаемых адресов (ТПА) запоминает, что затем в эту ячейку надо будет записать номер этого сегмента. Аналогично поступает и компоновщик: он строит свою (новую) ТПА, где запоминает, в какие ячейки он должен был бы записать номера каких сегментов, но не смог этого сделать. У нас в этой ТПА появится первая из следующих строк:
имя сегмента адрес вхождения
------------------------------
Q1 S2:2
Q2 S2:8
...
Третья строка ТВН из модуля M1 указывает, что в ячейку S2:6 надо записать и смещение, и номер сегмента имени P, т.е. здесь объединены два уже рассмотренных нами случая. Узнав по ОТОБ, что смещение имени P равно 0, компоновщик записывает 0 в ячейку S2:6. Из ОТОБ компоновщик узнает, что имя P
- из сегмента Q2, однако записать номер этого сегмента во вторую половину данной ячейки (в S2:8) не может, поэтому он добавляет в свою ТПА новый элемент - вторую из указанных выше строк.
Далее компоновщик просматривает ТВН из следующих модулей и поступает с ними аналогично.
На этом заканчивается замена внешних имен на их адреса, т.е. редактирование межмодульных связей. Полученный таким образом машинный код и является телом загрузочного модуля. Ничего более в нем компоновщик не будет менять.