Linux汇编语言开发指南简介_第1页
Linux汇编语言开发指南简介_第2页
Linux汇编语言开发指南简介_第3页
Linux汇编语言开发指南简介_第4页
Linux汇编语言开发指南简介_第5页
已阅读5页,还剩81页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Linux汇编语言开发指南汇编语言开发指南 一、简介一、简介 作为最基本的编程语言之一,汇编语言虽然应用的范围作为最基本的编程语言之一,汇编语言虽然应用的范围 不算很广,但重要性却勿庸置疑,因为它能够完成许多其它不算很广,但重要性却勿庸置疑,因为它能够完成许多其它 语言所无法完成的功能。就拿语言所无法完成的功能。就拿 Linux Linux 内核来讲,虽然绝大内核来讲,虽然绝大 部分代码是用部分代码是用 C C 语言编写的,但仍然不可避免地在某些关语言编写的,但仍然不可避免地在某些关 键地方使用了汇编代码,其中主要是在键地方使用了汇编代码,其中主要是在 Linux Linux 的启动部分。的启

2、动部分。 由于这部分代码与硬件的关系非常密切,即使是由于这部分代码与硬件的关系非常密切,即使是 C C 语言也语言也 会有些力不从心,而汇编语言则能够很好扬长避短,最大限会有些力不从心,而汇编语言则能够很好扬长避短,最大限 度地发挥硬件的性能。度地发挥硬件的性能。 大多数情况下大多数情况下 Linux Linux 程序员不需要使用汇编语言,因为即程序员不需要使用汇编语言,因为即 便是硬件驱动这样的底层程序在便是硬件驱动这样的底层程序在 Linux Linux 操作系统中也可以操作系统中也可以 用完全用用完全用 C C 语言来实现,再加上语言来实现,再加上 GCC GCC 这一优秀的编译器这一优

3、秀的编译器 目前已经能够对最终生成的代码进行很好的优化,的确有目前已经能够对最终生成的代码进行很好的优化,的确有 足够的理由让我们可以暂时将汇编语言抛在一边了。但实足够的理由让我们可以暂时将汇编语言抛在一边了。但实 现情况是现情况是 Linux Linux 程序员有时还是需要使用汇编,或者不得程序员有时还是需要使用汇编,或者不得 不使用汇编,理由很简单:精简、高效和不使用汇编,理由很简单:精简、高效和 libc libc 无关性。无关性。 假设要移植假设要移植 Linux Linux 到某一特定的嵌入式硬件环境下,首先到某一特定的嵌入式硬件环境下,首先 必然面临如何减少系统大小、提高执行效率等

4、问题,此时必然面临如何减少系统大小、提高执行效率等问题,此时 或许只有汇编语言能帮上忙了。或许只有汇编语言能帮上忙了。 汇编语言直接同计算机的底层软件甚至硬件进行交互,汇编语言直接同计算机的底层软件甚至硬件进行交互, 它具有如下一些优点:它具有如下一些优点: 能够直接访问与硬件相关的存储器或能够直接访问与硬件相关的存储器或 I/O I/O 端口;端口; 能够不受编译器的限制,对生成的二进制代码进行完全能够不受编译器的限制,对生成的二进制代码进行完全 的控制;的控制; 能够对关键代码进行更准确的控制,避免因线程共同访能够对关键代码进行更准确的控制,避免因线程共同访 问或者硬件设备共享引起的死锁;

5、问或者硬件设备共享引起的死锁; 能够根据特定的应用对代码做最佳的优化,提高运行速能够根据特定的应用对代码做最佳的优化,提高运行速 度;度; 能够最大限度地发挥硬件的功能。能够最大限度地发挥硬件的功能。 同时还应该认识到,汇编语言是一种层次非常低的语言,它同时还应该认识到,汇编语言是一种层次非常低的语言,它 仅仅高于直接手工编写二进制的机器指令码,因此不可避免仅仅高于直接手工编写二进制的机器指令码,因此不可避免 地存在一些缺点:地存在一些缺点: 编写的代码非常难懂,不好维护;编写的代码非常难懂,不好维护; 很容易产生很容易产生 bugbug,难于调试;,难于调试; 只能针对特定的体系结构和处理器

6、进行优化;只能针对特定的体系结构和处理器进行优化; 开发效率很低,时间长且单调。开发效率很低,时间长且单调。 Linux Linux 下用汇编语言编写的代码具有两种不同的形式。下用汇编语言编写的代码具有两种不同的形式。 第一种是完全的汇编代码,指的是整个程序全部用汇编语言第一种是完全的汇编代码,指的是整个程序全部用汇编语言 编写。尽管是完全的汇编代码,编写。尽管是完全的汇编代码,Linux Linux 平台下的汇编工具也平台下的汇编工具也 吸收了吸收了 C C 语言的长处,使得程序员可以使用语言的长处,使得程序员可以使用 #include#include、 #ifdef #ifdef 等预处理

7、指令,并能够通过宏定义来简化代码。等预处理指令,并能够通过宏定义来简化代码。 第二种是内嵌的汇编代码,指的是可以嵌入到第二种是内嵌的汇编代码,指的是可以嵌入到C C语言程序中语言程序中 的汇编代码片段。虽然的汇编代码片段。虽然 ANSI ANSI 的的 C C 语言标准中没有关于内语言标准中没有关于内 嵌汇编代码的相应规定,但各种实际使用的嵌汇编代码的相应规定,但各种实际使用的 C C 编译器都做编译器都做 了这方面的扩充,这其中当然就包括了这方面的扩充,这其中当然就包括 Linux Linux 平台下的平台下的 GCCGCC。 二、二、Linux Linux 汇编语法格式汇编语法格式 绝大多

8、数绝大多数 Linux Linux 程序员以前只接触过程序员以前只接触过DOS/Windows DOS/Windows 下的汇编语言,下的汇编语言, 这些汇编代码都是这些汇编代码都是 Intel Intel 风格的。但在风格的。但在 Unix Unix 和和 Linux Linux 系统中,更多系统中,更多 采用的还是采用的还是 AT hello.asm ; hello.asm section .data section .data ; ; 数据段声明数据段声明 msg db “Hello, world!”, 0 xA ; msg db “Hello, world!”, 0 xA ; 要输出的

9、字符串要输出的字符串 len equ $ - msg ; len equ $ - msg ; 字串长度字串长度 section .text section .text ; ; 代码段声明代码段声明 global _start global _start ; ; 指定入口函数指定入口函数 _start: _start: ; ; 在屏幕上显示一个字符串在屏幕上显示一个字符串 mov edx, len mov edx, len ; ; 参数三:字符串长度参数三:字符串长度 mov ecx, msg mov ecx, msg ; ; 参数二:要显示的字符串参数二:要显示的字符串 mov ebx, 1

10、mov ebx, 1 ; ; 参数一:文件描述符参数一:文件描述符(stdout) (stdout) mov eax, 4 mov eax, 4 ; ; 系统调用号系统调用号(sys_write) (sys_write) int 0 x80 int 0 x80 ; ; 调用内核功能调用内核功能 ; ; 退出程序退出程序 mov ebx, 0 mov ebx, 0 ; ; 参数一:退出代码参数一:退出代码 mov eax, 1 mov eax, 1 ; ; 系统调用号系统调用号(sys_exit) (sys_exit) int 0 x80 int 0 x80 ; ; 调用内核功能调用内核功能 上

11、面两个汇编程序采用的语法虽然完全不同,但功能却都是调用上面两个汇编程序采用的语法虽然完全不同,但功能却都是调用 Linux Linux 内核提供的内核提供的 sys_write sys_write 来显示一个字符串,然后再调用来显示一个字符串,然后再调用 sys_exit sys_exit 退出程序。在退出程序。在 Linux Linux 内核源文件内核源文件 include/asm-include/asm- i386/unistd.h i386/unistd.h 中,可以找到所有系统调用的定义。中,可以找到所有系统调用的定义。 四、四、Linux Linux 汇编工具汇编工具 Linux L

12、inux 平台下的汇编工具虽然种类很多,但同平台下的汇编工具虽然种类很多,但同 DOS/Windows DOS/Windows 一样,一样, 最基本的仍然是汇编器、连接器和调试器。最基本的仍然是汇编器、连接器和调试器。 1.1.汇编器汇编器 汇编器(汇编器(assemblerassembler)的作用是)的作用是将用汇编语言编写的源程序转换将用汇编语言编写的源程序转换 成二进制形式的目标代码成二进制形式的目标代码。Linux Linux 平台的标准汇编器是平台的标准汇编器是 GASGAS,它是,它是 GCC GCC 所依赖的后台汇编工具,通常包含在所依赖的后台汇编工具,通常包含在 binuti

13、ls binutils 软件包中。软件包中。GAS GAS 使用标使用标 准的准的 AT buf, size_t count); 该函数的功能最终是通过该函数的功能最终是通过 SYS_write SYS_write 这一系统调用来实现这一系统调用来实现 的。根据上面的约定,参数的。根据上面的约定,参数 fbfb、buf buf 和和 count count 分别存在寄存器分别存在寄存器 ebxebx、ecx ecx 和和 edx edx 中,而系统调用号中,而系统调用号 SYS_write SYS_write 则放在寄存器则放在寄存器 eax eax 中,当中,当 int 0 x80 int

14、0 x80 指令执行完毕后,返回值可以从寄存器指令执行完毕后,返回值可以从寄存器 eax eax 中获得。或许你已经发现,在进行系统调用时至多只有中获得。或许你已经发现,在进行系统调用时至多只有 5 5 个寄存个寄存 器能够用来保存参数,难道所有系统调用的参数个数都不超过器能够用来保存参数,难道所有系统调用的参数个数都不超过 5 5 吗?当然不是,例如吗?当然不是,例如 mmap mmap 函数就有函数就有 6 6 个参数,这些参数最后都个参数,这些参数最后都 需要传递给系统调用需要传递给系统调用 SYS_mmapSYS_mmap: void void * * mmap(void mmap(v

15、oid * *start, size_t length, int prot , int start, size_t length, int prot , int flags, int fd, off_t offset); flags, int fd, off_t offset); 当一个系统调用所需的参数个数大于当一个系统调用所需的参数个数大于 5 5 时,执行时,执行int 0 x80 int 0 x80 指令时仍需将系统调用功能号保存在寄存器指令时仍需将系统调用功能号保存在寄存器 eax eax 中,所不同的中,所不同的 只是全部参数应该依次放在一块连续的内存区域里,同时在寄只是全部参数应

16、该依次放在一块连续的内存区域里,同时在寄 存器存器 ebx ebx 中保存指向该内存区域的指针。系统调用完成之后,中保存指向该内存区域的指针。系统调用完成之后, 返回值仍将保存在寄存器返回值仍将保存在寄存器 eax eax 中。中。 由于只是需要一块连续的内存区域来保存系统调用的参数,由于只是需要一块连续的内存区域来保存系统调用的参数, 因此完全可以像普通的函数调用一样使用栈因此完全可以像普通的函数调用一样使用栈(stack)(stack)来传递系统来传递系统 调用所需的参数。调用所需的参数。 但要注意一点,但要注意一点,Linux Linux 采用的是采用的是 C C 语言的调用模式,这就语

17、言的调用模式,这就 意味着所有参数必须以相反的顺序进栈,即最后一个参数先入意味着所有参数必须以相反的顺序进栈,即最后一个参数先入 栈,而第一个参数则最后入栈。如果采用栈来传递系统调用所栈,而第一个参数则最后入栈。如果采用栈来传递系统调用所 需的参数,在执行需的参数,在执行int 0 x80 int 0 x80 指令时还应该将栈指针的当前值复指令时还应该将栈指针的当前值复 制到寄存器制到寄存器 ebxebx中。中。 六、命令行参数六、命令行参数 在在 Linux Linux 操作系统中,当一个可执行程序通过命令行启动时,其操作系统中,当一个可执行程序通过命令行启动时,其 所需的参数将被保存到栈中

18、:首先是所需的参数将被保存到栈中:首先是 argcargc,然后是指向各个命令行参数,然后是指向各个命令行参数 的指针数组的指针数组 argvargv,最后是指向环境变量的指针数据,最后是指向环境变量的指针数据 envpenvp。在编写汇编。在编写汇编 语言程序时,很多时候需要对这些参数进行处理,下面的代码示范了如语言程序时,很多时候需要对这些参数进行处理,下面的代码示范了如 何在汇编代码中进行命令行参数的处理:何在汇编代码中进行命令行参数的处理: 例例3. 3. 处理命令行参数处理命令行参数 # args.s # args.s .text .text .globl _start .globl

19、 _start _start: _start: popl %ecx # argc popl %ecx # argc vnext: vnext: popl %ecx # argv popl %ecx # argv test %ecx, %ecx # test %ecx, %ecx # 空指针表明结束空指针表明结束 jz exit jz exit movl %ecx, %ebx movl %ecx, %ebx xorl %edx, %edx xorl %edx, %edx strlen: strlen: movb (%ebx), %al movb (%ebx), %al inc %edx inc

20、%edx inc %ebx inc %ebx test %al, %al test %al, %al jnz strlen jnz strlen movb $10, -1(%ebx) movb $10, -1(%ebx) movl $4, %eax # movl $4, %eax # 系统调用号系统调用号 (sys_write) (sys_write) movl $1, %ebx # movl $1, %ebx # 文件描述符文件描述符(stdout) (stdout) int $0 x80 int $0 x80 jmp vnext jmp vnext exit: exit: movl $1,

21、%eax # movl $1,%eax # 系统调用号系统调用号(sys_exit) (sys_exit) xorl %ebx, %ebx # xorl %ebx, %ebx # 退出代码退出代码 int $0 x80 int $0 x80 ret ret 七、七、GCC GCC 内联汇编内联汇编 用汇编编写的程序虽然运行速度快,但开发速度非常慢,效用汇编编写的程序虽然运行速度快,但开发速度非常慢,效 率也很低。如果只是想对关键代码段进行优化,或许更好的办法是率也很低。如果只是想对关键代码段进行优化,或许更好的办法是 将汇编指令嵌入到将汇编指令嵌入到 C C 语言程序中,从而充分利用高级语言和

22、汇编语语言程序中,从而充分利用高级语言和汇编语 言各自的特点。但一般来讲,在言各自的特点。但一般来讲,在 C C 代码中嵌入汇编语句要比代码中嵌入汇编语句要比 纯粹纯粹 的汇编语言代码复杂得多,因为需要解决如何分配寄存器,以及如的汇编语言代码复杂得多,因为需要解决如何分配寄存器,以及如 何与何与C C代码中的变量相结合等问题。代码中的变量相结合等问题。 GCC GCC 提供了很好的内联汇编支持,最基本的格式是:提供了很好的内联汇编支持,最基本的格式是: _asm_(asm statements); _asm_(asm statements); 例如:例如: _asm_(nop); _asm_(

23、nop); 如果需要同时执行多条汇编语句,则应该用如果需要同时执行多条汇编语句,则应该用ntnt将各个语将各个语 句分隔开,例如:句分隔开,例如: _asm_( pushl %eax nt _asm_( pushl %eax nt movl $0, %eax nt movl $0, %eax nt popl %eax); popl %eax); 通常嵌入到通常嵌入到 C C 代码中的汇编语句很难做到与其它部分没有代码中的汇编语句很难做到与其它部分没有 任何关系,因此更多时候需要用到完整的内联汇编格式:任何关系,因此更多时候需要用到完整的内联汇编格式: _asm_(asm statements

24、: outputs : inputs : _asm_(asm statements : outputs : inputs : registers-modified); registers-modified); 插入到插入到 C C 代码中的汇编语句是以代码中的汇编语句是以:分隔的四个部分,分隔的四个部分, 其中第一部分就是汇编代码本身,通常称为指令部,其格式和其中第一部分就是汇编代码本身,通常称为指令部,其格式和 在汇编语言中使用的格式基本相同。指令部分是必须的,而其在汇编语言中使用的格式基本相同。指令部分是必须的,而其 它部分则可以根据实际情况而省略。它部分则可以根据实际情况而省略。 在将汇

25、编语句嵌入到在将汇编语句嵌入到C C代码中时,操作数如何与代码中时,操作数如何与C C代码中的代码中的 变量相结合是个很大的问题。变量相结合是个很大的问题。GCCGCC采用如下方法来解决这个问采用如下方法来解决这个问 题:程序员提供具体的指令,而对寄存器的使用则只需给出题:程序员提供具体的指令,而对寄存器的使用则只需给出 “样板样板”和约束条件就可以了,具体如何将寄存器与变量结合和约束条件就可以了,具体如何将寄存器与变量结合 起来完全由起来完全由GCCGCC和和GASGAS来负责。在来负责。在GCCGCC内联汇编语句的指令部中,内联汇编语句的指令部中, 加上前缀加上前缀%的数字的数字( (如如

26、%0%0,%1)%1)表示的就是需要使用寄存器表示的就是需要使用寄存器 的的“样板样板”操作数。操作数。 指令部中使用了几个样板操作数,就表明有几个变量需要指令部中使用了几个样板操作数,就表明有几个变量需要 与寄存器相结合,这样与寄存器相结合,这样GCCGCC和和GASGAS在编译和汇编时会根据后面给在编译和汇编时会根据后面给 定的约束条件进行恰当的处理。由于样板操作数也使用定的约束条件进行恰当的处理。由于样板操作数也使用%作作 为前缀,因此在涉及到具体的寄存器时,寄存器名前面应该加为前缀,因此在涉及到具体的寄存器时,寄存器名前面应该加 上两个上两个%,以免产生混淆。,以免产生混淆。 紧跟在指

27、令部后面的是输出部,是规定输出变量如何紧跟在指令部后面的是输出部,是规定输出变量如何 与样板操作数进行结合的条件,每个条件称为一个与样板操作数进行结合的条件,每个条件称为一个“约约 束束”,必要时可以包含多个约束,相互之间用逗号分隔开,必要时可以包含多个约束,相互之间用逗号分隔开 就可以了。就可以了。 每个输出约束都以每个输出约束都以=号开始,然后紧跟一个对操作数号开始,然后紧跟一个对操作数 类型进行说明的字后,最后是如何与变量相结合的约束。类型进行说明的字后,最后是如何与变量相结合的约束。 凡是与输出部中说明的操作数相结合的寄存器或操作数本凡是与输出部中说明的操作数相结合的寄存器或操作数本

28、身,在执行完嵌入的汇编代码后均不保留执行之前的内容,身,在执行完嵌入的汇编代码后均不保留执行之前的内容, 这是这是GCCGCC在调度寄存器时所使用的依据。在调度寄存器时所使用的依据。 输出部后面是输入部,输入约束的格式和输出约束相输出部后面是输入部,输入约束的格式和输出约束相 似,但不带似,但不带=号。如果一个输入约束要求使用寄存器,则号。如果一个输入约束要求使用寄存器,则 GCCGCC在预处理时就会为之分配一个寄存器,并插入必要的指在预处理时就会为之分配一个寄存器,并插入必要的指 令将操作数装入该寄存器。与输入部中说明的操作数结合令将操作数装入该寄存器。与输入部中说明的操作数结合 的寄存器或

29、操作数本身,在执行完嵌入的汇编代码后也不的寄存器或操作数本身,在执行完嵌入的汇编代码后也不 保留执行之前的内容。保留执行之前的内容。 有时在进行某些操作时,除了要用到进行数据输入和输有时在进行某些操作时,除了要用到进行数据输入和输 出的寄存器外,还要使用多个寄存器来保存中间计算结果,出的寄存器外,还要使用多个寄存器来保存中间计算结果, 这样就难免会破坏原有寄存器的内容。在这样就难免会破坏原有寄存器的内容。在GCCGCC内联汇编格式中内联汇编格式中 的最后一个部分中,可以对将产生副作用的寄存器进行说明,的最后一个部分中,可以对将产生副作用的寄存器进行说明, 以便以便GCCGCC能够采用相应的措施

30、。能够采用相应的措施。 下面是一个内联汇编的简单例子:下面是一个内联汇编的简单例子: 例例4.4.内联汇编内联汇编 / /* * inline.c inline.c * */ / int main() int main() int a = 10, b = 0; int a = 10, b = 0; _asm_ _volatile_(movl %1, %eax;nr _asm_ _volatile_(movl %1, %eax;nr movl %eax, %0; movl %eax, %0; :=r(b) / :=r(b) /* * 输出输出 * */ / :r(a) / :r(a) /* *

31、输入输入 * */ / :%eax); / :%eax); /* * 不受影响的寄存器不受影响的寄存器 * */ / printf(Result: %d, %dn, a, b); printf(Result: %d, %dn, a, b); 变量变量b b是输出操作数,通过是输出操作数,通过%0%0来引用,而变量来引用,而变量a a是输入操作数,是输入操作数, 通过通过%1%1来引用。来引用。 输入操作数和输出操作数都使用输入操作数和输出操作数都使用r r进行约束,表示将变量进行约束,表示将变量a a和和 变量变量b b存储在寄存器中。输入约束和输出约束的不同点在于输存储在寄存器中。输入约束和

32、输出约束的不同点在于输 出约束多一个约束修饰符出约束多一个约束修饰符=。 在内联汇编语句中使用寄存器在内联汇编语句中使用寄存器eaxeax时,寄存器名前应该加两个时,寄存器名前应该加两个 %,即,即%eax%eax。内联汇编中使用。内联汇编中使用%0%0、%1%1等来标识变量,任何等来标识变量,任何 只带一个只带一个%的标识符都看成是操作数,而不是寄存器。的标识符都看成是操作数,而不是寄存器。 内联汇编语句的最后一个部分告诉内联汇编语句的最后一个部分告诉GCCGCC它将改变寄存器它将改变寄存器eaxeax中中 的值,的值,GCCGCC在处理时不应使用该寄存器来存储任何其它的值。在处理时不应使用

33、该寄存器来存储任何其它的值。 上面的程序完成将变量上面的程序完成将变量a a的值赋予变量的值赋予变量b b,有几点需要说明:,有几点需要说明: 由于变量由于变量b b被指定成输出操作数,当内联汇编语句执行完毕后,被指定成输出操作数,当内联汇编语句执行完毕后, 它所保存的值将被更新。它所保存的值将被更新。 在内联汇编中用到的操作数从输出部的第一个约束开始编号,在内联汇编中用到的操作数从输出部的第一个约束开始编号, 序号从序号从0 0开始,每个约束记数一次,指令部要引用这些操作数时,只开始,每个约束记数一次,指令部要引用这些操作数时,只 需在序号前加上需在序号前加上%作为前缀就可以了。需要注意的是

34、,内联汇编作为前缀就可以了。需要注意的是,内联汇编 语句的指令部在引用一个操作数时总是将其作为语句的指令部在引用一个操作数时总是将其作为3232位的长字使用,位的长字使用, 但实际情况可能需要的是字或字节,因此应该在约束中指明正确的但实际情况可能需要的是字或字节,因此应该在约束中指明正确的 限定符:限定符: 限定符限定符 意义意义 mm、vv、o o 内存单元内存单元 r r 任何寄存器任何寄存器 q q 寄存器寄存器eaxeax、ebxebx、ecxecx、edxedx之之 一一 ii、h h 直接操作数直接操作数 EE和和F F 浮点数浮点数 g g 任意任意 aa、bb、cc、d d 分

35、别表示寄存器分别表示寄存器eaxeax、ebxebx、ecxecx和和edx edx SS和和D D 寄存器寄存器esiesi、edi edi I I 常数(常数(0 0至至3131) 八、小结八、小结 Linux Linux操作系统是用操作系统是用C C语言编写的,汇编只在必要的时语言编写的,汇编只在必要的时 候才被人们想到,但它却是减少代码尺寸和优化代码性能的候才被人们想到,但它却是减少代码尺寸和优化代码性能的 一种非常重要的手段,特别是在与硬件直接交互的时候,汇一种非常重要的手段,特别是在与硬件直接交互的时候,汇 编可以说是最佳的选择。编可以说是最佳的选择。LinuxLinux提供了非常

36、优秀的工具来支持提供了非常优秀的工具来支持 汇编程序的开发,使用汇编程序的开发,使用GCCGCC的内联汇编能够充分地发挥的内联汇编能够充分地发挥C C语言语言 和汇编语言各自的优点。和汇编语言各自的优点。 九、参考资料九、参考资料 在网站上可以找到大量的在网站上可以找到大量的LinuxLinux汇编资源。汇编资源。 软件包软件包binutilsbinutils提供了提供了asas和和ldld等实用工具,其相关信息可以在网站上等实用工具,其相关信息可以在网站上 找到。找到。 NASMNASM是是IntelIntel格式的汇编器,其相关信息可以在网站上找到。格式的汇编器,其相关信息可以在网站上找到

37、。 ALDALD是一个短小精悍的汇编调试器,其相关信息可以在网站上找到。是一个短小精悍的汇编调试器,其相关信息可以在网站上找到。 intel2gasintel2gas是一个能够将是一个能够将IntelIntel汇编格式转换成汇编格式转换成AT 上面的函数中:上面的函数中: LOCK_PREFIX:这是一个宏,如果定义了:这是一个宏,如果定义了 _SMP_,扩展为,扩展为“lock;”, 用于指定总线锁定前缀,否则扩展用于指定总线锁定前缀,否则扩展 为为“”“”。 ADDR:这也是一个宏,定义为:这也是一个宏,定义为(*(volatile struct _dummy *) addr) btcl

38、%1,%0:这就是嵌入的汇编语言指令,:这就是嵌入的汇编语言指令,btcl为指令操为指令操 作码,作码,%1, %0是这条指令两个操作数的占位符。后面的两个限定是这条指令两个操作数的占位符。后面的两个限定 字符串就用于描字符串就用于描 述这两个操作数。述这两个操作数。 : “=m” (ADDR):第一个冒号后的限定字符串用于描述:第一个冒号后的限定字符串用于描述 指令中的指令中的“输输 出出”操作数。刮号中的操作数。刮号中的ADDR将操作数将操作数 与与C语言的变量联系起来。这个语言的变量联系起来。这个 限定字符串表示指令限定字符串表示指令 中的中的“%0”就是就是addr指针指向的内存操作数

39、。这指针指向的内存操作数。这 是一是一 个个“输出输出”类型的内存操作数。类型的内存操作数。 : “ir” (nr):第二个冒号后的限定字符串用于描述指令:第二个冒号后的限定字符串用于描述指令 中的中的“输入输入” 操作数。这条限定字符串表示指令中的操作数。这条限定字符串表示指令中的 “%1”就是变量就是变量nr,这个的操,这个的操 作数可以是一个立即操作数可以是一个立即操 作数或者是一个寄存器操作数。作数或者是一个寄存器操作数。 *注:限定字符串与操作数占位符之间的对应关系是这注:限定字符串与操作数占位符之间的对应关系是这 样的:在所有样的:在所有 限定字符串中(包括第一个冒号后的以限定字符

40、串中(包括第一个冒号后的以 及第二个冒号后的所有限定字及第二个冒号后的所有限定字 符串),最先出现的字符串),最先出现的字 符串用于描述操作数符串用于描述操作数“%0”,第二个出现的字,第二个出现的字 符串描符串描 述操作数述操作数“%1”,以此类推。,以此类推。 汇编指令模板汇编指令模板 asm语句中的汇编指令模板主要由汇编指语句中的汇编指令模板主要由汇编指 令序列和限定字符串组成。令序列和限定字符串组成。 在一个在一个asm语句中可以包括多语句中可以包括多 条汇编指令。汇编指令序列中使用操作条汇编指令。汇编指令序列中使用操作 数占位符引用数占位符引用C语言语言 中的变量。一条中的变量。一条

41、asm语句中最多可以包含十个操语句中最多可以包含十个操 作数占位作数占位 符:符:%0,%1,.,%9。汇编指令序列后面是操作数限定字。汇编指令序列后面是操作数限定字 符串,对指令序列中的占位符进行限定。限定的内容包括:符串,对指令序列中的占位符进行限定。限定的内容包括: 该占位符该占位符 与哪个与哪个C语言变量对应,可以是什么类型的操作数语言变量对应,可以是什么类型的操作数 等等。等等。 限定字符串限定字符串 可以分为三个部分:输出操作数限定字符串可以分为三个部分:输出操作数限定字符串 (指令序列后第一个冒号(指令序列后第一个冒号 后的限定字符串),输入操作数后的限定字符串),输入操作数 限

42、定字符串(第一个冒号与第二个冒限定字符串(第一个冒号与第二个冒 号之间),还有第三号之间),还有第三 种类型的限定字符串在第二个冒号之后。同一种种类型的限定字符串在第二个冒号之后。同一种 类型的限类型的限 定字符串之间用逗号间隔。定字符串之间用逗号间隔。asm语句中出现的第一个限定字语句中出现的第一个限定字 符串用于描述占位符符串用于描述占位符“%0”,第二个用于描述占位符,第二个用于描述占位符“%1”, 以此类以此类 推(不管该限定字符串的类型)。如果指令序列中推(不管该限定字符串的类型)。如果指令序列中 没有任何输出操作没有任何输出操作 数,那么在语句中出现的第一个限定字数,那么在语句中出

43、现的第一个限定字 符串(该字符串用于描述输入符串(该字符串用于描述输入 操作数)之前应该有两个冒操作数)之前应该有两个冒 号(这样,编译器就知道指令中没有输出号(这样,编译器就知道指令中没有输出 操作数)。操作数)。 指令中的输出操作数对应的指令中的输出操作数对应的C语言变量应该具有左值类型,当语言变量应该具有左值类型,当 然对于然对于 输出操作数没有这种左值限制。输出操作数没有这种左值限制。 输出操作数必须是只输出操作数必须是只 写的,也就是说,写的,也就是说,asm对取出某个操作数,执行对取出某个操作数,执行 一定计算以后一定计算以后 再将结果存回该操作数这种类型的汇编指令的支持不是再将结

44、果存回该操作数这种类型的汇编指令的支持不是 直接的,直接的, 而必须通过特定的格式的说明。如果汇编指令中包含了一个而必须通过特定的格式的说明。如果汇编指令中包含了一个 输输 入入-输出类型的操作数,那么在模板中必须用两个占位符对该操输出类型的操作数,那么在模板中必须用两个占位符对该操 作作 数的不同功能进行引用:一个负责输入,另一个负责输出。数的不同功能进行引用:一个负责输入,另一个负责输出。 例如:例如: asm (addl %2,%0:=r(foo):0(foo),g(bar); 在上面在上面 这条指令中,这条指令中,“%0”是一个输入是一个输入-输出类型的操作数,输出类型的操作数, =r

45、(foo) 用于限定其输出功能,该指令的输出结果会存放用于限定其输出功能,该指令的输出结果会存放 到到C语言变量语言变量foo中;中; 指令中没有显式的出现指令中没有显式的出现“%1”操作数,操作数, 但是针对它有一个限定字符串但是针对它有一个限定字符串 0(foo),事实上指令中隐,事实上指令中隐 式的式的“%1”操作数用于描述操作数用于描述“%0”操作数操作数 的输入功能,它的输入功能,它 的限定字符串中的的限定字符串中的0限定了限定了“%1”操作数与操作数与“%0” 具有相具有相 同的地址。可以这样理解上述指令中的模板:该指令将同的地址。可以这样理解上述指令中的模板:该指令将 “%1”

46、和和“%2”中的值相加,计算结果存放回中的值相加,计算结果存放回“%0”中,中, 指令中的指令中的“%1”与与 “%0”具有相同的地址。注意,用于描具有相同的地址。注意,用于描 述述“%1”的的0限定字符足以限定字符足以 保证保证“%1”与与“%0”具有相同具有相同 的地址。但是,如果用下面的指令完成的地址。但是,如果用下面的指令完成 这种输入这种输入-输出操作输出操作 就不会正常工作:就不会正常工作: asm (“addl %2,%0”:“=r”(foo):“r”(foo),“g”(bar); 虽然虽然 该指令中该指令中“%0”和和“%1”同样引用了同样引用了C语言变量语言变量foo,但,但

47、 是是gcc并并 不保证在生成的汇编程序中它们具有相同的地址。不保证在生成的汇编程序中它们具有相同的地址。 还有一些汇编指令可能会改变某些寄存器的值,相应的汇还有一些汇编指令可能会改变某些寄存器的值,相应的汇 编指令模板编指令模板 中必须将这种情况通知编译器。所以在模板中必须将这种情况通知编译器。所以在模板 中还有第三种类型的限定中还有第三种类型的限定 字符串,它们跟在输入操作数字符串,它们跟在输入操作数 限定字符串的后面,之间用冒号间隔。限定字符串的后面,之间用冒号间隔。 这些字符串是某这些字符串是某 些寄存器的名称,代表该指令会改变这些寄存器中的些寄存器的名称,代表该指令会改变这些寄存器中

48、的 内内 容。容。 在内嵌的汇编指令中可能会直接引用某些硬件寄存器,我们在内嵌的汇编指令中可能会直接引用某些硬件寄存器,我们 已经知道已经知道 AT”或者或者“n”隔开。隔开。 操作数限定字符操作数限定字符 操作数限定字符串中利用规定的限定字操作数限定字符串中利用规定的限定字 符来描述相应的操作数,一些符来描述相应的操作数,一些 常用的限定字符有:(还有常用的限定字符有:(还有 一些没有涉及的限定字符,参见一些没有涉及的限定字符,参见) 1.“m”:操作数是内存变量。操作数是内存变量。 2.“o”:操作数是内存变量,但它的寻址方式必须是操作数是内存变量,但它的寻址方式必须是“偏

49、移偏移 量量”类型的,类型的, 也就是基址寻址或者基址加变址寻址。也就是基址寻址或者基址加变址寻址。 3.“V”:操作数是内存变量,其寻址方式非操作数是内存变量,其寻址方式非“偏移量偏移量”类型。类型。 4.“ ”:操作数是内存变量,其地址自动增量。操作数是内存变量,其地址自动增量。 5.“r”:操作数是通用寄存器。操作数是通用寄存器。 6.i:操作数是立即操作数。(其值可在汇编时确定)操作数是立即操作数。(其值可在汇编时确定) 7.。“n”:操作数是立即操作数。有些系统不支持除字操作数是立即操作数。有些系统不支持除字(双字节双字节) 以外的以外的 立即操作数,这些操作数要用立即操作数,这些操

50、作数要用“n”而不是而不是“i”来描述。来描述。 8 .“g”:操作数可以是立即数,内存变量或者寄存器,只要寄存操作数可以是立即数,内存变量或者寄存器,只要寄存 器属器属 于通用寄存器。于通用寄存器。 9.“X”:操作数允许是任何类型。操作数允许是任何类型。 10.“0”,“1”,.,“9”:操作数与某个指定的操作数匹配。也就是说,操作数与某个指定的操作数匹配。也就是说, 该操作数就是指定的那个操作数。例如,如果用该操作数就是指定的那个操作数。例如,如果用“0”来描述来描述 “%1”操作操作 数,那么数,那么“%1”引用的其实就是引用的其实就是“%0”操作数。操作数。 11.“p”:操作数是一

51、个合法的内存地址(指针)。操作数是一个合法的内存地址(指针)。 12.“=”:操作数在指令中是只写的(输出操作数)。操作数在指令中是只写的(输出操作数)。 13.“+”:操作数在指令中是读操作数在指令中是读-写类型的(输入写类型的(输入-输出操作数)。输出操作数)。 14“a”:寄存器寄存器EAX。 15.“b”:寄存器寄存器EBX。 16.“c”:寄存器寄存器ECX。 17.“d”:寄存器寄存器EDX。 18.“q”:寄存器寄存器“a”,“b”,“c”或者或者“d”。 19.“A”:寄存器寄存器“a”或者或者“d”。 20.“a”:寄存器寄存器EAX。 21.“f”:浮点数寄存器。浮点数寄存

52、器。 22.“t”:第一个浮点数寄存器。第一个浮点数寄存器。 23.“u”:第二个浮点数寄存器。第二个浮点数寄存器。 24.“D”:寄存器寄存器di。 25.“S”:寄存器寄存器si。 26.I:0-31之间的立即数。(用于之间的立即数。(用于32位的移位指令)位的移位指令) 27.“J”:0-63之间的立即数。(用于之间的立即数。(用于64位的移位指令)位的移位指令) 28.“N”:0-255之间的立即数。之间的立即数。(用于用于“out”指令)指令) 29.G:标准的标准的80387浮点常数。浮点常数。 一、汇编语言与高级语言的比较一、汇编语言与高级语言的比较 特特 征征汇编语言汇编语言

53、高级语言高级语言 目标程序运行时间目标程序运行时间短短长长 程序运行时占用的存储空间程序运行时占用的存储空间小小大大 直接访问硬件的能力直接访问硬件的能力强强弱弱 程序可读性和可维护性程序可读性和可维护性差差强强 程序开发的复杂程序程序开发的复杂程序高高低低 程序的可移植性程序的可移植性差差强强 对硬件环境和软件的兼容性对硬件环境和软件的兼容性差差强强 汇编语言与高级语言混合编程汇编语言与高级语言混合编程 二、嵌入式汇编二、嵌入式汇编 许多高级语言中包含了嵌入式汇编程序,允许在高级许多高级语言中包含了嵌入式汇编程序,允许在高级 语言源程序内插入用汇编写的个别语句序列。语言源程序内插入用汇编写的

54、个别语句序列。 1 Mcrosoft C 6.0 允许在允许在C语言程序中使用语言程序中使用_asm修饰符嵌修饰符嵌 入汇编语言语句,其格式为:入汇编语言语句,其格式为: 单行汇编格式:单行汇编格式: _asm 汇编语言指令汇编语言指令 这里,这里, _asm 指明本行内的后续部分为嵌入式汇编语言语句。指明本行内的后续部分为嵌入式汇编语言语句。 多行汇编格式:多行汇编格式: _asm 汇编语言语句序列汇编语言语句序列 这里,这里, _asm 指明其后大括号内的每一行都是嵌入式汇指明其后大括号内的每一行都是嵌入式汇 编语言语句。编语言语句。 2 Borland公司的公司的Turbo C提供了直接

55、插入汇编指令,提供了直接插入汇编指令, 其格式为:其格式为: asm 汇编指令;或换行符汇编指令;或换行符 例例1: if ( i0) _asm mov al, 4 else i=7; 三、库的使用三、库的使用 库文件可分为两种,一种是文本形式的汇编语言源程序库,库文件可分为两种,一种是文本形式的汇编语言源程序库, 另一种是二进制形式的目标文件库。另一种是二进制形式的目标文件库。 对库文件的引用使用以下伪指令:对库文件的引用使用以下伪指令: INCLUDE 库文件名;引用汇编语言源程序库库文件名;引用汇编语言源程序库 INCLUDELIB 库文件名;引用二进制形式的目标文件库库文件名;引用二进

56、制形式的目标文件库 四、汇编语言与四、汇编语言与C语言的接口语言的接口 在汇编语言与高级语言混合编程时,必须遵从一系列的接口约定。在汇编语言与高级语言混合编程时,必须遵从一系列的接口约定。 1、符号的定义和说明、符号的定义和说明 字母的大小写:对字母的大小写字母的大小写:对字母的大小写C语言敏感,而汇编语言不敏感。语言敏感,而汇编语言不敏感。 许多汇编程序的版本许多汇编程序的版本允许使用汇编开关通知汇编程序以大小写敏感允许使用汇编开关通知汇编程序以大小写敏感 的方式进行汇编。的方式进行汇编。 下划线下划线 C语言产生的目标文件自动为语言产生的目标文件自动为每个标识符加入下划线作为前缀每个标识符

57、加入下划线作为前缀,所,所 以汇编程序中引用以汇编程序中引用C语言的符号时,应在符号名前增加一个下划线;语言的符号时,应在符号名前增加一个下划线; 而在汇编中定义需要由而在汇编中定义需要由C语言引用的符号时,也要在符号名前加一个语言引用的符号时,也要在符号名前加一个 下划线。下划线。 例例2:在:在C语言中定义如下变量语言中定义如下变量 intnumber 则在汇编程序中需要用带有下划线的该变量:则在汇编程序中需要用带有下划线的该变量: mov _number,ax 2、全局符号的定义和说明、全局符号的定义和说明 C语言中符号的定义和说明:按语言中符号的定义和说明:按C语言版本的规定。语言版本

58、的规定。 汇编语言中符号的定义和说明汇编语言中符号的定义和说明 如果如果C语言中使用汇编中的变量或函数,语言中使用汇编中的变量或函数,则在汇编中使则在汇编中使 用用PUBLIC或或EXTERNDEF伪指令说明变量名或函数名具伪指令说明变量名或函数名具 有公共属性。有公共属性。 反之,在汇编中使用反之,在汇编中使用C中定义的变量或函数,应在汇编中中定义的变量或函数,应在汇编中 使用使用EXTERNDEF或或EXTERN伪指令说明。伪指令说明。 3、段和内存模式、段和内存模式 由于高级语言中段是由编译程序自动生成段结构,因此在由于高级语言中段是由编译程序自动生成段结构,因此在 混合编程中,应使汇编

59、程序遵从高级语言的段约定。混合编程中,应使汇编程序遵从高级语言的段约定。 4、寄存器的约定、寄存器的约定 在混合编程中,在混合编程中,CPU内部的寄存器也是公共资源。所以在内部的寄存器也是公共资源。所以在 互相调用时,要注意如何使用和保护互相调用时,要注意如何使用和保护CPU寄存器的问题。寄存器的问题。 5、参数的传递、参数的传递 C语言中执行函数调用时,函数的入口参数是通过堆栈传递的。语言中执行函数调用时,函数的入口参数是通过堆栈传递的。 参数类型:参数类型:C语言参数类型见下表:语言参数类型见下表: 原始类型原始类型入栈类型入栈类型在堆栈中占用的字节数在堆栈中占用的字节数 charint2

60、 unisigned charunisigned int2 short , intint2 unisigned short, unisigned unisigned int2 longlong4 unisigned long unisigned long4 floatfloat/double4/8 doubledouble8 near(指针)(指针)near(指针)(指针)2 far(指针)(指针)far(指针)(指针)4 array数组起始地址数组起始地址2或或4 struct按值入栈按值入栈 union按值入栈按值入栈 80 x86的堆栈是以字为的单位的,故的堆栈是以字为的单位的,故相应的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论