8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
csapp第三章中,对汇编指令的要求比较高
这里把常见的指令整理一下1g++ -Og -S -masm=intel xxx.cpp
得到反汇编文件查阅
程序编码
代码示例和解释:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include
#include
using namespace std;
long mult2(long a, long b) {
return a*b;
}
void multstore(long x, long y, long* des) {
long t = mult2(x, y);
*des = t;
}
int () {
long a = 100, b = 150;
long dest;
multstore(a, b, &dest);
cout << dest << endl;
}
反汇编代码示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23__Z9multstorellPl: ## @_Z9multstorellPl
.cfi_startproc
## %bb.0: ## rdx第三个参数,rsi第2个参数,rdi第一个参数
pushrbp## rbp为基指针寄存器(base pointer),存取调用堆栈中的数据
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
movrbp, rsp## rsp为堆栈指针(stack pointer)寄存器,只可访问堆栈顶
.cfi_def_cfa_register rbp
pushrbx## rbx操作数寄存器,用来存放运算结果
pushrax## 用来临时存放函数mult2()的返回值,rax一般都是来存放返回值的
.cfi_offset rbx, -24
movrbx, rdx## rdx是第三个参数,copy dest to rbx, 最后*dest = mult2(x, y), 要先把dest存储在寄存器中,稍后访问
call__Z5mult2ll
movqword ptr [rbx], rax## 运行的结果,将mult2(x, y)存入[rbx]即*dest中
addrsp, 8
poprbx ## 可以这么理解: rbx = rax, rbx may be the left value, rax may be the right value, 一般来说, rax是返回值
poprbp## restore rbp
ret
.cfi_endproc
## -- End function
.globl_main ## -- Begin function main
.p2align4, 0x90
_main: ## @main
数据传输
寄存器
第一个参数:rdi, 第二个参数:rsi, 第三个参数:rdx, 第四个参数:rcx
汇编器常见的错误1
2movl %rax, %(rsp)## wrong, it is moveq %rax, %(rsp)
movl %eax, %rdx ## 原来应该是movzlq,但是并没有这样的指令,rdx应该对应movq
数据传送示例1
2
3
4
5
6
7char -> int
char类型4字,转int需要符号扩展,4字用movl,这里需要符号扩展
用movsbl
movsbl (%rdi), %eax
movl %eax, (%rsi)1
2
3
4
5
6char -> unsigned
同样4字对4字,需要符号扩展movsbl
movsbl (%rdi), %eax
movl %eax, (%rsi)1
2
3
4
5
6
7unsigned char -> long
0扩展,转成8字存储,操作的是unsigned char,本来用的是movl, 需要0扩展
用movzbl
movzbl (%rdi), %eax;
movq %rax, (%rsi)1
2
3
4
5
6int -> char
操作数是int, 直接用movl即可, int是4字,存放到eax中
movl (%rdi), %eax
movb %al, (%rsi)
char只需要低位的,所以用movb %al, (%rsi)1
2
3
4
5unsigned -> unsigned char
操作数unsigned,4字,movl存放在eax中
movl (%rdi), %eax
movb %al, (%rsi)1
2
3
4
5
6
7char -> short
需要做符号扩展,并且char要截断,取低位,存放在ax中,本来用movw
这里涉及到符号扩展,用movsbw
movsbw (%rdi), %ax
movw %ax, (%rsi)
移位操作
移位量是由%cl寄存器的低m位决定的,高位会被忽略1
2
3
4
5
6
7
8
9
10
11long shift_left4_rightn(long x, long n) {
x <<= 4;
x >>= n;
return x;
}
shift_left4_rightn:
movq %rdi, %rax
salq $4, %rax
movl %esi, %ecx ## get n (4 bytes)
sarq %cl, %rax ## 本来应该是sarq %ecx, %rax, 因为移位操作取的是低位%cl
特殊的算术操作
乘法,乘积低位放在%rax中,高位放在%rdx中
除法,需要用到%rdx寄存器来存放参数,商放在%rax中,余数放在%rdx中
控制
条件控制实现条件分支1setnle D
溢出的处理,用xor,看作是不进位的加法
如果有符号溢出,相应的值要变化,比如
SF: t < 0
溢出的话,要xor OF
实际上SF 0->1
注意跳转指令前面有符号,f8表示符号位是-1,所以是0xd-0x81
2
3
4
5
60xffffff73所代表的跳转值,首先最高位为负
值为8位16进制数
所以最高位为-16^7
-16^7 + 0xfffff73 = -141
即为跳转偏移量
跳转指令翻译1
2
3
4
5
6
7
8
9cmpq xxx1, xxx2
jge .L2
code....
means:
if(xxx2 < xxx1)
code...
else
.L2
用条件传送来实现条件分支1
2
3
4
5
6
7
8testq %rdi, %rdi
cmovns%rdi, %rax
## 实现方法:cmovns表示非负数
## if( >= 0) rax
## 条件中的值为testq %rdi的值,即
if(x >= 0) v = x;
用循环来实现条件分支
for循环1
2
3
4
5
6
7
8
9
10
11fun_a:
movl$0, %eax
jmp.L5
.L6:
xorq%rdi, %rax
shrq%rdi
.L5:
testq%rdi, %rdi
jne.L6
andl$1, %eax
ret
转换成c语言代码,可以发现L5是循环的主体1
2
3
4
5
6
7
8while(%rdi != 0) {
.L6
}
return %eax & 0x1
.L6的实现如下
val ^= x
x >>= 1
该实现如下:1
2
3
4
5
6
7
8long fun_a(unsigned long x) {
long val = 0;
while(x) {
val ^= x;
x >>= 1;
}
return val & 0x1;
}
代码功能:奇数个1, 每一位取出来xor,其值还是1
偶数个1,每一位取出来,其值是0
如果有奇数个1,返回1,偶数个1,返回01
2
3
4
5
6
7
8
9
10
11
12fun_b:
movl$64, %edx
movl$0, %eax
.L10:
movq$rdi, %rcx
andl$1, %ecx
addq%rax, %rax
orq%rcx, %rax
shrq%rdi
subq$1, %rdx
jne.L10
rep; ret
for循环主体1
2
3
4
5
6
7
8
9
10
11
12
13subq$1, %rdx
eax = val = 0;
for(edx = 64; edx != 0; edx--) {
.L10
}
.L10:
val in rax
tmp = x; tmp &= 1; (tmp in rcx)
val << 1; val |= tmp
x >> = 1
综上:1
2
3
4
5
6
7
8
9long fun_b(unsigned long x) {
long val = 0;
long i;
for(i = 64; i != 0; i--) {
val = (x & 0x1) | (val << 1)
x >>= 1;
}
return val;
}
这个代码的作用很有意思
如图所示,创造x的镜像
switch语句
1
2
3
4
5switch2:
addq$1, %rdi
cmpq$8, %rdi
ja.L2
jmp*.L4(, %rdi, 8)
start: rdi+1
$ x+1 = 0 $
$ x+1 > 8 quad (default) $
$ x+1 < 8 $
$ x = -1, 0, 1, 2, 3, 4, 5, 6, 7 $
函数调用过程
转移控制
函数调用的具体分析如下
stack上的局部存储
寄存器中的局部存储空间
数组的分配和访问
定长数组
异质的数据结构,联合,结构体
联合1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18typedef union {
struct {
long u;
short v;
char w;
} t1;
struct {
int a[2];
char* p;
} t2;
} u_type;
void get(u_type *up, type* dest) {
*dest = expr;
}
// up in %rdi, dest in %rsi
具体的内存分配如下:
数据对齐
数据对齐的时候,start位置必须是类型的整数倍
如果不满足,则会填充
这里又一个优化技巧
结构体对数据类型进行降序排列1
2
3
4
5
6
7
8struct P {
char* x1;
long x2;
float x3;
int x4;
short x5;
...
};
指针与缓冲区溢出
对抗缓冲区溢出攻击
支持变长栈帧1
2
3long vframe(long n, long idx, long *q) {
long *p[n];
}