nasm的使用

nasm -f bin myfile.asm -o myfile.com

nasm -f coff myfile.asm -l myfile.lst

如果你没有对 NASM 使用'-f'选项,它会自己为你选择一个输出文件格式。在发布的NASM 版本中,缺省的输出格式总是'bin';

如果你对 NASM 使用了'-l'选项,后面跟一个文件名,NASM 会为你产生一个源文件的列表
文件,在里面,地址和产生的代码列在左边,实际的源代码(包括宏扩展,除了那些指定
不需要在列表中扩展的宏,参阅 4.3.9)列在右边,比如:
nasm -f elf myfile.asm -l myfile.lst

`-M'选项: 产生 Makefile 依赖关系.
NASM -M myfile.asm > myfile.dep

NASM 需要方括号来引用内存地址。 规则是任何对内存中内容的存取操作必须要在地址上加上方括号。

`TIMES': 重复指令或数据。

注意在'times 100 r esb 1'跟'resb 100'之间并没有显著的区别,除了后者在汇编时会快上一百倍。

临界表达式。 !!!???


本地 Labels :一个以单个句点开始的Label 会被处理成本地 label, 这意味着它会跟前面一个非本地 label 相关联.

`ALIGN' and `ALIGNB': 数据对齐


`BITS': 指定目标处理器模式。
最有可能使用'BITS'的场合是在一个纯二进制文件中使用 32 位代码;这是因
为'bin'输出格式在作为 DOS 的'.COM'程序,DOS 的'.SYS'设备驱动程序,或引
导程序时,默认都是 16 位模式。

`SECTION'或`SEGMENT': 改变和定义段
 
`ORG': 二进制程序的起点位置。

混合 16 位与 32 位代码 :
本章将介绍一些跟非常用的地址与跳转指令相关的一些问题, 这些问题当你在
编写操作系统代码时会常遇上,比如保护模式初始化过程,它需要代码� �作混合
的段 size,比如在 16 位段中的代码需要去修改在 32 位段中的数据,或者在不同的
size 的段之间的跳转.
9.1 混合 Size 的跳转.
最常用的混合 size 指令的形式是在写 32 位操作系统时用到的:在 16 位模式中完成
你的设置,比如载入内核,然后你必须通过切入到保护模式中引导它,然后跳转到
32 位的内核起始地址处.在一个完全 32 位的操作系统中,这是你唯一需要用到混合
size 指令的地方,因为在它之间的所有事情都可以在纯 16 位代码中完成,而在它之
后的所在事情都在纯 32 位代码中.
这种跳转必须指定一个 48 位的远地址,因为目标段是一个 32 位段.但是,它必须在
16 位段中被汇编,所以,仅仅如下面写代码:
jmp 0x1234:0x56789ABC ; wrong!
不会正常工作,因为地址的偏移域部分会被截断成'0x9ABC',然后,跳转会是一个
普通的 16 位远跳转.
Linux 内核的设置代码使用'as86'通过手工编码� �产生这条指令,使用'DB'指令,
NASM 可以比它更好些,可以自己产生正确的指令,这里是正确的做法:
jmp dword 0x1234:0x56789ABC ; right
'DWORD'前缀(严格地讲,它应该放在冒后的后面,因为它只是把偏移域声明为
doubleword;但 是 NASM 接受任何一种形式,因为两种写法都是明确的)强制偏移域
在假设你正从一个 16 段跳转到 32 位段的前提下,被处理为 far.
你可以完成一个相反的操作,从一个 32 位段中跳转到一个 16 位段,使用'word'
前缀:
jmp word 0x8765:0x4321 ; 32 to 16 bit
如果'WORD'前缀在 16 位模式下被指定, 或者'DWORD'前缀在 32 位模式下被指定,
它们都会被忽略,因为它们每一个都显式强制 NASM 进入一个已进进入的模式.



9.3 其他的混合 size 指令.
你可能需要用于获取数据的其它的方式可能就是使用字符串指令('LODSx'
'STOSx',等等)或'XLATB'指令.这些指令因为不带有任何参数,看上去好像很难
在它们被汇编进 16 位段的 时候使它们使用 32 位地址.
而这正是 NASM 的'a16'和'a 32'前缀的目的,如果你正在 16 位段中编写'LODSB',
但它是被用来获取一个 32 位段中的字符串的,你应当把目标地址载入'ESI',然
后编写:
a32 lodsb
这个前缀强制地址的 size 为 32 位,意思是'LODSB'从[DS:ESI]中载入内容,而不是
从[DS:SI]中.要在编写 32 位段的时候,获取在 16 位段中的字符串,相应的前缀'a16'
可以被使用.
'a16'和'a32'前缀可以被运用到 NASM 指令表的任何指令上,但是他们中的大多数
可以在没有这两个前缀的情况下产生所有有用的形式.这两个前缀只有在那些带
有隐式地址的指令中是有效的: `CMPSx' (section B. 4.27),
`SCASx' (section B.4.286), `LODSx' (section B.4.141), `STOSx'
(section B.4.303), `MOVSx' (section B.4.178), `INSx' (section
B.4.121), `OUTSx' (section B.4.195), and `XLATB' (section B.4.334).
还有,就是变量压栈与出栈指令,(`PUSHA'和`POPF' 和更常用的`PUSH'� ��`POP')
可以接受'a16'或'a32'前缀在堆栈段用在另一个不同 size 的代码段中的时候,强
制一个特定的'SP'或' ESP'被用作栈指针,
'PUSH'和'POP',当在 32 位模式中被用在段寄存器上时,也会有一个不同的行为,
它们会一次操作 4bytes,而最高处的两个被忽略,而最底部的两个给出正被操作的
段寄存器的值.为了强制 push 和 pop 指令的 16 位行为,你可以使用操作数前缀'o16'
o16 push ss
o16 push ds
这段代码在栈空间中开辟一个 doubleword 用于存放两个段寄存器,而在一般情况
下,这一个 doubleword 只会存放一个寄存器的值.
(你也可以使用'o32'前缀在 16 位模式下强制 32 位行为,但这看上去并没有什么用
处.)


第十章: 答疑
本章介绍一些用户在使用 NASM 时经常遇到的普遍性问题,并给出解答.同时,如果你
发现了这儿还未列出的 BUG,这儿也给出提交 bug 的方法.
10.1 普遍性的问题.
10.1.1 NASM 产生了低效的 代码.
我得到了很多关于 NASM 产生了低效代码的 BUG 报告,甚至是产生错误代码,比如像
指令'ADD ESP,8'产生的代码.其实这是一个经过深思熟虑设计特性,跟可预测的
输出相关:NASM 看到'ADD ESP,8'时,会产生一个预留 32 位偏移的指令形式.如果你
希望产生一个节约空间的指令形式,你必须写上'ADD ESP,BYTE 8'.这不是一个 BUG,
至多也只能算是一个不好的特性,各人看法不同而已.
10.1.2 我的 jump 指令超出范围.
相似的,人们经常抱怨说在他们使用条件跳转指令时(这些指令缺省状况下是'short'
的)经常需要跳转比较远,而 NASM 会报告说'short jump out of range',而不作长
远转.
同样,这也是可预测执行的一个部分,但实际上还有一个更有实际的理由.NASM 没有
办法知道它产生的代码运行的处理器的类型;所以它自己不能决定它应该产生'
Jcc NEAR'类型的指令,因为它不知道它正在 386 或更高一级的处理� �上工作.相反,
它把可能超出范围的短'JNE'指令替换成一个很短的'JE'指令,这个指令仅仅跳过
一个'JMP NEAR'指令;对于低于 386 的处理器,这是一个可行的解决方案,但是对于
有较好的分支预测功能的处理器很难有较好的效果,所以可代之以'JNE NEAR'.所
以,产生什么的指令还是取决于用户,而不是汇编器本身.
10.1.3 `ORG'不正常工作.
那些用'bin'格式写引导扇区代码的人们经常抱怨'ORG'没有按他们所希望的那样
正常工作:为了把'0xAA55'放到 512字节的引导扇区的末尾,使用 NASM 的人们会这样
写:
ORG 0
; some boot sector code
ORG 510
DW 0xAA55
这不是 NASM 中使用'ORG'的正确方式,不会正常工作.解决这个问题的正确方法是使用
'TIMES'操作符,就象这样:
ORG 0
; some boot sector code
TIMES 510-($-$$) DB 0
DW 0xAA55
'TIME'操作符会在输出中插入足够数量的零把汇编点移到 510.这种办法还有一个
好处,如果你意外� �在你的引导扇区中放入了太多的内容,以致超出容量,NASM 会
在汇编时检测到这个错误,并报告.所以你最终就不必重汇编并去找出错误所在.
10.1.4 `TIMES'不正常工作.
关于上面代码的另一个普遍性的问题是,有人这样写'TIMES'这一行:
TIMES 510-$ DB 0
因为'$'是一个纯数字,就像 510,所以它们相减的值也是一个纯数字,可以很好地
被 TIMES 使用.
NASM 是一个模块化的汇编器:不同的组成部分被设计为可以很容易的单独重用,所
以它们不会交换一些不必要的信息.结果,'BIN'输出格式尽管被'ORG'告知'.text'
段应当在 0 处开始,但是不会把这条信息传给表达式的求值程序.所以对求值程序
来讲,'$'不是一个纯数值:它是一个从一个段基址开始的偏移值.因为'$'和 510'之
间的计算结果也不是一个纯数,而是含有一个段基址.含有一个段基址的结果是不能
作为参数传递给'TIMES'的.
解决方案就象上一节� ��描述的,应该如下:
TIMES 510-($-$$) DB 0
在这里,'$'和'$$'是从同一个段基址的偏移,所以它们相减的结果是一个纯数,这
句代码会解决上述问题,并产生正确的代码.
10.2 Bugs
我们还从来没有发布过一个带有已知 BUG 的 NASM 版本.但我们未知的 BUG 从来就是不
停地出现.你发现了任何 BUG,应当首先通过在
`https://sourceforge.net/projects/nasm/'(点击 bug)的'bugtracker'提交给
我们,如果上述方法不行,请通过 1.2 中的某一个联系方式.
请先阅读 2.2,请不要把列在那儿的作为特性的东西作为 BUG 提交给我们.(如果你认
为这个特性很不好,请告诉我们你认为它应当被修改的原因,而不是仅仅给我们一
个'这是一个 BUG')然后请阅读 10.1,请不要把已经列在那里的 BUG 提交给我们.
如果你提交一个 bug,请给我们下面的所有信息.
(这部分信息一般用户并不关心,在些省略,原文请参考 NASM 的英文文档.)
附录 A: Ndi sasm
-------------------
反汇编器, NDISASM
A.1 简介
反汇编器是汇编器 NASM 的一个很小的附属品.我们已经拥有一个具有完整的指令
表的 x86 汇编器,如果不把这个指令表尽最大可能地利用起来,似乎很可惜,所以
我们又加了一个反汇编器,它共享 NASM 的指令表(并附加上一些代码)
反汇编器仅仅产生二进制源文件的反汇编.NDISASM 不理解任何目标文件格式,就
象'objdump',也不理解'DOS .EXE'文件,就象'debug',它仅仅反汇编.
A.2 开始: 安装.
参阅 1.3 的安装指令.NDISASM 就象 NASM,也有一个帮助页,如果你在一个 UNIX 系统下,
你可能希望把它放在一个有用的地方.
A.3 运行 NDISASM
要反汇编一个文件,你可以象下面这样使用命令:
ndisasm [-b16 | -b32] filename
NDISASM 可以很容易地反汇编 16 位或 32 位代码,当然,前提是你必须记得给它指定是
哪种方式.如果'-b'开关没有,NDISASM 缺省工作在 16 位模式下.'-u'开关 也包含 32 位
模式.
还有两个命令行选项,'-r'打印你正运行的 NDISASM 的版本号,'-h'给你一个有关命
令行选项的简短介绍.
A.3.1 COM 文件: 指定起点地址.
要正确反汇编一个'DOS.COM'文件,反汇编器必须知道文件中的第一条指令是被装载
到地址'0x100'处的,而不是 0,NDISASM 缺省地认为你给它的每一个文件都是装载到 0
处的,所以你必须告诉它这一点.
'-o'选项允许你为你正反汇编的声明一个不同的起始地址.它的参数可以是任何
NASM 数值格式:缺省是十进制,如果它以''$''或''0x''开头,或以''H'结尾,它是十
六进制的,如果以''Q''结尾,它是 8 进制的,如果是''B''结尾,它是二进制的.
所以,反汇编一个'.COM'文件:
ndisasm -o100h filename.com
能够正确反汇编.
A.3.2 代码前有数据: 同步.
假设你正反汇编一个含有一些不是机器码的数据的文件,这个文件当然也含有一些
机器码.NDISASM 会很诚实地去研究数 据段,尽它的能力去产生机器指令(尽管它们中
的大多数看上去很奇怪,而且有些还含有不常见的前缀,比如:'FS OR AX, 0x240A'')
然后,它会到达代码段处.
假设 NDISASM 刚刚完成从一个数据段中产生一堆奇怪的机器指令,而它现在的位置正
处于代码段的前面一个字节处.它完全有可能以数据段的最后一个字节为开始产生另
一个假指令,然后,代码段中的第一条正确的指令就看不到了,因为起点已经跳过这条
指令,这确实不是很理想.
为了避免这一点,你可以指定一个'同步'点,或者可以指定你需要的同步点的数目(但
NDISASM 在它的内部只能处理 8192 个同步点).同步点的定义如下:NDISASM 保证会到达
这个同步点.如果它认为某条指令会跳过一个同步点,它会忽略这条指令,代之以一个
'DB'.所以它会从同步点处开始反汇编,所以你可以看到你的代码段中的所有指令.
同步点是用'-s'选项来指定的 :它们以从程序开始处的距离来衡量,而不是文件位置.
所以如果你要从 32bytes 后开始同步一个'.COM'文件,你必须这样做:
ndisasm -o100h -s120h file.com
而不是:
ndisasm -o100h -s20h file.com
就象上面所描述的,如果你需要,你可以指定多个同步记号,只要重复'-s'选项即可.
A.3.3 代码和数据混合: 自动(智能)同步.
假设你正在反汇编一个'DOS'软盘引导扇区(可能它含有病毒,而你需要理解病毒,这
样你就可以知道它可能会对你的系统造成什么样的损害).一般的,里面会含有'JMP'
指令,然后是数据,然后接下来才是代码,所以,这很可能会让 NDISASM 不能在数据与
代码交接处找不到正确的点,所以同步点是必须的.
另一方面,你为什么要手工指定同步点呢?你要找出来的同步点的地址,当然是可以
从'JMP'中读取,然后可以用它的目标地址作为一个同步点,而 NDISADM 是否可以为你
做到这一点?
答案当然是可以 :使用同步开关'-a'(自动同步)或'-i'(智能同步)会启用'自动同步
"模式.自动同步模式为 PC 相关的前向引用或调用指令自动产生同步点.(因为 NDISASM
是一遍的,如果它遇上一个目标地址已经被处理过的 PC 相关的跳转,它不能做什么.)
只有 PC 相关的 jump 才会被处理,因为一个绝对跳转可能通过一个寄存器(在这种情况
下,NDISASM 不知道这个寄存器中含有什么)或含有一个段地址(在这种情况下,目标代
码不在 NDISASM 工作的当前段中,所以同步点不能被正确的设置)
对于一些类型的文件,这种机制会自动把同步点放到所有正确的位置,可以让你不必
手工放置同步点.但是,需要强调的是自动模式并不能保证找出所有的同步点,你可能
还是需要手工放置同步点.
自动同步模式不会禁止你手工声明同步点:它仅仅只是把自动产生的同步点加上.同
时指定'-i'和'-s'选项是完全可行的.
关于自动同 步模式,另一个需要提醒的是,如果因为一些讨厌的意外,你的数据段中的
一些数据被反汇编成了 PC 相关的调用或跳转指令,NDISASM 可能会很诚实地把同步点放
到所有的这些位置,比如,在你的代码段中的某条指令的中间位置.同样,我们不能为此
做什么,如果你有问题,你还是必须使用手工同步点,或使用'-k'选项(下面介绍)来禁
止数据域的反汇编.
A.3.4 其他选项.
'-e'选项通过忽略一个文件开头的 N 个 bytes 来跳过一个文件的文件头.这表示在反汇
编器中,文件头不被计偏移域中:如果你给出'-e10 -o10',反汇编器会从文件开始的
10byte 处开始,而这会对偏称域给出 10,而不是 20.
'-k'选项带有两个逗号--分隔数值参数,第一个是汇编移量,第二个是跳过的 byte 数.
这是从汇编偏移量处开始计算的跳过字节数:它的用途是禁止你需要的数据段被反汇
编.
A.4 Bug 和改进.
现在还没有已知的 bug.但 是,如果你发现了,并有了补丁, 请发往`jules@d sf.org.uk'
或`anakin@pobox.com', 或者在`https://sourceforge.net/projects/nas m/'上的
开发站点,我们会改进它们,请多多给我们改进和新特性的建议.
将来的计划包括能知道特定的指令运行在那种处理器上,并能标出那些对于某些处理
器来说过于高级的指令(或是'FPU'指令,或是没有公开的操作符, 或是特权保护模式
指令,或是其它).





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值