zoukankan      html  css  js  c++  java
  • NASM网际编译器手册(十)

    第九章 混合16位和32位编码


    本章主要说明一些不常用的地址格式和跳转指令,在写象保护模式下的操作系统代码时需要的混合段尺寸操作
    代码,如16位的段试途修改一个32位的数据或在不同尺寸段间的跳转。


    9.1 混合尺寸跳转
    当写一个32位的系统时要用一个常用的混合尺寸指令:你在16位模式设置,如取内核,然后切换到保护模式中
    跳到32位内核的起始地址。在一个全32位的OS中,这将需要混合指令,因为种种原因在它在纯16位代码运行前
    和纯32位代码运行后。跳转必须指定一个48位的远地址,因为种种原因一个目标地址为32位的。然而,它必须
    在一个16位的段中汇编。所以这样做:
    jmp 0x1234:0x56789ABC;错的
    将不工作,因为偏移部分的地址将被载成0x9ABC并跳到一个原始的16位远地址处。
    Linux内核设置代码得到相似于as86用代码生成的指令,用DB指令。NASM可以更好些,但它自己会生成正确的
    代码: jmp dword 0x1234:0x56789ABC;对的
    DWORD前缀(严格的说,它应该跟在冒号后面,因为种种原因它定义了一个doubleword的偏移;但NASM将会接受
    这种格式,因为两者都是明确的)将使偏移部分被做为far对待,假设你故意写一个从16位段到32位段的跳转。
    你可以保佑和相反操作,从32位段到16位用一个WORD前缀:
    jmp word 0x8765:0x4321 ;32到16位
    如果WORD前缀在16位模式中被指定,或DWORD前缀在32位模式它们将被忽略,因为每一个会使NASM在它在的模式中操作。


    92. 在不同尺寸段中定址
    如果你的OS是16位和32位混和,或你正写一个DOS扩展,你想处理一些16位段和32位。这时,你将在16位段中
    写一个访问在32位段中的数据,反之也是。
    最简单的方法是保证你用一个地址寄存器,因为有效地址包含一个32位的寄存器将被变成32位地址 ,所以:
    mov eax,offset_into_32_bit segment_specified_by_fs
    mov dword [fs:eax],0x11223344
    这样做不错,但如果你已经知道了你要的精确偏移,这就有些讨厌(因为它浪费了一个指令和一个寄存器)。x86
    体系允许32位有效地址指定4字节的偏移,所以为什么NASM不能为这个目的生成最好的指令?它能,象在第
    9.1节中,你只需要用DWORD前缀地址,并且为一个32位的地址:
    mov dword [fs:dwrod my_offset],0x11223344
    也象在第9.1节中一样,NASM不注意DWORD前缀是在段重载前还是后,所以关于一个美观的争论为:
    mov dword [dword fs:my_offset],0x11223344
    不要对DWORD前缀在方括号外感到迷惑,这是为了控制存在地址上的数据的尺寸 。在方括号里面的将控制地址
    本身的长度。这两个很容易区分:
    mov word [dword 0x12345678],0x9abc
    这将移动16个位数据到用32位偏移指定的位置。你也可以指令WORD或DWORD前缀来使FAR前缀来表示跳转或
    调用,例如:
    call dword far [fs:word 0x4321]
    这条指令包含了16位偏移指定的地址;它从中取出48位远指针(16位段和32位偏移),然后调用地址。


    9.2 其它混合尺寸的指令
    另外的访问数据的指令为字串指令(LODSx,STOSx等待)或XLATB指令。这些指令由于它们没有参数,在16位段中
    汇编是时32位定址不是很容易。这是NASM用a16和a32前缀的原因。如果你在16位段中用LODSB编码但它支持
    在32位段中访问一个字串,你可以从ESI中取出地址然后:
    a32 lodsb
    这个前缀使定址的尺寸为32位的,意味着LODSB从[DS:ESI]中取数而不是[DS:SI]。当写一个32位代码时要在
    一个16位段中访问字串,可以用a16前缀。
    a16和a32前缀可以用在NASM指令表中的任何指令前,但大多数指令在没有用前缀的时,也会生成正确的指令。
    这个前缀对于隐式的定址是有用的:CMPSx,(见第A.24节),SCASX(见第A.229节),LODSx(见第A.117节),STOSx(见第
    A.243节),MOVSx(见第A.137节),INSx(见第A.98节),OUTSx(见第A.149节)和XLATB(见第A.269节),不同的push和pop指令
    也可以用a16或a32做前缀使SP或ESP中的一个成为堆栈指针。(PUSHA和POPF与PUSH和POP一样)在这种情况下,
    堆栈段将与代码段为不一样的尺寸 。PUSH和POP,当用在32位模式的段寄存器中时,每次将处理4个字节,上
    面的2个将忽略,而下面的2个将交给段寄存器处理。为了使用16位行为的段寄存器push和pop指令,你可以用操
    作尺寸前缀o16:
    o16 push ss
    o16 push ds
    这段代码将存一个doubleword的堆栈通过将两个段寄存器修改空间用压入堆栈和消费的方法。(你也可以用o32前
    缀在16位模式中用,但这很少用。

    第十章 常见问题

    本章描述了用户在使用NASM所遇到的一个问题及解答。它也给出了一些NASM中的显示bugs的指令,如果你发
    现它不在列表中时。


    10.1常见问题

    10.1.1 NASM生成的无效代码
    我得到很多\'bug\'显示NASM生成了无效的或\'wrong\'的代码象指令ADD ESP,8。这是一个故意设计的特性:连到
    预期的输出:NASM处理ADD ESP,8时将生成为32位偏移保存空间的指令。你需要用ADD ESP,BYTE 8如果你想要
    让空间有效的指令。这不是bug:这是一个有歧义的特性并只是一种观点。


    10.1.2我的跳转出界了
    同样,人们抱怨当他们用跳转指令跳太远时,NASM报告\'short jump out of range\'而不是生成一个远跳转。这也是
    预处理的一种,但事实是有更多的原因。NASM不能告诉代码所运行的处理器的类型;所以它不能决定它是在
    386还是更高的机器运行生成Jcc NEAR类型的指令。另外它可心伤脑筋一个短JE指令代替出界的短JNE指令也就
    是跳过JMP NEAR;这对低于386的处理器是一个敏感的解决方法,保龄球对有好的预期操作或用JNE NEAR的处
    理器上却无效。所以这是由用户而不是编译器来决定应生成什么的指令。


    10.1.3 ORG不能工作
    人们用bin格式引导程序经常抱怨ORG不能象他们想的去工作:为了在512个字节后放一个0xAA55标志,他们的
    用MASM代码如下:
    ORG 0
    ;一些扇区代码
    ORG 510
    DW 0xAA55
    而在NASM中用ORG定向符则不工作,在NASM中正确的方法是TIMES定向符:
    ORG 0
    ;一些扇区代码
    TIMES 510-($-$$) DB 0
    DW 0xAA55
    TIMES定向符将会插入适当的0到输出中使汇编大小为510个字节。这种方法也可以是如果你意外的将你的引导
    扇区填满,NASM将在汇编时捕到错误并显示,所以你不必在运行时反汇编它来找错误。


    10.14 TIMES不工作
    用上面代码常见的问题为人们用下面的代码:
    TIME 510-$ DB 0
    这会使$成为一个纯数字,象510一样,它们间的区别为一个数字被传给了TIMES。NASM是一个模块式的编译器
    :不同的单元被设计成可重用的和易分隔的,所以它们不需要交换信息。结果bin输出格式甚至用ORG定向符
    来表明.text段为从0开始,而不把这个信息传给后面的表达式。所以从计算的观点看,$不是一个纯数字:它是
    从一个段基址的偏移,然而在$和510之间不同的也不是纯数字,除了包括一个段基址。包含段基址的值的不能
    被传到TIMES里做参数。象前面的面一样,应中下写:
    TIMES 510-($-$$) DB 0
    这里$和$$是从相同段基址的偏移,所以它们的区别为纯数字。这解决了问题并生成敏感的代码。


    10.2 Bugs
    我们从未出版过带有任何已知问题的NASM。这不会经常停在我们不很知道的地方。你发现的任何问题请向
    报告。
    请先读第2.2节,不要报告列在列表中的问题。(如果你觉得这个特性不好请说明原因,但不要写\'This is a buf\')。
    然后 读一下10.1节也不要报告在列表中问题。
    如果你报告了一个问题,请给我们下面的信息:
    你用NASM的操作系统,DOS,Linux,NetBSD,Win16,Win32,VMS(外加的)或其它。
    如果你在DOS或Win32下运行NASM,请告诉我们你是从DOS源文档中编译的执行文件还是用标准的二进制文档。
    如果你用本地的二进制文件,请用标准的二进制文件重新生成一遍,使我们更容易修正问题。
    你用的NASM的版本号,及你如何运行它的。给我们精确的命令行。和NASM环境变量的内容。
    你所用程序的任何补充版本,你是如何执行它们的。如果问题在连接时出现,说明你用的连接器,及它的版本
    和连接器的命令行。如果问题在连接用另外的编译器生成的目标文件出现的,说明是什么编译器,什么版本,
    和你用的命令行。(如果你用的是一个IDC请用命令行版本的编译重新生成一下。)
    如果有可能,请发给我们一分NASM出错的源文件。如果这引起版权问题(如你只能重生成限制发布代码的一个
    问题)那么考虑以下两点:首先,我们保证任何发给我们源码的文件只用于调试NASM,如果我们修改完后,我们
    将删除我们所有的拷贝。第二,我们接收很大的源码。文件越小越好。一个三行的文件将比上千行的代码更容易找到问题。(当然在大文件中有些问题突然出现)
    问题的具体描述,\'它不能工作\'不是一个有用的描述!请精确的描述应该发生什么现象,不应该发生什么。例如:
    \'\'NASM生成一条错误信息表示第3行有个错误实际在第5行\',\'NASM生成一个错误信息我认为 它根本不应该生成\'
    ,\'从源码生成的目标文件使我的连接器不能工作\';\'一个输出文件的第9个字节为66而我认为它应为77\'
    如果你认为 输出文件从NASM出来是错的,请发给我们。这让我们来决定我们的NASM输出是否为同一个文件,
    或问题是在于我们还是你们。我们可以处理的二进制文件为MIME附件,uuencode和BinHex.我们提供FTP站点
    你可以上传有问题的文件,但寄给我们是最容易的。
    另外信息或数据文件可能有用,如果NASM失败生成一个目标文件而TASM生成一个无错的等同文件。那么请把
    两个文件都寄给我们。我们可以看出TASM的不同?

  • 相关阅读:
    SQL 死锁
    【Redis】存入redis的值,莫名其妙多了很多“u0000”
    for循环里面使用task
    Idea---SpringBoot整合Mybatis问题整理
    到相对应的元素位置。
    npm安装任何包都报错的解决办法
    form表单提交和ajax提交的使用场景和差别
    computed计算属性以及与watch的区别
    了解js的宏任务(macrotask)和微任务(microtask)以及Event-Loop
    async/await,promise的优缺点
  • 原文地址:https://www.cnblogs.com/cnlmjer/p/4099877.html
Copyright © 2011-2022 走看看