LI@NG

Machine Prog.

IA32, IA64, x86 (a.k.a x86-64), AMD, ARM这些处理器的指令集向上为汇编语言提供了接口,可以把汇编语言看成高阶API,然后让硬件工程师去实现,该课程是按照x86-64指令集为标准做介绍的,所以由于历史遗留原因,64位的处理器的registers有很多奇怪的名字,这很大原因是因为很久以前比如386,8086等时期设计了一些“特殊意义”的registers,但是先进大多数已经不再单独规范它们的功能了,所以名字就留下来了,这点和我们编程一个道理,很久前的代码命的名字就不用改了嘛,为了兼容性的考虑。


汇编语言中的类型:

常量:用符号$开头,比如$0x15$15等。
寄存器:用符号 %开头,比如%rsp
内存地址:用括号()括起来,比如:(%rbx) 代表访问register %rbx中存在的地址


特殊的registers有:

%rbp -> base pointer,指向stack frame的底部地址(内存地址大端),不过该register可选,有的compiler生成的汇编语言,仅把rbp当做普通register用。
%rsp -> stack pointer,永远指向stack frame的最顶端地址 %rax -> 函数调用返回值存放位置

用作arguments的registers(根据参数的先后顺序,从第一个到第n个)分别为: %rdi, %rsi, %rdx, %rcx, %r8, %r9

caller save: caller will save the registers before calling callee. callee save: callee will save the registers before using registers.

用作caller save的register: %rax, %rdx, %rcx

用作callee save的register:
%rdi, %rsi, %rbx


汇编语言一些操作对应例子(AT&T格式):

补充一个,test a b是指做AND运算,但是结果不会产生任何影响,比如:

汇编中的位移操作:

汇编语言中一些跳转例子:

例子:

cmp $0x15213, %r12
jge 0x123

instruction will jump to 0x123 when r12 >= 0x15213


数据在register和内存间的移动

Register中的数据可以被移动到指定内存中,同时也可以将内容移动到另外的register中,其实register可以看成是一个变量,而”指定内存“可以看成一个指针变量,比如:(提醒,这里的movq,q是(quadword四字)的简称,是64位中,一次性移动64位,4个字节的位置)

int a = b;    movq %rdi %rax   // 这里b是%rdi, a是%rax;
int *b;
b = a;        movq %rdi (%rax) // 将register %rdi中的内容放入内存中,内存地址这里用括号()括起来,表示register %rax是一个内存地址

还可以常量直接移动到register中,也可以直接放在内存中:

int a = 0x1;  movq 0x1 %rdi    // 常量直接复制给register
int *p;
*p = 0x1;     movq 0x1 (%rax)

也可以将内存地址中的内容(如果想拿到内存地址的话要用leaq,后面会说到)放入register中:

int *a;
int b;

b = *a;       movq (%rax)  %rdi;

内存中的内容也可以从地址1变到内存中的地址2,但是因为硬件设计的原因,这一操作没法直接进行,所以就只能先从内存移动到register,然后再从register移动到内存中:

int *a;
int *b;

int c;

c = *a;     movq (%rax) %rdi
*b = c;     movq %rdi   (%rsi)

因为是64位的,所以移动默认是4bytes,如果要对读取的地址进行移位,可以通过:比如:8(%rdx)就是从%rdx中存的内存地址+8,当然这只是移位的一种表示方法,下面会介绍更多。

比如:%rdx = 0xf000,%rcx = 0x0100;

如下表达式的对应关系分别是:

0x8(%rdx)  -> 0xf000 + 0x8 = 0xf008

(%rdx, %rcx) -> 0xf000 + 0x0100 = 0xf100;

(%rdx, %rcx, 4) -> 0xf00 + 0x0100 * 4 = 0xf400;

//就是最后一个比如这个2和上面的4,乘以倒数第二个,加上最前面的,不用管逗号
0x80( , %rdx, 2) -> 0xf000*2 + 0x80;

//如果没有scale index (s),那么前面就直接相加
0x80(%rdx, %rcx) -> %rcx + %rdx + 0x80

stack & procedure

stack位于内存中的高地址端,stack的加栈是从高地址端像低地址端加的,也就是说,stack的底部内存的地址要大于顶部的内存地址,为局部变量分配空间的一般方式比如:%rsp-0x4,是要减去一定的offset。

当汇编代码中有如callq 0x123<func_name>代码是,代码即将调用一个函数,在这个时候,在内存中会有一个新的stack frame出现,同时一般有如下行为:

stack里面看起来是:

arguments part
ret address
%rbp // 可选
...
%rsp

GDB 常用命令

gdb ./binary