CSAPP第三章练习

寄存器

3.0.1

搬运指令

3.0.2

练习题 3.1

3.1

假设下面的值存放在指明的内存地址和寄存器中

地址
0x100 0xFF
0x104 0xAB
0x108 0x13
0x10C 0x11
寄存器
%rax 0x100
%rcx 0x1
%rdx 0x3

填写下表,给出所示操作数的值:

操作数 注释
%rax 0x100 寄存器
0x104 0xAB 绝对地址
$0x108 0x108 立即数
(%rax) 0xFF 地址0x100
4(%rax) 0xAB 地址0x104
9(%rax, %rdx) 0x11 地址0x10C
260(%rcx, %rdx) 0x13 地址0x108
0xFC(, %rcx, 4) 0xFF 地址0x100
(%rax, %rdx, 4) 0x11 地址0x10C

练习题 3.2

3.2

指令 源操作数 目标操作数
mov l %eax (%rsp)
mov w (%rax) %dx
mov b $0xFF %bl
mov b (%rsp, %rdx, 4) %dl
mov q (%rdx) %rax
mov w %dx (%rax)

练习题 3.3

3.3

答:

  1. 立即数要先写入寄存器, 再写入内存(? 使用%ebx作为64位系统地址寄存器, 在栈上是危险的
  2. movl指令大小错误, 应该用movq, rax是绝对四字寄存器 指令后缀和寄存器ID之间不匹配
  3. mov指令源操作数和目的操作数同为内存。
  4. 不存在名为sl的寄存器
  5. 立即数不能作为目的操作数
  6. 源操作数和目的操作数字不匹配
  7. ~~不懂 ~~ 指令后缀和寄存器ID之间不匹配

练习题 3.4 (我不李姐)

3.4.1

3.4.2

答:

src_t in %rdi dest_t in rsi

src_t dest_t 指令 注释
long long movq(%rdi), %rax
movq %rax, (%rsi)
读8个字节
存8个字节
char int movsbl (%rdi), %eax
movq %eax, (%rsi)
讲char转成int
存4个字节
char unsigned movsbl (%rdi), %eax
movq %eax, (%rsi)
将char转成int
存4个字节
unsigned char long movzbl (%rdi), %eax
movq %rax, (%rsi)
读一个字节并零扩展
存8个字节
int char movl (%rdi), %eax
movb %al, (%rsi)
读4个字节
存低位字节
unsigned unsigned char movl (%rdi), %eax
movb %al, (%rsi)
读4个字节
存低位字节
char short movsbw (%rdi), %ax
movw %aw, (%rsi)
读一个字节并符号扩展
存2个字节

练习题 3.5

3.5

答:

1
2
3
4
5
6
7
8
void decode1 (long *xp, long *yp, long *zp) {
long x = *xp;
long y = *yp;
long z = *xp;
*yp = x;
*zp = y;
*xp = z;
}

算术和逻辑指令

3.5.1

3.5.2

3.5.3


练习题 3.6

3.6

3.6.1

3.6.2

答:

表达式 结果
leaq 6(%ax), %rdx 6 + (x & 0xFFFF)
leaq (%rax, %rcx), %rdx x + y
leaq (%rax, %rcx, 4), %rdx x + y * 4
leaq 7(%rax, %rax, 8), %rdx 8 + x * 5 7 + 9x
leaq 0xA (, %rcx, 4), %rdx 0xA + (y * 4) 10 + 4y
leaq 9(%rax, %rcx, 2), %rdx 9 + (x + y * 2)

练习题 3.7

3.7

1
2
3
4
long scale2(long x, long y, long z) {
long t = 5 * x + y * 2 + 8 * z;
return t;
}

练习题 3.8

3.8

答:

指令 目的
addq %rcx, (%rax) 0x100 0x100
subq %rdx, 8(rax) 0x108 0xA8
imulq $16, (%rax, %rdx, 8) 0x118 0x176 0x110
incq 16(%rax) 0x100 0x100 0x14
decq %rcx %rcx 0x0
subq %rdx, %rax %rax 0xFD

练习题3.9

3.9

1
2
sal	4 %rax 
sarl %ecx %rax

参考答案:

1
2
salq $4, %rax
sarq %cl, %rax

练习题3.10

3.10

答:

1
2
3
4
5
6
7
long arith2(long x, long y, long z) {
long t1 = x | y;
long t2 = t1 >> 3;
long t3 = ~t2;
long t4 = z - t3;
return t4;
}

练习题 3.11

3.11

答:

A: 寄存器%rdx值置零

B: subq %rdx %rdx 或者 movq $0 %rdx

C:

特殊的运算操作

3.5.3

练习题 3.12

3.12

答:

1
2
3
4
5
6
7
8
remdiv:
movq %rdx, %r8
movq %rdi, %rax
movq $0, %rdx
idivq %rsi
movq %rax, (%r8)
movq %rdx, (%rcx)
ret

条件码

3.6.1

比较和测试指令

3.5.2

set 指令

3.6.2

练习题 3.13

3.13

答:

1
2
3
4
5
6
7
8
9
10
11
A:  
int < int

B:
short >= short

C:
unsigned char <= unsigned char

D:
long != long\unsigned long

练习题 3.14

3.14

答:

1
2
3
4
5
6
7
8
9
10
11
A:  
long >=

B:
short\unsigned short ==

C:
unsigned char >

D:
int\unsigned int !=

jump 指令

练习题 3.15

3.15

1
2
3
4
A:4003fc + 0x2 = 4003fe
B:400431 + 0xf4(-12) = 400425
C:400543 \ 400545
D:4005ed + 0xffffff73(-141) = 400560

练习题 3.16

3.16

答:

1
2
3
4
5
6
7
8
9
10
11
12
A:
void cond(long a, long *p) {
if (p == 0)
goto done;
if (*p >= a) {
goto done;
}
*p = a;
done;
}
B:汇编一次只能执行一条指令, 两条比较指令包含两个分支

练习题 3.17

3.17

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
long gotodiff_se(long x, long y) {
long result;
if (x < y) {
goto x_lt_y
}
ge_cnt++;
result = x - y;
return result;
x_lt_y:
lt_cnt++;
result = y - x;
return resultl;

}

3.17.B

练习题 3.18

3.18

com s1, s2 // s2 - s1

1
2
3
4
5
6
7
8
9
10
11
long test (long x, long y, long z) {
long val = x + y + z;
if (x < -3) {
if (y < z)
val = x * y;
else
val = y * z;
} else if (x > 2)
val = x * z;
return val;
}

练习题 3.19

3.19

1
2
3
//不懂	
A: T = 2 * (31 - 16) = 30
B: 16 + 30 = 47

条件传送指令

练习题 3.20

3.20

1
2
3
4
5
6
7
8
9
10
A: x / 8, 如果x是负数, 必须先加上偏置数7
B:
//long arith(long x):
//x in %rdi
arith:
leaq 7(%rdi), %rax // temp = 7 + x
testq %rdi, %rdi // x & x
cmovns %rdi, %rax // 如果 %rdi > 0, %rax = %rdi
sarq $3, %rax // %rax >> 3;
ret

练习题 3.21

3.21

1
2
3
4
5
6
7
8
9
10
11
12
13
long test(long x, long y, long z) {
long val = 8 * x;
if (y > 0) {
if (x < y)
val = y - x;
else
val = x + y;

} else if (y <= -2){
val = x + y;
}
return val;
}

练习题 3.22

3.22

1
2
A:
B:

练习题 3.23

3.23

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
A.
x : %rax
y : %rcx
n : %rdx
B.
答;编译器认为指针p总是指向x,因此表达式(*p) ++就能实现x+1。通过第7行的leap指令, 把这个加一和加y组合起来。
C.
// long dw_loop(long x)
// x initially in %rdi
dw_loop:
movq %rdi, %rax //%rax = x
movq %rdi, %rcx //%rcx = x
imulq %rdi, %rcx //y = x * x
leaq (%rdi, %rdi) //n = x + x
.L2: //loop
leaq 1(%rcx, %rax), %rax //x += y + 1;
subq $1, %rdx //n-=1
testq %rdx, %rdx //Test n
jg .L2 //if n > 0, goto loop;
rep;ret //return %rax;

练习题 3.25

3.25

1
2
3
4
5
6
7
8
long loop_while2(long a, long b) {
long result = b;
while (b > 0) {
result = result * a;
b = b - a;
}
return result;
}

3.26

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
A; 方法:跳转到中间 (jump to middle)
B;
long fun_a (unsigned long x) {
long val = 0;
while (x) {
val ^= x;
x >>= 1;
}
return val & 0x1;
}
C;
不懂
参考答案:
这个代码计算参数x的奇偶性。 也就是, 如果x中有奇数个1,就返回1, 否则返回0

练习题 3.27

3.27

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
long fact_for(long n) {
long i;
long result = 1;
for (i = 2; i <= n; i ++)
result *= i;
return result;
}
A;
while;
long fact_for (long n) {
long i = 2;
long result = 1;
while (i <= n)
result *= i++;
return result;
}
guarded_do 变换;
long fact_for_gd_goto(long n) {
long i = 2;
long result = 1;
if (n <= 1)
goto done;
loop:
result *= i;
i ++;
if (i <= n)
goto loop;
done:
return result;
}

练习题 3.28

3.28

1
2
3
4
5
6
7
8
9
10
11
12
13
A;
long fun_b (unsigned long x) {
long val = 0;
long i;
for (i = 64; i != 0; i --) {
val = (val << 1) | (0x1 & x);
x >>= 1;
}
return val;
}
B; 这段代码是从guarded_do变换生成的。编译器发现i初始化成64,所以一定满足不等于0,因此初始测试是没必要的。
C;
翻转x的二进制位

练习题 3.29

3.29

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
A: for翻译到while循环;
long sum = 0;
long i = 0;
while (i < 10) {
if(i & 1)
continue;
sum += i;
i ++;
}
continue时, 会跳过iupdate部分代码, 导致死循环;
long sum = 0;
long i = 0;
while ( i < 10 ) {
if (1 & 1)
goto update;
sum += 1;
update:
i ++;
}

练习题 3.30

3.30

1
2
A; -1, 0, 1, 2, 3, 5, 7;
B; 2, 3;

练习题 3.31 (我不李姐)

3.31

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void switcher(long a, long b, long c, long *dest) {
long val;
swtich(a) {
case 5:
c = b ^ 15;
case 0:
val = c + 112;
break;
case 2:
case 7:
val = (b + c) << 2;
break;
case 4:
val = a;
default:
val = b;
}
*dest = val;
}

练习题 3.32

3.32

标号 PC 指令 %rdi %rsi %rax %rsp * %rsp 描述
M1 0x400560 callq 10 - - 0x7fffffffe820 - 调用first(10)
F1 0x400548 lea 10 11 - y=x+1
F2 0x40054c sub 9 11 - x -= 1
F3 0x400550 callq 9 11 调用last
L1 0x400540 mov 9 11 9 %rax = u
L2 0x400543 imul 9 11 99 %rax *= v
L3 0x400547 retq 9 11 99 从last返回 99
F4 0x400555 repz retq 9 11 99 从first返回99
M2 0x400565 mov 9 11 99 继续执行想··