汇编快速认识
作者:
| 更新日期:之前反汇编服务二进制,根据自己的汇编知识找到了问题, 今天记录一下汇编知识.
本文首发于公众号:天空的代码世界,微信号:tiankonguse
零、前言
先看一段汇编代码吧.
static inline uint32_t min_cmp_set(volatile int*l, int o, int n) {
unsigned char r;
__asm__ volatile (
"lock;"
"cmpxchgl %3, %1;"
"sete %0; "
: "=a" (r)
: "m" (*l), "a" (o), "r" (n)
: "cc", "memory");
return r;
}
还有下面使用性能工具和反汇编出来的代码.
mov 0x38(%rax),%rdi
mov $0x1,%esi
add $0xcf0,%rdi
callq 428
xor %eax,%eax
add $0x8,%rsp
retq
callq 434
mov 0x38(%rax),%rdi
mov $0x1,%esi
add $0xc78,%rdi
callq 449
or $0xffffffffffffffff,%eax
jmp 42a
看第一个代码, 完全不知道什么意思, 看第二个代码,大概能猜出几个意思,但是也不是很确定.
所以我们需要先了解一下汇编的基础知识, 然后就可以轻松的看上面的代码了.
一、寄存器
引用寄存器时在前加百分号,如movl %eax, %ebx
寄存器有这些:
- 8个32-bit寄存器 %eax,%ebx,%ecx,%edx,%edi,%esi,%ebp,%esp
- 8个16-bit寄存器:%ax,%bx,%cx,%dx,%di,%si,%bp,%sp(32-bit寄存器的低16位)
- 8个8-bit寄存器:%ah,%al,%bh,%bl,%ch,%cl,%dh,%dl。(16-bit寄存器的高8位和低8位)
- 6个段寄存器:%cs(code),%ds(data),%ss(stack), %es,%fs,%gs
- 3个控制寄存器:%cr0,%cr2,%cr3
- 6个debug寄存器:%db0,%db1,%db2,%db3,%db6,%db7
- 2个测试寄存器:%tr6,%tr7
- 8个浮点寄存器栈:%st(0),%st(1),%st(2),%st(3),%st(4),%st(5),%st(6),%st(7)
二、基础知识
- 命令操作顺序是从左到右的.
- 立即数就是一个数字.使用立即数,要在数前面加符号$
- 符号常数相当于给立即数定义了一个名字。
- 操作数的长度使用指令后的符号表示b(byte, 8-bit), w(word, 16-bits), l(long, 32-bits)
如果没有指定,编译器自己推断,推不出来了报错。 - 段内调用和跳转指令为
call
,ret
和jmp
段间调用和跳转指令为lcall
,lret
和ljmp
。 - 操作码前缀被用在下列的情况:
字符串重复操作指令(rep,repne)
指定被操作的段(cs,ds,ss,es,fs,gs)
进行总线加锁(lock) 指定地址和操作的大小(data16,addr16) - 内存引用的格式为:
section:[base+index*scale+displacement]
三、GCC Inline ASM
GCC 支持在C/C++代码中嵌入汇编代码,这些汇编代码被称作GCC Inline ASM——GCC内联汇编。
__asm__
用来声明一个汇编表达式。
__volatile__
是可选的,向GCC声明不能对汇编代码进行优化.
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);
__asm__ __volatile__(指令部:输出部:输入部:损坏部);
1. 解释
- 指令部: 数字加上前缀%,如%1、%0等,表示需要使用寄存器的样板操作数。
- 输出部: 规定对输出变量,即目标操作数如何结合的约束条件。
每个输出约束以“=”号开头,然后是一个字母表示对操作数类型的说明,然后是关于变量结合的约束。 - 输入部约束的格式和输出约束相似,但不带“=”号。
- 损坏部: 用于储存中间结果。
2. 约束条件
约束条件 含义如下:
- “m”、”v”和”o” 表示内存单元
- “r” 表示任何寄存器
- “q” 表示寄存器eax,ebx,ecx,edx之一
- “i”和”h” 表示直接操作数
- “E”和”F” 表示浮点数
- “g” 表示任意
- “a”,”b”, “c” “d” 分别表示要使用寄存器eax ebx ecx和edx
- “S”和”D” 分别表示要使用寄存器esi和edi
- “I” 表示常数(0至31)
3. 语法规则
后面的部分为空, 后面的冒号可以忽略, 前面的为空,前面的冒号不能忽略.
这个和函数默认参数一个道理,不然没法区分谁是谁了.
另外有冒号了就是C/C++格式的汇编, 没冒号就是内联汇编了. 对于内联汇编访问寄存器使用一个百分号,另一个使用两个.
四、汇编语法
这里只有常见的语法。
- MOV, PUSH, POP 赋值,入栈,出栈
- BSWAP(byte swap), XCHG, CMPXCHG,XADD(exchange and add), XLAT(translate) 交换
- ADD,INC, SUB,DEC, MUL, DIV, CMP 加减乘除,比较
- AND, OR, XOR, NOT, TEST,SHL,SHR,ROL,ROR 位操作
- MOVS(move string), CMPS, LODS(load string), STOS(store string), REP(repeat) 串操作
- JMP, CALL, RET(return), RETF(return far) 无条件跳转
- JG(jump when greater), JL, JZ(jump when has zero flag), JNG, JNL 条件跳转
- LOOP 循环控制
- INT(interrupt), INTO(overflow interrupt), IRET(interrupt return) 中断
- HLT(halt), WAIT, ESC(escape), LOCK, NOP(no operation), STI(set interrupt), CLI(clear interrupt) 其他
五、参考资料
好了, 有了这些基础只是我已经轻松看懂反汇编的所有代码了, 下面是对于的参考资料,ibm的资料果然很全。
六、其他文章
七、关于作者
曾是一名ACMer, 现在是鹅长视频部门的后台开发。
这里主要记录工作中的技术架构与经验,计算机相关的技术,数学、算法、生活上好玩的东西。
长按二维码关注作者, 了解作者发布的最新好玩的东西
长按图片关注公众号, 接受最新文章消息.
本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。