【硬件技术】x86架构与汇编语言
本文简要说明x86架构及x86汇编语言的使用。
80x86的历史
1978年Intel发布第一款16位微处理器8086(也称为iAPX86),之后又发布了80186、80286、80386、80486,因而该系列的CPU架构被称为x86。
- 8086是16位的处理器,80386(1985)是32位的
- 8086采用16位数据总线,20位地址总线(相当于1MB内存);80386采用32位数据总线,32位地址总线
- 8086只支持实模式,80386还支持保护模式
下面将介绍80386的一些基本汇编指令,注意这里采用nasm语法。
寄存器
现代的x86处理器(即80386之后)都有8个32位的通用寄存器(General Purpose Registers, GPR),
- 通用寄存器(32位)
- EAX:一般用作累加器(Accumulator)
- EBX:一般用作基址寄存器(Base)
- ECX:一般用来计数(Count)
- EDX:一般用来存放数据(Data)
- ESI:一般用作源变址(Source Index)
- EDI:一般用作目标变址(Destinatin Index)
- ESP:一般用作堆栈指针(Stack Pointer)
- EBP:一般用作基址指针(Base Pointer)
- 段寄存器(16位)
- CS:代码段寄存器
- DS:数据段寄存器
- SS:堆栈段寄存器
- ES、FS 及GS:附加数据段寄存器
- 程序计数器/指令计数器EIP,相对于CS
- 状态码EFLAGS
通常长的(32位)寄存器都可以被划分成小的读写块,如32位eax
,16位ax
,高8位ah
,低8位al
,注意nasm对标号大小写是敏感的,但对寄存器不会。
内存与地址模式
程序重定位
- 存放程序的为代码段,存放数据的为数据段
- 真实的内存单元地址称为物理地址/绝对内存地址,而程序中的地址为逻辑地址
由于程序并不知道自己会被加载到哪,因此访存如果用绝对地址将会出错,在执行程序时就需要程序重定位这个操作。
在汇编中通过org
指令实现,如org 0A100h
代表该程序中的所有标号都以0A100h
做偏移
内存分段机制
将内存分段,程序只需管偏移地址/相对地址就好了
段地址:偏移地址
程序重定位通过设置代码段CS寄存器和数据段DS寄存器实现
在8086中,地址总线是20位的,需要将段寄存器左移4位(0x10h
,相当于16进制左移1位)变为20位,然后再同偏移地址相加。
两种典型情况
- 因为段寄存器是16位的,在段不重叠的情况下,最多可以将1MB的内存分成65536个段,每个段16B,偏移地址从
0000H
到000FH
- 同样在不允许段之间重叠的情况下,因为偏移地址也是16位,1MB的内存最多只能划分成16个段,每段长64KB,段地址由
0000H
到F000H
段的划分是自由的,它可以起始于任何16字节对齐的位置,也可以是任意长度,只要不超过64KB。
正是由于段的划分非常自由,使得8086的内存访问也非常随意。
同一个物理地址,或者同一片内存区域,根据需要,可以随意指定一个段来访问它。
访问内存
声明静态数据需要添加数据大小,包括1Byte的db
,2B的dw
,4B的dd
,注意内存都是连续的,按字节(B)寻址
.DATAvar DB 64
var2 DB ? ; uninitialized byte
DB 10 ; no label, location is var2 + 1.
X DW ? ; 2B uninitialized
Y DD 30000;
str DB 'hello',0; 6 bytes starting at str
如果要访问某一大小的内存,则通过添加修饰词byte
、word
、dword
实现(注意nasm是不存储变量类型的),这里的字(word)通常就指16位,双字(double word)32位
mov byte[ebx], 2
函数调用(call)堆栈组织
Caller规则
- 在调用函数/子程序(subroutine)之前,先保存特定寄存器的状态(caller-saved)(包括
eax
、ecx
、edx
) - 将要传的参数堆栈(注意要逆序,最后一个参数最先入)。因为栈往下生长,因此第一个参数会被存在最低的地址
- 调用函数,
call
会将返回地址eip
压入栈中 - 返回时先把参数移出栈,然后将原来保存的寄存器再pop出来
Callee规则
- 将
ebp
推入栈,将esp
的值拷贝入ebp
push ebp
mov ebp, esp
- 分配局部变量,记住栈往下生长,如分配3个4B,则
sub esp, 12
- 保存寄存器状态
函数调用例子如下
.486.MODEL FLAT
.CODE
PUBLIC _myFunc
_myFunc PROC
; Subroutine Prologue
push ebp ; Save the old base pointer value.
mov ebp, esp ; Set the new base pointer value.
sub esp, 4 ; Make room for one 4-byte local variable.
push edi ; Save the values of registers that the function
push esi ; will modify. This function uses EDI and ESI.
; (no need to save EBX, EBP, or ESP)
; Subroutine Body
mov eax, [ebp+8] ; Move value of parameter 1 into EAX
mov esi, [ebp+12] ; Move value of parameter 2 into ESI
mov edi, [ebp+16] ; Move value of parameter 3 into EDI
mov [ebp-4], edi ; Move EDI into the local variable
add [ebp-4], esi ; Add ESI into the local variable
add eax, [ebp-4] ; Add the contents of the local variable
; into EAX (final result)
; Subroutine Epilogue
pop esi ; Recover register values
pop edi
mov esp, ebp ; Deallocate local variables
pop ebp ; Restore the caller's base pointer value
ret
_myFunc ENDP
END
常见指令
由于x86指令实在太多,这里只摘录一些常用的指令
- 通用数据传送
mov <ra>, <rb>
push <r>
;pusha
:把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈pop <r>
;popa
:把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈
- 算术逻辑运算
add <ra>, <rb>
、sub
:ra+=rbinc
、dec
cmp <ra>, <rb>
mul <r>
:等价于mul ax, <r>
and
、or
、xor
shl
、sal
、shr
、sar
:h为逻辑,a为算术
- 无条件转移
jmp <label>
call <label>
:过程调用ret
:过程返回
- 条件转移
je <label>
、jne
:上一算术逻辑运算结果jl
、jg
:小于大于
- 循环控制
loop
:cx不为0时循环
- 中断指令
cli
/sti
:关中断/开中断int <num>
:中断iret
:中断返回
通过标号加方括号访问内存,如mov ax, [mem]
还有一些比较常见的特殊指令:
leave
:等价于恢复堆栈,即mov esp,ebp\n\t pop ebp
nasm
The netwide assembler(NASM)是80x86和x86-64系列的汇编器
- 指令格式:
label: instruction operands ; comment
- 伪指令
message db 'hello, world'msglen equ ($-message)
zerobuf: times 64 db 0 ; executed n times
- 在nasm中支持多个变量的运算取址,如
mov eax,[ebx*2+ecx+offset]
- 字符串常量
db 'hello'
等价于db 'h','e','l','l','o',0
- 宏
%define
%macro prologue 1push ebp
mov ebp,esp
sub esp,%1
%endmacro
参考资料
- Assembly Language Tutorial (x86), http://www.hep.wisc.edu/~pinghc/x86AssmTutorial.htm
- x86 Assembly Guide, http://www.cs.virginia.edu/~evans/cs216/guides/x86.html#instructions
- NASM Tutorial, http://cs.lmu.edu/~ray/notes/nasmtutorial/
- 为什么我们还在使用X86 CPU?致敬8086处理器问世40周年 - 老狼的文章 - 知乎 https://zhuanlan.zhihu.com/p/38002889
- 80386的各种寄存器一览, https://www.cnblogs.com/alantu2018/p/8471955.html
- Question: Compare 8086, 80386 and Pentium, http://www.ques10.com/p/13578/compare-8086-80386-and-pentium-1/
- 李忠,《x86汇编语言–从实模式到保护模式》
- org指令的作用,https://blog.csdn.net/mirage1993/article/details/29908929
- Stack frame layout on x86-64, https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
以上是 【硬件技术】x86架构与汇编语言 的全部内容, 来源链接: utcz.com/a/127432.html