CSAPP:程序的机器级表示-3

本文最后更新于:2022年9月4日 下午

思考两个问题:

  • 控制结构相对应的机器语言的控制结构(汇编语言)
  • C语言实现的分支、循环、开关等操作,在底层的机器语言是如何实现的?

条件码的隐式赋值

几种标志寄存器

  • CF 进位标志位(Carry Flag, 无符号数)
  • SF 符号标志识位(Sign Flag, 有符号数)
  • ZF 零标志位 ( Zero Flag )
  • OF 溢出标志位( Overflow Flag , 有符号数)

add指令

addq Src,Dest 隐含了:t = a+b

  • CF=1
    • 进位
    • 当两数相加产生进位/两数相减产生进位的情况下CF=1
    • 127+127、1-2
    • 把两个数当做无符号数,相加结果是否有进位?若有进位,则CF=1
  • ZF=1
    • 运算结果为0
  • SF=1
    • 将t看做是有符号数,t<0的情况下,SF=1
    • 运算结果可以看做是有符号数,也可以看做是无符号数,因此具有两种含义
    • 假设运算结果为10000001,可以看做是无符号数+129,也可以看做是有符号数-1
  • PF
    • 奇偶标志位,如果运算结果的二进制表示中1的个数为偶数,PF=1,否则为0
  • OF=1
    • 有符号数(补码)溢出的情况
    • 把两个数当做有符号数,相加结果是否有溢出?
    • (a>0 && b>0 && t<0) || (a<0 && b<0 && t>=0)

cmp

cmpq Src1, Src2会计算Src2-Src1的值并改写条件码,但是不会改变这两个操作数

  • 对标志寄存器值的判断依据与上面add指令相似
  • t=Src2-Src1

跳转指令

C语言到汇编语言的转换

if else 语句

1
2
3
4
5
6
7
8
9
long absdiff (long x, long y)
{
long result;
if (x > y)
result = x-y;
else
result = y-x;
return result;
}

转换为如下goto语句:

1
2
3
4
5
6
7
8
9
10
11
12
long absdiff_j(long x, long y)
{
long result;
int ntest = x <= y;
if (ntest) goto Else;
result = x-y;
goto Done;
Else:
result = y-x;
Done:
return result;
}

转换为如下汇编:

1
2
3
4
5
6
7
8
9
10
absdiff:
cmpq %rsi, %rdi # x:y
jle .L4
movq %rdi, %rax
subq %rsi, %rax
ret
.L4: # x <= y
movq %rsi, %rax
subq %rdi, %rax
ret

对于三目运算符

1
val = Test ? Then_Expr : Else_Expr;

也可表示为ifelse语句,从而改写为汇编语句

循环结构

1
2
3
4
5
6
7
8
9
long pcount_do(unsigned long x) 
{
long result = 0;
do {
result += x & 0x1;
x >>= 1;
} while (x);
return result;
}
  • 计算x的二进制表示中有多少个1

转化为goto语句:

1
2
3
4
5
6
7
8
9
long pcount_goto (unsigned long x)
{
long result = 0;
loop:
result += x & 0x1;
x >>= 1;
if(x) goto loop;
return result;
}

转换为如下汇编:

1
2
3
4
5
6
7
8
 movl  $0, %eax	
.L2:
movq %rdi, %rdx 存储result
andl $1, %edx 按位与
addq %rdx, %rax rax是返回值
shrq %rdi 右移一位
jne .L2 跳转
rep; ret

for循环转化为while的通用版本:

1
2
3
4
5
6
7
8
9
for (Init; Test; Update )
Body
---

Init;
while (Test ) {
Body
Update;
}

do-while循环与while循环相似。两者唯一的分别:do-while循环将先会执行一次循环内的代码,再去判断循环条件。所以无论循环条件是否满足,do-while循环内的代码至少会执行一次。因此,do-while循环属于后测循环(post-test loop)。

do-while循环转化为goto的通用版本:

1
2
3
4
5
6
7
8
9
10
do 
Body
while (Test);

---

loop:
Body
if (Test)
goto loop

switch结构

1
2
3
4
5
6
7
8
9
switch(x) {
case val_0:
Block 0
case val_1:
Block 1
• • •
case val_n-1:
Block n–1
}
1
2
3
4
5
6
7
8
9
10
.section	.rodata
.align 8
.L4:
.quad .L8 # x = 0
.quad .L3 # x = 1
.quad .L5 # x = 2
.quad .L9 # x = 3
.quad .L8 # x = 4
.quad .L7 # x = 5
.quad .L7 # x = 6
  • jmp到跳转表
  • 跳转表是一个数组,存储了不同case入口处的地址
  • 由于每个存储地址的指针是8个字节,因此比例因子为8
  • 基址为.L4,跳转的x存储在%rdi,则需要跳转到.L4 + x*8处,也即是jmp *.L4(,%rdi,8)

CSAPP:程序的机器级表示-3
http://gls.show/p/a9307f29/
作者
郭佳明
发布于
2022年9月4日
许可协议