The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа

Расширенный ассемблер: NASM

Следующая глава | Предыдущая глава | Содержание | Указатель

Глава 6: Выходные форматы

Перевод: AsmOS group, © 2001
NASM — портируемый ассемблер, разработанный для компиляции на любых платформах, поддерживающих ANSI C и создающий исполнимые файлы различных Intel x86-совместимых операционных систем. Для обеспечения этого он поддерживает большое число выходных форматов, выбираемых при помощи ключа -f командной строки. В данной главе описываются все эти форматы вместе с их специфичным синтаксисом.

Как упоминалось в параграфе 2.1.1, NASM выбирает имя по умолчанию для выходного файла на основе имени входного файла и указанного выходного формата. При этом расширение входного файла (.asm, .s или др.) удаляется и вместо него подставляется расширение соответствующего выходного формата. Расширения файлов приводятся ниже в описаниях каждого формата.

6.1 bin: Плоский бинарный формат

Формат bin не создает объектных файлов: в выходной файл не генерируется ничего, кроме написанного вами кода. Такие "чисто бинарные" файлы используются в MS-DOS: как исполнимые файлы .COM и драйвера устройств .SYS. Бинарный формат полезен также при разработке операционных систем и загрузчиков.

Формат bin поддерживает только три стандартные секции с именами .text, .data и .bss. В файле, сгенерированном NASM, сначала будет идти содержимое секции .text, а затем содержимое секции .data, выровненное по 4-байтной границе. Секция .bss не хранится в выходном файле, но предполагается, что она будет расположена сразу после секции .data, опять же выровненная по 4-байтной границе.

Если вы явно не указываете директиву SECTION, написанный вами код будет направлен по умолчанию в секцию .text.

Использование формата bin переводит NASM по умолчанию в 16-битный режим (см. параграф 5.1). Чтобы использовать его для написания 32-битного кода (например, ядра ОС), вам необходимо явно указать директиву BITS 32.

По умолчанию bin не создает расширение файла: он просто оставляет исходное имя файла, удалив предварительно его расширение. Таким образом, NASM по умолчанию будет ассемблировать binprog.asm в бинарный файл binprog.

6.1.1 ORG: Начало бинарного файла

Формат bin предусматривает дополнительную к списку главы 5 директиву ORG. Функция этой директивы — задавать начальный адрес программы, с которого она располагается при загрузке в память.

Например, следующий код будет генерировать двойное слово 0x00000104:

          org 0x100 
          dd label 
label:

В отличие от директивы ORG, применяемой MASM-совместимыми ассемблерами, которые позволяют перемещаться в объектном файле и переписывать уже сгенерированный код, NASM-овская ORG означает только то, что и соответствующее слово: origin (начало). Ее единственная функция — задавать смещение, которое будет прибавляться ко всем ссылкам на адреса внутри файла; она не допускает такого жульничества, как MASM. Дополнительные комментарии содержатся в параграфе 10.1.3.

6.1.2 bin-расширение директивы SECTION

Выходной формат bin расширяет директиву SECTION (или SEGMENT), позволяя вам указывать требования к выравниванию сегментов. Это делается путем добавления в конец строки определения секции спецификатора ALIGN. Например, строка

          section .data align=16

переключается на секцию .data и также указывает, что эта секция должна быть выровнена в памяти по границе параграфа.

Спецификатор ALIGN указывает, сколько младших бит в начальном адресе секции должны быть установлены в ноль. Указываемое значение выравнивания может быть любой степенью двойки.

6.2 obj: Объектные файлы OMF Microsoft

Файлы формата obj (исторически в NASM они называются obj, а не omf), создаваемые MASMом и TASMом, обычно "скармливаются" 16-битным DOS-компоновщикам, на выходе которых получаются .EXE файлы. Этот формат используется также в OS/2.

Формат obj предполагает расширение выходного файла по умолчанию .obj.

obj не является исключительно 16-битным форматом: NASM полностью поддерживает 32-битные расширения этого формата. В частности, 32-битный obj формат используется Win32-компиляторами Borland, которые не применяют новый объектный формат win32 от Майкрософт.

Формат obj не определяет специальных имен сегментов: вы можете называть ваши сегменты как угодно. Типичными именами сегментов obj формата обычно являются CODE, DATA и BSS.

Если в вашем исходном файле до явного указания директивы SEGMENT содержится код, NASM будет помещать его в собственный сегмент с именем __NASMDEFSEG.

Когда вы определяете сегменты в obj файле, NASM воспринимает их имена как символы, поэтому вы можете легко получить адрес любого определенного сегмента, например:

          segment data 
dvar:     dw 1234 
          segment code 
function: mov ax,data            ; получение сегментного адреса данных 
          mov ds,ax              ; и помещение его в DS 
          inc word [dvar]        ; теперь эта ссылка работает 
          ret

Формат obj разрешает также использование операторов SEG и WRT, поэтому вы можете писать код, делающий что-то вроде

          extern foo 
          mov ax,seg foo         ; получение сегмента foo 
          mov ds,ax 
          mov ax,data            ; получение другого сегмента 
          mov es,ax 
          mov ax,[ds:foo]        ; это доступ к 'foo' 
          mov [es:foo wrt data],bx  ; так это делается

6.2.1 obj-расширения директивы SEGMENT

Выходной формат obj расширяет директиву SEGMENT (или SECTION), позволяя задавать различные характеристики определяемого сегмента. Это делается путем добавления в конец строки определения сегмента дополнительных спецификаторов. Например,

          segment code private align=16

определяет сегмент code, объявляя его при этом закрытым и требуя, чтобы код этого модуля был выровнен по границе параграфа.

Имеются следующие спецификаторы:

Атрибуты сегмента по умолчанию в NASM — это PUBLIC, ALIGN=1, без класса, без оверлея и USE16.

6.2.2 GROUP: Определение групп сегментов

Формат obj позволяет группировать сегменты, благодаря чему для ссылки на все сегменты группы может использоваться один сегментный регистр. NASM для этого поддерживает директиву GROUP. Вы можете написать:

          segment data 
               ; некоторые данные 
          segment bss 
               ; некоторые неинициализированные данные 
          group dgroup data bss

При ассемблировании этого кода будет создана группа dgroup, содержащая сегменты data и bss. Как и в случае SEGMENT, имя директивы GROUP определяется как символ, поэтому вы можете ссылаться на переменную var в сегменте data как var wrt data или как var wrt dgroup, в зависимости от того, какое значение в данный момент находится в сегментном регистре.

Однако если вы ссылаетесь на var, объявленную в сегменте группы, NASM по умолчанию предоставляет смещение относительно начала группы, а не сегмента. Вследствие этого SEG var также будет возвращать базу группы, а не базу сегмента.

NASM позволяет сегменту являться частью более одной группы, однако при этом он будет выдавать предупреждение. Переменные, объявленные в таком сегменте, по умолчанию будут относиться к первой включающей данный сегмент группе.

Группа может не иметь ни одного сегмента; однако при этом вы можете делать WRT ссылки на группу, которая не содержит нужную переменную. OS/2, например, определяет специальную группу FLAT без всяких сегментов.

6.2.3 UPPERCASE: Отключение чувствительности к регистру

В отличие от NASM, некоторые OMF компоновщики нечувствительны к регистру символов; вследствие этого иногда полезно заставлять NASM генерировать объектные файлы в одном регистре. Директива формата UPPERCASE перед записью в объектный файл переводит имена всех сегментов, групп и символов в верхний регистр. В пределах исходного файла NASM остается регистро-чувствительным; однако объектные файлы, если это необходимо, могут быть составлены целиком в верхнем регистре.

UPPERCASE должна оставаться единственной в строке; она не требует никаких параметров.

6.2.4 IMPORT: Импортирование символов из DLL

Директива IMPORT определяет символ, импортируемый из DLL и используемый например для написания в NASM библиотеки импорта. При использовании директивы IMPORT вы должны объявить символ как EXTERN.

Директива IMPORT требует указания двух параметров, разделенных пробелом и являющихся соответственно именем импортируемого символа и именем библиотеки, из которой он импортируется. Например:

          import WSAStartup wsock32.dll

Третьим (необязательным) параметром является имя, под которым импортируемый символ известен в соответствующей библиотеке. Этот параметр применяется в том случае, если вы хотите, чтобы имя символа, используемое внутри вашего кода, не совпадало с именем того же символа в библиотеке. Например:

          import asyncsel wsock32.dll WSAAsyncSelect

6.2.5 EXPORT: Экспортирование символов в DLL

Директива EXPORT определяет глобальный символ, экспортируемый как DLL-символ в создаваемую DLL (если вы пишете DLL в NASM). Чтобы использовать директиву EXPORT, вы должны объявить символ как GLOBAL.

EXPORT требует один параметр, являющийся именем экспортируемого символа (в том виде, в каком он определен в вашем исходном файле). Необязательный второй параметр (отделенный пробелом от первого) представляет собой внешнее имя символа: это имя, под которым данный символ будет известен использующим эту DLL программам. Если это имя то же самое, что и внутреннее, можете оставить второй параметр пустым.

Для определения атрибутов экспортируемого символа могут быть заданы дополнительные параметры. Эти параметры, как и второй, отделяются друг от друга пробелами. Если дополнительные параметры задаются, внешнее имя также должно указываться, неважно, совпадает ли оно с внутренним или нет. Имеются следующие дополнительные атрибуты:

Например:

          export myfunc 
          export myfunc TheRealMoreFormalLookingFunctionName 
          export myfunc myfunc 1234  ; экспортирование по ординалу 
          export myfunc myfunc resident parm=23 nodata

6.2.6 ..start: Определение точки входа в программу

OMF-компоновщики требуют, чтобы один из компонуемых объектных файлов определял точку входа в программу (место, откуда начнется выполнение программы после загрузки). Если этот файл ассемблируется при помощи NASM, вы можете указать точку входа путем объявления в этом месте специального символа ..start.

6.2.7 obj-расширения директивы EXTERN

Если вы объявляете внешний символ директивой

          extern foo

то ссылки наподобие mov ax,foo будут давать вам смещение foo относительно базы его предопределенного сегмента (как задано в модуле, где определен foo). Таким образом, доступ к содержимому foo вы будете обычно осуществлять следующим образом

          mov ax,seg foo         ; получение базы сегмента 
          mov es,ax              ; перемещение ее в ES 
          mov ax,[es:foo]        ; и использование смещения 'foo' от нее

Это получается немного громоздко, особенно если вы знаете, что внешний символ будет доступен из данного сегмента или группы (например, dgroup). Так, если DS уже содержит dgroup, вы можете написать

          mov ax,[foo wrt dgroup]

Однако необходимость каждый раз это печатать для получения доступа к foo очень утомительна; NASM позволяет вам объявить foo в альтернативной форме

          extern foo:wrt dgroup

Эта форма заставляет NASM делать вид, что базовым сегментом foo на самом деле является dgroup; так, выражение seg foo теперь будет возвращать dgroup, а выражение foo будет эквивалентно foo wrt dgroup.

Описанный WRT-механизм может быть использован для "проявления" внешних символов по отношению к любой группе или сегменту в программе. Он также может быть применен к общим переменным (см. параграф 6.2.8 ниже).

6.2.8 obj-расширения директивы COMMON

Формат obj позволяет общим переменным быть как ближними, так и дальними; NASM обеспечивает этот механизм при помощи следующего синтаксиса:

          common nearvar 2:near  ; 'nearvar' - ближняя переменная 
          common farvar 10:far   ; 'farvar' - дальняя переменная

Дальние общие переменные могут иметь размер больше 64 Кб, поэтому спецификация OMF говорит, что они объявляются как число элементов данного размера. Так, 10-байтная дальняя переменная должна быть объявлена как 10 однобайтных элементов, 5 двухбайтных, 2 пятибайтных или 1 десятибайтный элемент.

Некоторые OMF-компоновщики при разрешении общих переменных, объявленных более чем в одном модуле, требуют совпадения размеров элемента и переменной. Поэтому NASM должен позволять задавать размер элемента дальних переменных. Он делает это при помощи следующего синтаксиса:

          common c_5by2 10:far 5 ; два пятибайтных элемента 
          common c_2by5 10:far 2 ; пять двухбайтных элементов

Если размер элемента не задан, он предполагается по умолчанию равным 1. Не требуется также и явное указание ключевого слова FAR, так как только дальние общие переменные могут иметь размер элемента. Исходя из этого, приведенные выше объявления будут эквивалентны следующим:

          common c_5by2 10:5     ; два пятибайтных элемента 
          common c_2by5 10:2     ; пять двухбайтных элементов

В дополнение ко всему вышесказанному, директива COMMON в obj поддерживает также и спецификацию WRT-умолчаний наподобие тому, как это работает для EXTERN (см. параграф 6.2.7). Поэтому вы можете объявлять такие, например, вещи:

          common foo 10:wrt dgroup 
          common bar 16:far 2:wrt data 
          common baz 24:wrt data:6

6.3 win32: Объектные файлы Win32 Microsoft

Выходной формат win32 генерирует объектные файлы Microsoft Win32, передаваемые обычно компоновщикам Microsoft. Заметьте, что компиляторы Borland Win32 не используют этот формат, вместо него они используют obj (см. параграф 6.2).

win32 подразумевает по умолчанию расширение выходного объектного файла .obj.

Имейте в виду, что хотя Майкрософт и утверждает, что объектные файлы Win32 следуют стандарту COFF, объектные файлы, созданные компиляторами Microsoft Win32, не совместимы с COFF-компоновщиками (например, DJGPP) и наоборот. Это происходит из-за разницы во взглядах на семантику таблицы перемещений. Для создания COFF-совместимых выходных файлов для DJGPP используйте выходной формат coff NASMа; обратное также справедливо — файлы, полученные в объектном формате coff, не обрабатываются корректно компоновщиками Win32.

6.3.1 win32-расширения директивы SECTION

Как и формат obj, win32 позволяет вам указывать в строке с директивой SECTION дополнительную информацию, предназначенную для управления типом и свойствами определяемой секции. Обычно тип секции и ее свойства для стандартных имен .text, .data и .bss генерируются NASM автоматически, но при помощи приведенных спецификаторов могут быть и переопределены.

Имеются следующие спецификаторы:

Если вы не укажете ни одного описанного выше спецификатора, NASM принимает следующие значения по умолчанию:

          section .text code align=16 
          section .data data align=4 
          section .bss bss align=4

Любые другие имена секций обрабатываются так же, как и .text.

6.4 coff: Общий формат объектных файлов

Выходной формат coff создает COFF-объектные файлы, обрабатываемые компоновщиком DJGPP. Этот формат предусматривает по умолчанию расширение выходных файлов .o.

Формат coff поддерживает те же самые расширения директивы SECTION, что и win32, однако спецификатор align и секция типа info не поддерживаются.

6.5 elf: Объектные файлы ELF Линукс

Выходной формат elf генерирует объектные файлы ELF32 (Executable and Linkable Format), используемые в Линукс. Этот формат использует по умолчанию расширение .o выходных файлов.

6.5.1 elf-расширения директивы SECTION

Как и формат obj, elf позволяет вам указывать в строке с директивой SECTION дополнительную информацию, предназначенную для управления типом и свойствами определяемой секции. Обычно тип секции и ее свойства для стандартных имен .text, .data и .bss генерируются NASM автоматически, но при помощи приведенных спецификаторов могут быть и переопределены.

Имеются следующие спецификаторы:

Если вы не укажете ни одного описанного выше спецификатора, NASM принимает следующие значения по умолчанию:

          section .text progbits alloc   exec nowrite align=16 
          section .data progbits alloc noexec   write align=4 
          section .bss    nobits alloc noexec   write align=4 
          section other progbits alloc noexec nowrite align=1

(Любые секции, не являющиеся .text, .data или .bss обрабатываются так же, как и секция other).

6.5.2 Позиционно-независимый код: Специальные символы формата elf и WRT

Спецификация ELF содержит достаточное число возможностей написания позиционно-независимого кода (PIC), благодаря которым разделяемые библиотеки ELF весьма гибкие. Однако это также означает, что NASM должен быть способен генерировать разнообразные замысловатые типы релокейшнов объектных ELF-файлов.

Так как ELF не поддерживает сегментные ссылки, оператор WRT для своей обычной цели не используется; вместо этого формат elf использует WRT для других целей, а именно для специальных PIC-типов перемещений.

elf определяет пять специальных символов, которые вы можете использовать с правой стороны оператора WRT для получения PIC-типов перемещений. Это ..gotpc, ..gotoff, ..got, ..plt и ..sym. Их функции описаны ниже:

Более полное объяснение использования типов перемещений для написания в NASM разделяемых библиотек дано в параграфе 8.2.

6.5.3 elf-расширения директивы GLOBAL

Объектные файлы ELF могут содержать больше информации о глобальном символе, чем просто его адрес: они могут содержать размер символа, а также его тип. Это сделано не только для удобства при отладке, это просто необходимо, если программа пишется в виде разделяемой библиотеки. Для задания дополнительной информации NASM поддерживает некоторые расширения директивы GLOBAL.

Поставив после имени глобальной переменной двоеточие и написав function или data (object является синонимом data), вы можете указать, чем является глобальная переменная — функцией или объектом данных. Например, строка

          global hashlookup:function, hashtable:data

экспортирует глобальный символ hashlookup как функцию, а hashtable — как объект данных.

После ввода спецификатора вы можете также указать в виде числового выражения (которое может включать метки и даже опережающие ссылки) размер ассоциированных с символом данных, например:

          global hashtable:data (hashtable.end - hashtable) 
hashtable: 
          db this,that,theother  ; здесь некоторые данные 
.end:

Это заставит NASM автоматически подсчитать длину таблицы и поместить эту информацию в символьную таблицу ELF.

Объявление типа и размера глобальных символов требуется при написании разделяемых библиотек. Дополнительную информацию вы можете найти в параграфе 8.2.4.

6.5.4 elf-расширение директивы COMMON

ELF позволяет задавать требования к выравниванию общих переменных. Это делается путем помещения числа (которое должно быть степенью двойки) после имени и размера общей переменной и отделения этого числа (как обычно) двоеточием. Например, массив двойных слов, который должен быть выровнен по двойным словам:

          common dwordarray 128:4

Эта строка объявляет массив размером 128 байт и требует, чтобы он был выровнен по 4-байтной границе.

6.6 aout: Объектные файлы a.out Линукс

Формат aout генерирует объектные файлы a.out в форме, используемой устаревшими Линукс системами. (Он отличается от других объектных файлов a.out магическим числом в первых четырех байтах файла. Также некоторые реализации a.out, например NetBSD, поддерживают позиционно-независимый код, который реализация Линукс не знает).

Формат a.out подразумевает расширение выходных файлов по умолчанию .o.

Этот формат очень простой. Он не поддерживает специальных директив и символов, не использует SEG или WRT и в нем нет расширений никаких стандартных директив. Он поддерживает только три стандартных секции с именами .text, .data и .bss.

6.7 aoutb: Объектные файлы a.out NetBSD/FreeBSD/OpenBSD

Формат aoutb генерирует объектные файлы a.out в форме, используемой различными BSD-клонами UNIX: NetBSD, FreeBSD и OpenBSD. Для большинства объектных файлов этот формат не отличается от aout за исключением магического числа в первых четырех байтах файла. Однако формат поддерживает (как и формат elf) позиционно-независимый код, поэтому вы можете использовать его для написания разделяемых библиотек BSD.

Расширение объектных файлов формата aoutb по умолчанию .o.

Формат не поддерживает специальных директив и символов и имеет только три стандартных секции с именами .text, .data и .bss. Несмотря на это, для обеспечения типов перемещений в позиционно-независимом коде он поддерживает использование WRT так же, как это делает elf. Более подробно это описано в параграфе 6.5.2.

aoutb, как и elf поддерживает также расширение директивы GLOBAL: см. параграф 6.5.3.

6.8 as86: Объектные файлы as86 Линукс

16-битный ассемблер Линукс as86 имеет свой собственный нестандартный формат объектных файлов. Хотя его компаньон компоновщик ld86 выдает что-то близкое к обычным бинарникам a.out, объектный формат, используемый для взаимодействия между as86 и ld86, все же не является a.out.

NASM на всякий случай поддерживает данный формат как as86. Расширение выходного файла по умолчанию для данного формата .o.

Формат as86 — это очень простой объектный формат (с точки зрения NASM). Он не поддерживает специальных директив и символов, не использует SEG и WRT, и в нем нет никаких расширений стандартных директив. Он поддерживает только три стандартных секции с именами .text, .data и .bss.

6.9 rdf: Перемещаемые динамические объектные файлы

Выходной формат rdf создает объектные файлы RDOFF. RDOFF — это "доморощенный" формат объектных файлов, разработанный вместе с NASM и отражающий в себе внутреннюю структуру ассемблера.

RDOFF не используется никакими широко известными операционными системами. Однако тот, кто пишет собственную систему, возможно захочет использовать его в качестве собственного объектного формата, так как разработан он прежде всего для упрощения и содержит очень мало бюрократии в заголовках файлов.

Архив Unix NASM и архив DOS с исходниками имеют подкаталог rdoff, содержащий набор RDOFF-утилит: RDF-компоновщик, менеджер статических библиотек, утилита, делающая дамп RDF-файла, и программа, загружающая и выполняющая RDF-исполнимый файл под Линукс.

Формат rdf поддерживает только стандартные секции с именами .text, .data и .bss.

6.9.1 Требование библиотеки: Директива LIBRARY

RDOFF содержит механизм "требования библиотеки", которая будет связана с модулем как во время загрузки, так и при выполнении. Это осуществляется директивой LIBRARY, которая принимает один аргумент, являющийся именем модуля:

          library mylib.rdl

6.10 dbg: Формат для отладки

Выходной dbg формат в конфигурации NASM по умолчанию отсутствует. Если вы строите собственный исполнимый файл NASM из исходников, то можете для включения этого формата определить символ OF_DBG в файле outform.h или командной строке компилятора.

Формат dbg не создает объектных файлов как таковых; вместо этого он создает текстовый файл, содержащий полный список всех транзакций между ядром NASM и модулем выходных форматов. Это обычно нужно людям, намеревающимся написать собственные выходные драйвера и которые благодаря этому формату могут получить картину различных запросов основной программы к выходному драйверу и увидеть, в каком порядке они осуществляются.

Для простых файлов можно использовать dbg формат так:

nasm -f dbg filename.asm

в результате чего генерируется диагностический файл filename.dbg. Однако это не будет работать на файлах, разработанных под различные объектные форматы, так как каждый формат определяет собственные макросы (обычно пользовательские формы директив), не определенные в формате dbg. Поэтому здесь необходимо запускать NASM дважды, с препроцессированием выбранного объектного формата:

nasm -e -f rdf -o rdfprog.i rdfprog.asm 
nasm -a -f dbg rdfprog.i

Здесь rdfprog.asm препроцессируется в rdfprog.i, оставляя при этом выбранным объектный формат rdf. Это нужно, чтобы специальные директивы RDF правильно конвертировались в примитивную форму. Затем препроцессированный исходный файл обрабатывается в формате dbg и при этом генерируется окончательный диагностический файл.

Такой обходной путь обычно не будет работать с программами, предназначенными для формата obj, так как директивы SEGMENT и GROUP последнего имеют побочные эффекты определения имен сегментов и групп как символов; dbg этого не делает, поэтому программа ассемблироваться не будет. Если вам позарез нужно получить dbg-трассировку исходников, написанных для obj, вы можете обойти это, определив символы самостоятельно (например, при помощи EXTERN).

Формат dbg принимает любые имена секций и любые директивы, протоколируя все их в свой выходной файл.

Следующая глава | Предыдущая глава | Содержание | Указатель


Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру