NASM网际编译器手册(七)

第六章 输出格式


NASM是一个方便的编译器,用来 设计在任何ASNI的C支持的平台中编译,并生成物一个在不同Intelx86操作系统上
输出.对于这个原因,它可以有很多输出格式,用NASM的命令行参数-f可以选择所要的格式.对每种格式,都有它自已
的基于NASM的扩展语法,本章将进行详细说明.
在第2.1.1节中,NAM将在输入文件名的基础上选择一个默认的输出文件名和输出文件格式。这种方法是将输入
文件名的扩展名(.asm,.s或其它你用的名字)移去然后加上输出格式定义的扩展名。这个扩展将在下面给出。


6.1 bin:Flat-Form二进制格式输出
bin格式不能生成目标文件:它只产生你写的代码。这种\'\'纯二进制\'\'文件用在MS-DOS的.COM 和.SYS二进制文件
中。纯二进制文件主要用于对开发操作系统和引导程序的开发。bin支持三种标准的段名字:.text,.data和.bss。
NASM首先输出.text段的内容,然后是四字节对齐的.data段内容。如果你用SECTION定向符指定,你写的代码将
默认的放到.text段中。16位模式的bin格式(见第5.1节)为了在系统中用一个32位代码,你需要指定一个BIT32定向
符。bin没有默认的扩展格式:它只是去除源文件的扩展名,如NASM对binprog.asm汇编时将会生成二进制文件
binprog。


6.1.1 ORG:二进制格式的起点
bin格式提供了一个附加的定向符,它在第5章列出:ORG。ORG定向符的功能是在NASM将程序装入内存中时,
指定的一个起始位置。例如,下面的代码将生成长字0xc00000104:
org 0x100
dd label
label:
不象与MASM相关编译器提供的ORG定向符一样允许你在目标文件中跳转并重写你生成过的代码,NASM的ORG
定向符的只是确定初始值。它指定一个偏移值用来加在文件中所有的内部地址上;它不允许MASM版本所做的
任何欺骗行为。更多的信息见第10.1.3节。


6.1.2 bin扩展到SECTION定向符
bin输出格式允许你将SECTION(或SEGMENT)定向符来为一个需要对齐的段进行处理。这可以在段定义行后加
ALIGN来实现:
section .data align=16
这将会切换到section .data并指定它必须为16字节对齐的。ALIGN指定了段开始地址的最低多少位必须为0。
这个值必须为二个指数幂。


6.2 obj:微软的OMF目标文件格式
obj文件格式(NASM不叫的omf的原因是因为一些历史原因)是由MASM和TASM的一种输出格式,它主要用在16位
的DOS连接器生成.EXE文件。它也是OS/2的一种格式。obj提供一个默认的输出文件扩展名:.obj。
虽然obj不全是一个16位的格式,NASM还是支持32位扩展模式。通常32位obj格式在Borland的Win32编译器中使
用,来代替微软的新的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
定义一个段代码并定义它为一个原始格式的段并且必须为16字节对齐的。
下面为可用的定向符:
PRIVATE,PUBLIC,COMMON和STACK将指令段的综合特性。PRIVATE段将不能用连接器和其它段连接;
PUBLIC和STACK段将在连接时间进行连接;COMMON段将每一个段相互覆盖的连接而不是end-to-end连接。
ALIGN的用法象上面一样,指定段的开始位置最少多少位为0。给出的段值为2的指数幂可以从1-4096;实际上,
为1时只支持1,2,4,16,256到4096,如果为8时则可以是16,32,64和128到256等等。4096字节的偏移将是一个
PharLap扩展并且不被所有连接器支持。
CLASS将用来指定段的类别;这个特性将指示连接器将相同类型的段在输出文件中放一起。这个类型的名字可
以是任何词:如CLASS=CODE。
OVERLAY象CLASS一样带任何一个参数,并对overlay相兼容的连接器提供overlay信息。段可以用USE16和USE32
定义,它对在目标格式中对选择进行记录,并且当段分别是16位和32位时确保NASM能用默认的汇编。
当写OS/2目标文件时,你可以用FLAT来定义一个32位的段,用来为一个指定的FLAT的组定义一个段基址,如果组滑有定义时也定义组。obj文件格式允许段来定义一个预定义绝对段地址,虽然没有连接器用这个特性;但
NASM允许你用SEGMENT SCREEN ABSOLUTE=0XB800来定义一个段。ABSOLUTE和ALIGN关键字将是相互排斥的。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 wrt data或var wrt dgroup来在一个数据段中引用一个变量,这取决于呶一个段值为在你当前的段
寄存器。如果你引用一个var,而var是在一个段组中的一个段中被定义的,那么NASM将默认从段组的开始定义
var的偏移,而不是段的。SEG变量,将返回组基址而不是段基址。
NASM允许一个段成为多个组的一部分,但你这样做将会生成一个警告信息。在多个段组中的段里定义的变量
将默认与包含这个段的第一个段组相关联。一个组不一定包含任何段:你可以用WRT引用一个在组中不包含你
引用的变量。对于OS/2,则定义了一个特殊组FLAT,没有段在它里面。


6.2.3 UPPERCASE:在输出中禁止大小写
虽然NASM本身是大小敏感的,有一些OMF连接器不是;然而它对于NASM输出单向敏感是有用的。UPPERCAT的
格式定义符将对在目标格式文件中的所有段,组和符号名在它们写入前来变成大写。在一个源码文件中,NASM
仍然是大小写区别的;如果愿意要可以将整个文件变成大写。UPPERCAT单独用一行表示,并不带参数。


6.2.4 IMPORT:引入DLL符号
如果你用NASM写一个DLL的引入库,IMPORT格式定义符定义一个从DLL引用的符号。你可以象EXTERN一样,使
用IMPORT定向符。IMPORT定向符带两个参数,用空格来分隔,它们分别为你想引用的符号名和你相引入的库
名字:
import WSAStartup wsock32.dll
第三个可选的参数将给出一个在引入库中的名字,一旦你在代码中引用它时,则和这个符号相同。如:
import asyncsel wsock32.dll WSAAsyncSelect


6.2.5 EXPORT:引出DLL符号
如果你用NASM写了一个DLL,EXPORT格式描述符定义一个可以外部引用DLL的全局符号。你可以象定义GLOBAL
一样定义EXPORT定向符。EXPORT带一个参数用来给你引出的名字命名,它将在你的源文件中定义。第二个可
选参数(用空格与第一个参数分隔)给出了这个符号的外部名:你在程序中用DLL时想要程序知道的符号名。
如果这个名字和内部名相同,你可以不用第二个参数。
更多的参数将用来定义外部符的属性。这些参数象第二个参数,用空格分隔,如果更多的参数给出,外部名也
必须指定,它甚至可以和内部名一样。下面为可用属性:
resident表示外部符号是可以在系统启动时保存的。这对通过名字引入的符号通常有用。
nodata表示引出符号是一个不用初始化数据的函数。
parm=NNN,这里NNN是一个整数,设置参数的个数,这些参数是在32位和16位段间门调用的符号。
数值的属性表示一个符应该用一个数字(序号)导出,并给出一个预计的数字。例如:
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的偏移。拟你可以象下面这样访问fo
mov ax,seg foo;给出段基址的引用
mov es,ax ;将它移至ES中
mov ax,[es:foo] ;给出\'\'foo\'\'的偏移
这是一个不很广泛的用法:如果你想对一个已知的段或组中的外部符号进行访问可以用dgroup。如果DS已经包
含了dgroup,你应该写成:mov ax,[foo wrt dgroup]
然而,这时你想访问fo将很难,NASM允许你在一个变格式的中定义foextern fowrt dgroup
这种格式使NASM假装在引用foo的段基址是在一个实际的dgroup中;所以seg foo将返回dgroup并写表达式
foo将与foo wrtr dgroup等价。默认的WRT机制鹏程万里你程序中任何相关的组或段中出员的外部符号。它也可以
用在common变量中:见第6.2.8节


6.2.8 obj对COMMON定向符的扩展
obj格式允许common变量为一个near或far;NASM允许你指定一个变量的语法用法:
common nearvar 2:near ;\'\'nearvar\'\'是一个near common
common farar 10:far ;\'\'farvar\'\'是一个far
Far common变量可以大于64Kb的尺寸,OMF规定它们用一个给定的元素个数的尺寸定义。所以一个10字节的far common变量
将用10个一字节的元素定义,5个二字节,和2个五字节可一个10字节。一些OMF连接器需要这个尺寸,和变量
尺寸一样,为了在多个模块中解决common变量的定义和匹配。然而NASM认你必须指定你far common变量中
元素的指尺寸 。如下的语法:
common c_5by2 10:faq 5;二个五字节
common c_2by5 10:far 2;五个二字节
如果元素尺寸未指定则默认为1。当元素的尺寸指定时FAR关键字可是以不用,这是因为只有far common变量才
需要元素尺寸,如下定义:
common c_5by2 10:5;二个五字节
common c_2by5 10:2;五个二字节
附加的扩展为,obj中的COMMON定向符象EXTERN(见第6.2.7节)一样也支持默认的WRT操作。所以你可以写:
common foo 10:wrt dgroup
common bar 16:far 2:wrt data
common baz 24:wrt data:6


6.3 win32:微软的Win32目标文件格式
win332输出格式生成微软的Win32目标文件,主要为用象Visual C++等微软的连接器。而Borland的Win32编译器不
用这种格式,而是用obj格式代替(见第6.2节).
win32指提供的默认输出扩展名为.obj.注意虽然微软说Win32目标文件是COFF(通用目标文件格式)标准的,但微软
生成的目标文件夹与象DJGPP COFF连接器生成的不兼容性,相反也一样。这是由于PC相关重定位的语法上的
区别。为了产生DJGPP的COFF文件可以使用NASM的coff输出格式;相反,coff格式也不能生成Win32连接器产生的
目标文件格式。


6.3.1 win32对SECTION定向符的扩展
象obj格式一样,win32允许你在SECTION定向符行上指定附加的信息,如你定义段的类型与属性。段的类型和
属性在NASM自动生成时为.text,.data和.bss,但这可以通过下面的限定符修改:
code或等价text,定义了一个代码段。 这使段可以是只读和可执行的,但不可写,也表示连接器用一
个代码段来处理它。
data和bss定义了一个数据段,类似代码段。数据段是可读写的,但不能执行。data定义了一个初始化
了的数据段,而bss定义了一个非初始化的数据段。
info定义了一个信息段,不能通过连接器包含,但可以将信息传给连接器。如定义一个叫.drectve的信息
段使连接器将段的内容做为一个命令行操作:
align=,象obj一样处理段数据的对齐。你可以指定的最大值为64:Win32目标文件格式不能包含大于这个
段偏移值。如果参数不指定则默认为code段为16个字节,data段(BSS)为4字节,信息段为1字节(不对齐):
section .text code align=16
section .data data align=4
section .bss bss align=4
其它段的名字象.text一个默认对待。


6.4 coff:通用目标文件格式
coff输出用DJGPP连接器生成的COFF目标文件。coff提供了一个默认的扩展名:.o.
cof格式支持SECTION定向符的扩展同win32一样,除了不支持align限定符和info段类型。


6.5 elf:Linux ELF目标文件
elf输出格式生成ELF32(执行和连接格式)目标文件,在Linux下使用。elf提供了一个默认的扩展名:.o.


6.5.1 elf对SECTION的扩展
象obj格式一样,elf允许你在SECTION定向符后指定附加信息来对你定义的段的类型与属性。段类型与属性在
NASM自动生成时为.text,.data和.bss并能用下面的限定符修改:
alloc定义一个段在程序运行时就装入内存。noalloc定义则相反常用于一个信息段或注释段。
exec定义当程序运行时是可执行的。noexec定义不可以。
write定义 程序运行时是可以写的。onwrite定义不可写。
progbits定义一个段将显式的在目标文件中存储内容:一个原始代码可数据段,如nobits定义一个段
将不用显式的内容如BSS段。
align=象obj一样对齐数据。
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的段都将象上面代码一样对待)


6.5.2 位置独立代码:elf指定符和WRT
ELF的指定格式包含足够的特性使位置独立代码生成(PIC),这使ELF可以弹性的共享其它 的库。这也就是说NASM
必须能在ELF目标文件中生成大量奇怪的重定位信息,如果这个源代码是用PIC写的。由于ELF不支持段基址的
引用,所以WRT操作符不能用它的正常用法;然而NASM的elf输出格式可以用不同格式的WRT用法,用PIC分隔
的重定位类型。elf定义了5种指定的符号包含PIC的重定位类型,你可以将它们放在WRT操作符的右边。它们是
..gotpc,..gotoff,..got,..plt和..sym。它们的功能 如下:
引用符号时用wrt标记全局偏移表的基址时,..gotpc将结束从当前段位置到全局偏移表的距离。
(_GOBAL_OFFSET_TABLE 是一个标准符号名用来引用GOT)。所以你需要将$$加到结果上来得到真实的GOT地址。
在你自己的段中引用一个位置时用wrt ..gotoff将会全出从GOT开始的位置到指定位置的距离,所以将地址 加到
GOT上将会给出一个你想要的真实地址 。
用wrt ..got对一个外部或全局符号的引用将会使连接器在构造GOT时包含符号的地址和给出GOT开始到入口的
距离 。所以你可以在GOT上加一个地址,从结果地址中取出再接到符号的地址上。
用wrt ..plt引用一个过程名将使连接器为符号构造一个可连接的过程表入口,并给出PLT入口的地址。你可以只
在上下相关环境中生成物一个PC相关的重定位信息(如一个CALL或JMP的目标),ELF对PLT的入口绝对引用 包含
不能重定位的类型。
用wrt ..sym对一个符号名引用时会使NASM写一个原始的重定位,而不是一个段开始的相关重定位并加上符号
偏移,它将写一个重定位记录来直接处理符号方面的问题。这个做法对于一个常见的动态连接是有必要的。
如何使用重定位类型的一个完全解释是在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变量上指定对齐方式。这通过在变量 的名字和大不上气不接下气放一个数字(2的指数幂)
实现,中间用冒号来实现,如下面将使一个双字的数组4字节对齐。
common dwordarray 128:4
数组的整个尺寸 为128个字节,而它是4字节对齐的。
6.6 aout:Linux a.out目标格式
aout格式生成a.out目标文件,用在早期的Linux系统上.(与其它a.out目标文件在magic数字前四个字节上有区别,
有些a.out执行文件,如NetBSD的,支持位置独立代码,而Linux的则不能执行)a.out提供的默认扩展名为.o.
a.out是一种非常简单的目标格式,它不支持指定的定向符,指定的符号和SEG或WRT的用法及对任何标准定向
符的扩展。它只支持三种标准的段名字:.text,.data和.bss。


6.7 aoutb:NetBD/FreeBSD/OpenBSD a.out目标文件
aoutb格式生成a.out目标文件,这种格式用在各种自由的BSD Unix版本:NetBSD,FreeBSD和OpenBSD。对简单的
目标文件,这种目标格式和aout一样除了文件前面的4个字节magic数字不同。然而,aoutb格式象elf格式一样
支持位置独立的代码,所以你可以用它来写BSD的共享库。aoutb提供了一个默认的扩展名为.o.aoutb不支持指定
的定向符,符号,只支持三个标准的段名:.text,.data和.bss。然而它也和elf一样支持WRT的用法,提供位置独立
的代码重定位类型。更多这种属性的文件见第6.5.2节
aoutb象elf一样支持GLOBAL定向符:见第6.5.3节这方面的文档。


6.8 as86:Linux的as86目标文件
Linux的16位编译器as86有它自己的非标准的目标文件格式。虽然它的相关连接器ld86生成相似的a.out二进制输
出,但在as86和ld86间目标文件的用法的交流不是a.out本身。NASM支持这种格式,是因为它在这方面有用。
as86提供默认的扩展名为.o。as86是一种非常简单的目标格式(从NASM用户观点)。它除了支持三种标准的段名:
.text,.data和.bss外不支持其它的定向符,符号和SEG或WRT的用法。


6.9 rdf:重定位动态目标文件格式
rdf输出格式生成RDOFF目标文件。RDOFF(重定位动态目标文件格式)是一种home-grown目标格式,为了NASM自身
设计并影响编译器内部结构的文件格式。RDOFF不用在出名的操作系统上。哪些写它们自己操作系统的人用这
种RDOFF做为它们的目标格式化,它主要设计用来单独包含那些少量的官方文件头。Unix NASM档案和DOS档案
包含源代码和一个rdoff子目录用来设置RDOFF:一个RDF连接器,一个RDF静态库,一个RDF文件查看器及在
Linux下的装入和执行RDF的程序。rdf只支持标准的段名:.text,.data和.bss。


6.9.1 所需库:LIBRARY定向符
RDOFF包含一个机制为目标文件决定将一个给出库连接到模块上,在装入时或运行时。通常LIBRARY定向符带
一个参数做为模块的名字:library mylib.rdl


6.10 dbg:调试格式
dbg输出格式不在NASM的默认匹配中构造。如果你要从源码文件中构造自己的NASM执行文件,你可以在
outform.h或命令行定义OF_DBG来包含dbg输出格式。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到rdfprogi里,保存选中的rdf目标格式为了确保RDF指定定向符可以正确的转成原始模
式。这种预处理的源码将用dbg格式生成最终的错误输出。
这种情况对于obj格式不适用,因为obj的SEGMENT和GROUP定向符号地于用符号定义段和组是有影响的;dbg则
不会这样做,所以程序将不编译。如果你想从目标源文件中得到一条dbg信息,则你必须定义你自己的符号
(如用EXTERN)。dbg可以接受任何段的名字,定向符,并将它们记录到它的输文件中?

原文地址:https://www.cnblogs.com/cnlmjer/p/4099880.html