Chapter_10 (1110562), страница 3
Текст из файла (страница 3)
Заметим,что переход на внешнюю метку Error – это тоже статическая связь по управлению, так как адресметки известен до начала счёта. В то же время возврат из внешней процедуры по команде ret является динамической связью по управлению, так как конкретный адрес возврата в другой модульбудет помещён в стек только во время счёта программы.Программа Ассемблера не в состоянии перевести каждый исходный модуль в готовый к счётуфрагмент программы на машинном языке, так как, во-первых, не может определить внешние адресамодуля, а, во-вторых, не знает будущего расположения сегментов модуля в памяти. Говорят, что Ассемблер переводит исходный модуль на специальный промежуточный язык, который называется1Иногда в таких случаях говорят, что имя A объявлено в сегменте Data (правда термин "объявить имя"используется в основном в языках высокого уровня).
Объявление имени переменной в некотором сегменте модуля, в отличие от описания этого имени, не требует выделения для переменной памяти в данном сегментеэтого модуля. Для Ассемблера такое объявление переменной является также и указанием (директивой) о том,что во время счёта программы данная переменная будет находиться в памяти именно этого сегмента, как этообеспечивается, мы узнаем несколько позже.7объектным языком.
Следовательно, программа Ассемблер преобразует входной модуль в объектный модуль. Полученный объектный модуль оформляется в виде файла, имя этого файла обычносовпадает с именем исходного файла на языке Ассемблер, но имеет другое расширение. Так, нашиисходные файлы p1.asm и p2.asm будут переводиться (или, как чаще говорят, компилироватьсяили транслироваться) в объектные файлы с именами p1.obj и p2.obj.Рассмотрим теперь, чего не хватает в объектном модуле, чтобы быть готовым к счёту фрагментом программы на машинном языке.
Например, самая первая команда всех наших программmov ax,Dataдолжна переводится в машинную команду пересылки формата mov ax,i16 , однако значение константы i16, которая равна физическому адресу начала сегмента Data в памяти, делённому на 16,неизвестна программе Ассемблера, поэтому поле операнда i16 в команде пересылки остаётся незаполненным. Таким образом, в объектном модуле некоторые адреса остаются неизвестными (неопределёнными). До начала счёта программы, однако, все такие адреса обязательно должны получитьконкретные значения.Объектный модуль, получаемый программой Ассемблера, состоит из двух частей: тела модуляи паспорта (или заголовка) модуля. Тело модуля состоит из сегментов, в которых находятся команды и переменные (области памяти) нашего модуля, а паспорт содержит описание структуры объектного модуля. В этом описании содержатся следующие данные об объектном модуле.• Сведения обо всех сегментах модуля (длина сегмента, его спецификация).• Сведения обо всех общедоступных (экспортируемых) именах модуля, заданных директивами public, с каждым таким именем связан его тип (abs,byte,word,near и т.д.) иадрес (входная точка) внутри какого-либо сегмента модуля (для константы типа abs этоне адрес, а просто целое число – значение этой константы).• Сведения об именах и типах всех внешних адресов модуля, заданных в директивахextrn.• Сведения о местоположении всех остальных незаполненных полей в сегментах модуля,для каждого такого поля задано его месторасположение в сегменте и информация о способе его заполнения перед началом счёта.• Другая информация, необходимая для сборки программы из модулей.p1.objP2.objStk segment stackData segment publicPublic A:wordPublic N:absextrn Summa:wordANSummaCode segment publicExtrn Sum:farPublic Error:nearSumErrorANSummaSumErrorData segment publicextrn A:wordextrn N:abspublic Summa:wordCode segment publicpublic Sum:farextrn Error:nearРис.
10.1. Схематический вид двух объектных модулей свнешними адресами и входными точками.На рис. 10.1 показано схематическое изображение объектных модулей p1.obj и p2.obj, независимо полученных программой Ассемблера, для каждого модуля изображены его сегменты, входные точки и внешние адреса. Вся эта информация содержится в паспортах объектных модулей (напомним, что для простоты изложения мы не принимаем во внимание третий модуль нашей программы с именем ioproc). 11Все имена исходного модуля, кроме внешних имён и имён входных точек, обычно заменяются в объектном модуле их численными значениями.
Например, уже на важно, какое имя имела та или иная (локальная)переменная или метка программы, эта информация больше никому не понадобится (исключением являются так8Обратимся теперь к проблеме сборки полной программы из модулей. Как мы уже упоминали,эту работу выполняет специальная системная программа, которая называется редактором внешнихсвязей.
Из этого названия хорошо видно одно из назначений этой программы – редактировать (всмысле устанавливать, настраивать) связи между внешними адресами и входными точками модулей.Рассмотрим схему работы редактора внешних связей на нашем предыдущем примере.10.2. Схема работы редактора внешних связейЦелью работы редактора внешних связей является построение из объектных модулей почти готового к счёту программы, которая называется загрузочным модулем. Загрузочный модуль всё ещёне является полностью готовой к счёту программой на машинном языке, в этом модуле остаются незаполненными некоторые поля. Например, наша первая команда в головном модулеmov ax,Dataвсё ещё будет иметь незаполненное поле Data формата i16 на месте второго операнда, так как конкретное значение этого поля (адрес сегмента данных) будет известно только перед самым началомсчёта программы, когда все её сегменты будут размещены в памяти компьютера.При вызове редактора внешних связей ему в качестве параметров передаются имена всех объектных модулей, а также имя загрузочного модуля, который необходимо построить.
Для нашегопримера вызов редактора внешних связей (в нашей системе программирования его имя link) будетвыглядеть, например, такlink p1+p2+ioproc,pЗдесь p1,p2 и ioproc – имена объектных модулей (не забывайте о третьем объектном модулес именем ioproc), а p – имя загрузочного модуля, который надо построить. 1 Первый из перечисленных объектных модулей считается головным модулем, с него начинается процесс сборки загрузочного модуля.
Работа редактора внешних связей включает в себя два этапа. На первом этапе происходит обработка сегментов, а на втором – собственно редактирование внешних связей и построение загрузочного модуля (загрузочные модули для нашего компьютера имеют расширение .exe).Разберёмся сначала с первым этапом.В нашем примере (если не принимать во внимание объектный модуль ioproc.obj) имеетсяпять сегментов: три сегмента с именами Stk,Data и Code в модуле p1.obj и два сегмента с именами Data и Code в модуле p2.obj.
Спрашивается, сколько сегментов будет в загрузочном модулеp.exe ? Здесь логически возможны три случая.• Все сегменты переходят в загрузочный модуль. В этом случае в нашем модуле p.exeдолжно было бы быть 5 сегментов: один стековый, два кодовых и два сегмента данных.• Некоторые из сегментов склеиваются, то есть один сегмент присоединяется в конец другого сегмента.• Некоторые из сегментов накладываются друг на друга (если сегменты имеют разнуюдлину, то, конечно, более длинный сегмент будет "торчать" из-под более короткого сегмента). Разумеется, почти всегда накладывать друг на друга имеет смысл только сегментыданных, в этом случае у нескольких модулей будут общие сегменты данных (или, какиногда говорят, общие области (или блоки) данных).Как именно будут обрабатываться сегменты при сборке загрузочного модуля из объектных модулей, определяет сам программист, задавая определённые параметры в директивах segment.Существуют следующие параметры, управляющие обработкой сегментов редактором внешних связей.Параметр public у одноимённых сегментов означает их склеивание.
2 Так как сборка начинается с головного модуля, то из двух одноимённых сегментов с параметром public сегмент из гоназываемые "отладчики в терминах входного языка", но для Ассемблеров они практически не используются имы не будем касаться этой темы).1Если объектных модулей очень много, то существует более компактный способ задать их, не перечисляяих все в строке вызова редактора внешних связей.2Вообще говоря, в нашем языке Ассемблера склеиваемые сегменты должны ещё принадлежать к одномуи тому же классу сегментов. В рассмотренных нами примерах это требование выполняется, а для более полного изучения понятия класса сегмента необходимо обратиться, например, к учебнику [5].9ловного модуля будет первым, в его конец будут добавляться соответствующие одноимённые сегменты из других объектных модулей.
В том случае, если одноимённые сегменты с параметром public встречаются не в головном модуле, то их порядок при склейке определяется конкретным редактором внешних связей (надо читать документацию). 1Для нашего примера сегмент данных с именем Data объектного модуля p2.obj будет добавлен в конец одноимённого сегмента данных головного модуля p1.obj. Такая же операция будетпроведена и для сегментов кода этих двух модулей. Таким образом, в загрузочном модуле останутсятолько три сегмента: сегмент стека Stk, сегмент данных Data и кодовый сегмент Code. При склейке кодовых сегментов редактору внешних связей придётся изменить некоторые адреса в командахперехода внутри добавляемого модуля.
Правда, как легко понять, меняются адреса только в командах абсолютного перехода и не меняются относительные переходы (это ещё одно достоинствокоманд перехода, которые реализуют относительный переход).Для склеиваемых сегментов данных могут измениться начальные значения переменных во втором сегменте, например, пусть в сегменте данных второго модуля находится такое предложение резервирования памятиZdwZЗдесь начальным значением переменной Z служит её собственный адрес (смещение от началасегмента данных). При склейке сегментов это значение увеличится на длину сегмента данных первого модуля (информация о необходимости какой коррекции начального значения, естественно, тожесодержится в паспорте второго объектного модуля).Отметим также, что параметр директивы сегмента stack, кроме того, что определяет сегментстека, даёт такое же указание о склейке одноимённых сегментов одного класса, как и параметр public.