yale_OS(4)——Intel IA32架构CPU的汇编编程
来源:互联网 发布:千锋教育云计算 编辑:程序博客网 时间:2024/05/21 18:50
IA32架构CPU下的汇编编程学习
通常掌握某一特定机器的汇编编程技术需要一定的时间。然而,如果掌握其他处理器的汇编编程(例如ARM,MIPS等),那么学习IA32结构CPU下的汇编编程将会省去很多时间。以下对IA32架构下的汇编语言编程进行简单的总结,方便以后回头来温习
1.指令语法
关于IA32架构的CPU的汇编语言的语法和表示有两种约定:Intel和AT&T,大多数的文件使用Intel的约定,而GNU汇编器使用AT&T的约定,这两种约定之间主要的不同如下表所示:
例如:push $4
movl $0xd00a, %eax没有限制
例如:push 4
mov ebx d00ah寄存器前面需要加上“%”
例如:%eax 无限制
例如:eax参数顺序(例如:
把C变量“foo”加到
寄存器EAX中)source1, [source2,] dest
例如:addl $_foo, %eaxdes, source1 [,source2]
例如:add eax, _foo单操作数指明操作数的大小(用{b, w, l})
例如:movb foo, %al用寄存器名间接的指明操作数的大小,
byte ptr, word ptr 和dword ptr
例如:mov al, foo寻址一个C变量"foo"_foo[_foo]寻址寄存器指定的内存(例如,EAX)(%eax)[eax]寻址寄存器中值的变量偏移值_foo(%eax)[eax + _foo]寻址32-bit整数的数组"foo"中的值_foo(,%eax,4)[eax * 4 + foo]等效于C代码中的*(p +1)1(%eax)如果EAX中存储的是p的值,则[eax + 1]
2. 内存操作
IA32处理器使用分段内存架构,也就是说内存位置需要通过段选择子和偏移来确定。
- 段选择子指定包含操作数的段。
- 偏移(从段的开始位置到操作数所在位置的第一个byte的字节数)指定了操作数的线性地址或有效地址
3.经常使用的指令
下表中所示是一些经常使用的指令。
类别指令解释
数据转移
mov{l,w,b} source, dest
移动source到dest
xchg{l,w,b} dest1, dest2
交换
cmpxchg{l,w,b} dest1, dest2
比较和交换
push/pop{l,w}
入栈/出栈
movsb
移动DS:(E)SI 的bytes到地址ES:(E)DI处,典型的使用前缀rep
算术
add/sub{l,w,b} source, dest
加减
imul/mul{l,w,b} formats
有符号/无符号乘
idiv/div{l,w,b} dest
有符号/无符号除
inc/dec/neg{l,w,b} dest
增1/减1/取负
cmp{l,w,b} source1, source2
比较
逻辑
and/or/xor/not{l,w,b} source, dest
逻辑与/或/异或/非操作
sal/sar{l,w,b} formats
算术左移/右移
shl/shr{l,w,b} formats
逻辑左移/右移
控制转移
jmp address
无条件跳转
call address
保存EIP到堆栈中,然后跳转到address处
ret
返回到通过call调用保存的EIP处
leave
从堆栈中恢复EBP; pop off the stack frame
j{e,ne,l,le,g,ge} address
如果{=,!=,<,<=,>,>=},跳转到address处
loop address
对ECX 或 CX进行减1操作; 如果 = 0,则跳转
rep
重发串操作的前缀
int number
软件中断
iret
中断返回,EFLAGS 从堆栈中出栈
另外,用于长跳转的名字为ljmp,长调用为lcall
这里只列了一小部分的命令,IA32 Intel Architecture Software Developer's Manual, Volume 2的3.2节对IA32的所有指令进行了详细的描述,在Intel手册中使用的指令名字是按照Intel汇编语法约定的。
4.汇编指令
GNU汇编器指令名字以句点“.”开始,然后剩下的是小写字母,以下是常用的一些指令:
.ascii "string foo" 定义了ASCII串“string foo”
.asciz "string foo" 定义了ASCII串“string foo”,以0结尾
.string "string foo" 与.asicz "string foo"相同
.align 4 以双字边界来进行内存对齐
.byte 10, 13, 0 定义了三个字节
.word 0x0456, 0x1234 定义了两个字
.long 0x001234, 0x12345 定义了两个长字
.equ STACK_SEGMENT, 0X9000 设置符号STACK_SEGMENT的值为0x9000
.globl symbol 让“symbol”全局可见(对于定义全局标签和程序名字非常有用)
.code16 告诉汇编器插入适合的重写前缀以其运行在实模式下
5. 内联汇编(Inline Assembly)
通过gcc编译器生成的内联汇编代码的最基本的格式如下:
asm volatile("assembly-instruction");
assembly-instruction将会内联 到asm语句所在的地方,关键字volatile是可选的。该关键字告诉gcc编译器不要去优化该条指令。对于没有寄存器的内联汇编指令写起来非常的方便,例如:
asm volatile("cli");
用于禁止中断
asm volatile("sti");
用于开启中断
在C代码中编写内联汇编代码的通用格式如下:
asm [volatile] ("statements": output_regs: input_regs: used_regs);
其中statements是汇编指令, 如果有多于一条指令,可以使用“\n\t”来分开它们,让其看起来舒服点,
“input_regs”告诉gcc编译器哪个C变量移动到哪个寄存器,例如,想加载变量"foo"到寄存器EAX中,以及变量"bar"到寄存器ECX,可以些微:
:“a”(foo), "c"(bar)
gcc使用单个字母来表示所有的寄存器
单个字母 寄存器aeaxbebxcecxdedxSesiDediI常数值(0到31)q从EAX,EBX,ECX,EDX分配一个寄存器r从EAX,EBX,ECX,EDX,ESI,EDI中分配一个寄存器"output_regs"提供了输出寄存器,一种方便的方法是让gcc编译器来选择寄存器,只需要指定“=q”或“=r”来让gcc编译器选择寄存器,可以通过利用“%0”来指定第一个分配的寄存器,“%1”指定第二个寄存器,等等。在汇编指令中,如果在输入寄存器列表中指定了寄存器。可以只需要“0”或“1”,不要前缀“%”
“used_regs”列出了在汇编代码中使用的寄存器。
下面给出了一段内联汇编包含在C代码中的例子。
asm ("leal (%1,%1,4), %0" : "=r" (x_times_5) : "r" (x) );和 asm ("leal (%0,%0,4), %0" : "=r" (x) : "0" (x) );
6. 汇编程序结构和调用约定
最简单的学习汇编编程的方法是把简单的C程序编译为汇编源代码,该源代码将会显示常用的操作码,指令和寻址语法。这是一种有效的方法来学习汇编编程。
下面通过一个例如来说明汇编程序结构和调用约定,考虑如下的C程序hello.c
#include <stdio.h>static char buf[4096];int foo(int n){return n - 1;}int main(void){printf("Hello world\n");return f00(5);}
通过如下的命令对该C源代码进行编译
gcc -S hello.c
gcc编译器将会编译hello.c为汇编源代码hello.s,
以下是hello.s的源代码
.file"hello.c".localbuf.commbuf,4096,32.text.globl foo.typefoo, @functionfoo:.LFB0:.cfi_startprocpushq%rbp.cfi_def_cfa_offset 16movq%rsp, %rbp.cfi_offset 6, -16.cfi_def_cfa_register 6movl%edi, -4(%rbp)movl-4(%rbp), %eaxsubl$1, %eaxleaveret.cfi_endproc.LFE0:.sizefoo, .-foo.section.rodata.LC0:.string"Hello world".text.globl main.typemain, @functionmain:.LFB1:.cfi_startprocpushq%rbp.cfi_def_cfa_offset 16movq%rsp, %rbp.cfi_offset 6, -16.cfi_def_cfa_register 6movl$.LC0, %edicallputsmovl$5, %edimovl$0, %eaxcallf00leaveret.cfi_endproc.LFE1:.sizemain, .-main.ident"GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3".section.note.GNU-stack,"",@progbits
可以编写一些其他的简单C代码,然后编译为汇编代码来查看汇编源代码
来自于http://zoo.cs.yale.edu/classes/cs422/2011/ref/pc-arch#memory
- yale_OS(4)——Intel IA32架构CPU的汇编编程
- yale_OS(3)——Intel IA32架构
- 剖析Intel IA32架构下C语言及CPU浮点数机制
- 剖析Intel IA32架构下C语言及CPU浮点数机制
- 剖析Intel IA32架构下C语言及CPU浮点数机制
- Intel, AMD及VIA CPU的微架构(4)
- yale_OS(2)——OS-xv6的源代码的调试
- yale_OS(5)——BIOS提供的服务
- yale_OS(6)——xv6中boot loader的学习
- Intel系列CPU架构的发展史
- 科普:intel CPU 微架构的发展史
- 汇编指令基础(一)——以IA32学习
- 汇编指令基础(二)——以IA32学习
- CPU架构之intel itanium
- AMD和Intel的cpu架构的区别
- X86架构下Intel CPU的power management
- Intel, AMD及VIA CPU的微架构(1)
- Intel, AMD及VIA CPU的微架构目录
- 使用asp.net导出Excel
- Java JMenu_2
- 《第十五周任务二》分数计算器
- Linux wait函数解析
- __QPainter___操作
- yale_OS(4)——Intel IA32架构CPU的汇编编程
- Java编程中“为了性能”尽量要做到的一些地方
- C++总结 ----- 静态变量和静态函数
- exec函数族的使用
- Linux系统分区和挂载浅
- 易语言
- 第15周实验报告3
- UML语法 —— 交互与交互图
- linux mmap 详解