linux内核x86汇编小结
汇编在linux内核中比重不大,但是很难啃。一部分原因在于汇编指令,某些有段时间不看就忘记了。另外一部分原因是C中内联汇编比较难懂。这里做个小结,方便以后复习汇编知识。
x86汇编指令
关于x86指令,GNU使用的和INTEL文档里面的有些不同。一个区别是,对于双字节,GNU用l,INTEL用d。 比如INTEL文档里面的指令MOVSD,对应GNU的版本就是movsl。这里有GNU格式汇编的文档。
MOVSB/MOVSW/MOVSD/MOVSQ
MOVB指令将ESI中内存地址存放的内容(单字节)复制到EDI指向的内存区域,然后将ESI/EDI中的地址+1(DF标志位为0)或者-1(DF标志位为1)。
其余指令做类似的事情,除了复制的内容大小有区别
STOSB/STOSW/STOSD/STOSQ—Store String
STOSB指令将寄存器AL中的内容复制到EDI中的目的地址,然后将EDI中地址+1(DF标志位为0)或者-1(DF标志位为1)。
其余指令做类似的事情,除了复制的内容大小有区别
LODSB/LODSW/LODSD/LODSQ—Load String
LODSB指令将EDI指向的内容复制到AL,然后将ESI中地址+1(DF标志位为0)或者-1(DF标志位为1)。
其余指令做类似的事情,除了复制的内容大小和目的寄存器有区别
SCASB/SCASW/SCASD—Scan String
SCASB指令将寄存器中AL的内容和EDI中目的内存地址对应的内容比较,并设置相应的标志位。然后将EDI中地址+1(DF标志位为0)或者-1(DF标志位为1)。
其余指令做类似的事情,除了复制的内容大小有区别
REP/REPE/REPZ/REPNE/REPNZ
REP前缀主要配合这些指令,INS, OUTS, MOVS, LODS, STOS,其余的主要配合如CMPS, SCAS(REP本身不是一个独立指令)。
REP前缀的执行逻辑可以用如下伪代码描述
|
|
下面这个表格列举了REP族指令的结束条件
Repeat Prefix | Termination Condition 1 | Termination Condition 2 |
---|---|---|
REP | RCX or (E)CX = 0 | None |
REPE/REPZ | RCX or (E)CX = 0 | ZF = 0 |
REPNE/REPNZ | RCX or (E)CX = 0 | ZF = 1 |
LEA—Load Effective Address
LEA指令的主要作用是做地址计算。 GNU汇编中的寻址写法是,section:disp(base, index, scale),对应地址section:(base+index*scale+disp)。其中,base和index必须是通用寄存器,scale取值必须是1,2,4,8中之一。
下面一段内联汇编程序说明了LEA的用法,以及和MOV指令的区别。(gdb中可以通过命令disassembly/ni/info register xxx调试汇编)
|
|
LEA指令另外一个比较常见的用法是,做一些简单的计算。比如,
|
|
内联汇编
内联汇编的基本用法可以参考linux内核情景分析和这篇文章。 这篇howto则详细介绍了内联汇编。
内联汇编的格式如下
|
|
输入输出部里面的寄存器或者内存标示符参考如下表格(只列举了常见的)
标示符 | 含义 |
---|---|
a, b, c, d | eax, ebx, ecx, edx |
q | one in eax/ebx/ecx/edx |
S, D | esi, edi |
r | any register |
& | Make sure input/ouput in different register |
m | Read from memory |
i | An immediate integer |
几个例子
strlen
|
|
strnlen_user
|
|
__copy_user_zeroing
|
|
__do_strncpy_from_user
|
|
strchr
|
|
get_user
|
|