Действительная структура программы в машинных кодах включает так называемые сегменты памяти. Эта структура невидима из языков высокого уровня, но является неизбежной и даже благодарной реальностью машинных структур данных. Основное назначение таких сегментов в современных архитектурах – обеспечить защитой от ошибочного доступа машинные коды команд и области данных. (В область команд запрещается что-либо записывать в процессе выполнения программы, а из области данных управляющему устройству процессора запрещается читать коды, автоматически интерпретируя их как коды машинных команд.) Получается, что машинная программа должна иметь как минимум два сегмента: сегмент машинных кодов (кодов команд) и сегмент данных. Заметим, что только в однозадачной ОС MS-DOS, где не было или не использовались указанные выше средства защиты сегментов друг от друга, можно было все машинные коды программы – и данные, и коды команд – размещать в одном единственном сегменте, причем все сегменты в этой ОС могли иметь максимальный размер всего в 64 Кбайт.
2 Что такое сегменты
Для описания сегментов служит специальная директива, обозначаемая словом SEGMENT, причем в этом случае может использоваться запись служебного слова как строчными, так и прописными буквами. (Синонимом этого служебного слова является слово SECTION, которое всегда можно использовать для обозначения сегмента.) В простейшем случае директива сегмента имеет единственный аргумент, который задает имя описываемого сегмента. С учетом ориентации ряда ОС на язык Си, как язык соглашений взаимодействия с системой, рекомендуемые названия сегментов кодов команд и данных есть соответственно.text и .data, причем начальная точка обозначения формальным образом входит в состав имени. Начинающему программисту следует обратить внимание на то, что между ключевым словом SEGMENT или SECTION и названием типа сегмента должен находиться по крайней мере один пробельный символ, иначе синтаксический анализ текста программы формирует ошибку.
Другой неожиданной для начинающих особенностью ассемблерных программ является необходимость явного обозначения машинной инструкции, с которой должна начать выполняться программа. Это отвечает естественно заложенной в архитектуру компьютеров возможности начать выполнение любой программы с любого места в области памяти, занятой кодами машинных команд.
Любая отметка места в памяти, относящаяся как к машинным командам, так и к данным, представляет собой так называемую метку. Метка на языке ассемблера есть просто имя, соотносимое с началом строки ассемблерной программы и размещаемое в начале этой строки. Традиционная методика использования меток требует, чтобы обозначение метки в том месте, где она именует строку программы, завершалось служебным символом двоеточия. В ассемблере NASM это требование является необязательным, но мы будем им пользоваться для отметок в тексте собственно машинных команд. В ассемблерах типа Intel требуется метки, обозначающие места в тексте машинных команд, обязательно завершать двоеточием, а метки, обозначающие данные, как правила, используются без завершающих двоеточий.
Сегменты в Яндекс Метрике: зачем нужны и как их создать
Место, с которого начинает выполняться программа, называется точкой входа. Метка точки входа в программу на языке NASM должна обязательно быть объявлена видимой извне объектного модуля, который возникает из исходного текста при традиционной методике создания программы. Такое объявление осуществляет директива GLOBAL. Назначение и возможности этой директивы гораздо шире, чем указание с ее помощью точки входа, но более подробное обсуждение данного вопроса будет проведено позднее. Использование директивы GLOBAL заключается в записи после ключевого слова этой директивы того имени, которое предполагается задать как глобальное. (В общем случае в одной директиве через запятые может быть указано много имен, которые предполагается использовать как глобальные.)
Программа-компоновщик в Linux требует имени _start в качестве обозначения начала программы (так как точка входа всей программы всегда одна, то такой выбор не несет никакого мыслимого ограничения). Указанное имя начинается со служебного символа подчеркивания, которое считается также входящим в состав этого имени. В общем случае имена в NASM могут состоять из алфавитно-цифровых символов, начинающихся с буквы, причем символ подчеркивания считается буквой. Кроме того, служебные имена могут начинаться с одной или двух точек. Использование служебных имен будет описываться особо по ходу изложения.
В ассемблерах типа Intel было принято несколько громоздкое в применении решение обозначать специальными служебными словами не только начало, но и конец сегмента программы. Для обозначения конца сегмента программы здесь предназначена директива со служебным именем ENDS, причем в этой директиве обязательно должно быть записано и имя того сегмента, который она завершает.
Такое решение призвано сократить число ошибок при написании программы, но, в связи с крайне скромным числом сегментов в современных программах, теперь такое решение кажется скорее красивым, чем целесообразным. В указанных ассемблерах типа Intel задание имени как в директиве SEGMENT, так и в директиве ENDS производится записью имени перед служебным словом директивы. Кроме рассмотренной особенности описания сегментов, ассемблеры типа Intel используют для указания завершения специальную директиву END, по назначению и форме очень напоминающую одноименную директиву в языке Pascal. В указанных же ассемблерах директива END используется также для указания метки точки входа.
Таким образом, сегментная структура программы для NASM имеет вид
. . . ; задание машинных команд на данном ассемблере
. . . ; описание данных на ассемблере NASM
причем сегменты .text и .data могут быть переставлены местами. Аналогичная же программа на ассемблере MASM или TASM будет иметь общий вид
имя_сегмента_кода SEGMENT
имя_точки_входа:
. . . ; задание машинных команд на данном ассемблере
имя_сегмента_кода ENDS
имя_сегмента_данных SEGMENT
. . . ; описание данных на ассемблере MASM или TASM
имя_сегмента_данных ENDS
END имя_точки_входа
где также сегменты данных и команд могут быть переставлены местами. Забегая вперед, отметим, что в современных ОС Windows имена сегментов кода и данных также должны задаваться по некоторым фиксированным соглашениям, так что практически то же могут считаться постоянными.
В современных ОС исходные описания сегментов используют не один сегмент данных, а по крайней мере две его модификации. Сегмент с традиционным наименованием .data или DATA применяется для инициализируемых данных. Характерной особенностью этих данных является задание начального значения для каждой единицы исходного описания данных.
Для размещения данных, исходное описание которых не содержит задания начального значения, принято наименование .bss или BSS. Следует учитывать, что транслятор NASM по-разному «относится» к содержимому указанных модификаций сегментов данных. Если программист делает попытку помещения неинициализированных им данных в сегмент с именем .data или DATA, то выдается предупреждение, а область самих таких данных заполняется нулевыми байтами. В свою очередь, попытка объявить данные (т.е. описать области размещения данных без задания их значений) в сегментах .bss или BSS приводит к сообщаемой ошибке.
Название этого специализированного сегмента программирования раскрывается как «Block Started by Symbol», которое звучит не очень информативно, поэтому профессиональные пользователи рекомендуют трактовку Better Saver Space. Этот сегмент предназначен для неинициализированных данных программы, поэтому по существу проблемы не требует хранения в кодах исполняемого файла. Практически неинициализированные данные могут быть указаны как помещаемые в сегменте с именем .datа, но тогда байты, соответствующие им, будут храниться в соответствующем месте исполняемого файла, увеличивая его объем. Практически использование секции .bss приводит только к сокращению объема исполняемого файла, что для современных программистов не является предметом внимания.
Источник: studfile.net
Сегментация программы
Следующий важный принцип организации памяти – сегментация. Сегментом называется непрерывная область памяти, хранящая данные одного вида (назначения) и имеющая собственную систему относительной адресации и ограничения доступа. К программированию это имеет отношение потому, что при трансляции разные компоненты программы попадают в различные сегменты программного кода.
Одновременное нахождение в памяти «алгоритма» и данных соответствует принципу хранимой программы, которыйзаключается в том, что программный код хранится в той же самой памяти, что и обрабатываемые данные, и в свою очередь сам представляет собой специфические данные, с которыми работает процессор во время выполнения программы. В настоящее время применяется немодифицируемый программный код.Это, в свою очередь, означает, что несколько программ могут его читать и исполнять, не мешая друг другу, в том числе и на физически параллельных процессорах.
Компоненты программы находятся в памяти, которая, в принципе, является общей для них, но логически разделяется на области, именуемые сегментами: прежде всего, это сегмент данных, содержащий данные программы; сегмент кода (команд), в котором находится алгоритмическая компонента (выражения, операторы); сегмент стека, в котором находятся локальные данные функций, «история» работы программы.
Процессор имеет в своем составе набор машинных слов – регистров. В зависимости от назначения они могут хранить как данные, так и адреса памяти. Сегментация поддерживается в процессоре при помощи регистров двух видов. Базовый регистр сегмента содержит его начальный адрес.
Регистры, работающие с данными сегмента, содержат относительный адрес данных от начала сегмента, или смещение. Результирующий адрес получается путем сложения содержимого этих регистров. Таким образом, каждый сегмент имеет собственную «систему координат», связанную с его началом (Рис.1.7). Если программа использует только сегментную адресацию, то сегменты можно перемещать по памяти при сохранении работоспособности программы: достаточно перенастроить соответствующие им базовые регистры.
Выполняемая программа состоит из нескольких сегментов. Некоторые из них создаются при трансляции, другие – при загрузке и при работе программы. В принципе, программа может иметь несколько сегментов одного вида:
Сегмент | Регистры | Что содержит | Когда создается |
Сегмент команд | CS- сегментный, IP- адрес команды | Программный код (операции, операторы) | трансляция |
Сегмент данных | DS – сегментный | Глобальные (статические) данные | трансляция |
Сегмент стека | SS – сегментный, SP– указатель стека | Локальные данные функций, «история» работы программы | при загрузке |
Динамическая память | DS – сегментный | Динамические переменные, создаваемые при работе программы | при загрузке, выполнении |
Динамически связываемые библиотеки (DLL) | CS– сегментный, IP– адрес команды | Программный код разделяемых библиотек | при загрузке |
Рис. 1.7. Сегментация программы
Следующий элемент архитектуры тоже имеет отношение к внутреннему представлению программы – виртуальное адресное пространство (виртуальная память). Его идея состоит в том, что каждая программа имеет свое, независимое от других, виртуальное адресное пространство (виртуальную память), в котором размещаются сегменты программы, определяются адреса и т.п. В процессоре имеется скрытая от программ система отображения виртуальных адресов на физические адреса. Виртуальное адресное пространство находится под управлением операционной системы и реализует защиту памяти программ, ее разделение (сегменты динамически связываемых библиотек), загрузку программы в память «по частям» и иллюзию наличия неограниченной памяти. Для нас из всего этого важно то, что внутренне представление программы в виртуальной памяти занимает все адресное пространство, как будто других программ и операционной системы в памяти нет вовсе.
Воспользуйтесь поиском по сайту:
Источник: studopedia.org
Сегменты. Организация программы
Каждая программа, написанная на любом языке программирования, состоит из одного или нескольких сегментов. Обычно область памяти, в которой находятся команды, называют сегментом кода, область памяти с данными — сегментом данных и область памяти, отведенную под стек, — сегментом стека. Разумеется, ассемблер позволяет изменять устройство программы как угодно — помещать данные в сегмент кода, разносить код на множество сегментов, помещать стек в один сегмент с данными или вообще использовать один сегмент для всего.
Сегмент программы описывается директивами SEGMENT и ENDS.
имя_сегмента segment readonly выравн. тип разряд ‘класс’. имя_сегмента ends
Имя сегмента — метка, которая будет использоваться для получения сегментного адреса, а также для комбинирования сегментов в группы.
Все пять операндов директивы SEGMENT необязательны.
READONLY. Если этот операнд присутствует, MASM выдаст сообщение об ошибке на все команды, выполняющие запись в данный сегмент. Другие ассемблеры этот операнд игнорируют.
Выравнивание. Указывает ассемблеру и компоновщику, с какого адреса может начинаться сегмент. Значения этого операнда:
BYTE — с любого адреса;
WORD — с четного адреса;
DWORD — с адреса, кратного 4;
PARA — с адреса, кратного 16 (граница параграфа);
PAGE — с адреса, кратного 256.
По умолчанию используется выравнивание по границе параграфа.
Тип. Выбирает один из возможных типов комбинирования сегментов:
тип PUBLIC (иногда используется синоним MEMORY) означает, что все такие сегменты с одинаковым именем, но разными классами будут объединены в один;
тип STACK — то же самое, что и PUBLIC, но должен использоваться для сегментов стека, потому что при загрузке программы сегмент, полученный объединением всех сегментов типа STACK, будет использоваться как стек;
сегменты типа COMMON с одинаковым именем также объединяются в один, но не последовательно, а по одному и тому же адресу, следовательно, длина суммарного сегмента будет равна не сумме длин объединяемых сегментов, как в случае PUBLIC и STACK, а длине максимального. Таким способом иногда можно формировать оверлейные программы;
тип AT — выражение указывает, что сегмент должен располагаться по фиксированному абсолютному адресу в памяти. Результат выражения, использующегося в качестве операнда для AT, равен этому адресу, деленному на 16. Например: segment at 40h — сегмент, начинающийся по абсолютному адресу 0400h. Такие сегменты обычно содержат только метки, указывающие на области памяти, которые могут потребоваться программе;
PRIVATE (значение по умолчанию) — сегмент такого типа не объединяется с другими сегментами.
Разрядность. Этот операнд может принимать значения USE16 и USE32. Размер сегмента, описанного как USE16, не может превышать 64 Кб, и все команды и адреса в этом сегменте считаются 16-битными.
В этих сегментах все равно можно применять команды, использующие 32-битные регистры или ссылающиеся на данные в 32-битных сегментах, но они будут использовать префикс изменения разрядности операнда или адреса и окажутся длиннее и медленнее. Сегменты USE32 могут занимать до 4 Гб, и все команды и адреса в них по умолчанию 32-битные. Если разрядность сегмента не указана, по умолчанию используется USE16 при условии, что перед директивой.MODEL не применялась директива задания допустимого набора команд.386 или старше.
Класс сегмента — это любая метка, взятая в одинарные кавычки. Все сегменты с одинаковым классом, даже сегменты типа PRIVATE, будут расположены в исполняемом файле непосредственно друг за другом.
Для обращения к любому сегменту следует сначала загрузить его сегментный адрес (или селектор в защищенном режиме) в какой-нибудь сегментный регистр. Если в программе определено много сегментов, удобно объединить несколько сегментов в группу, адресуемую с помощью одного сегментного регистра:
Операнды этой директивы — список имен сегментов (или выражений, использующих оператор SEG), которые объединяются в группу. Имя группы теперь можно применять вместо имен сегментов для получения сегментного адреса и для директивы ASSUME.
Директива ASSUME указывает ассемблеру, с каким сегментом или группой сегментов связан тот или иной сегментный регистр. В качестве операнда «связь» могут использоваться имена сегментов, имена групп, выражения с оператором SEG или слово «NOTHING», означающее отмену действия предыдущей ASSUME для данного регистра. Эта директива не изменяет значений сегментных регистров, а только позволяет ассемблеру проверять допустимость ссылок и самостоятельно вставлять при необходимости префиксы переопределения сегментов, если они необходимы.
Перечисленные директивы удобны для создания больших программ на ассемблере, состоящих из разнообразных модулей и содержащих множество сегментов. В повседневном программировании обычно используется ограниченный набор простых вариантов организации программы, известных как модели памяти.
Источник: studopedia.su