20145236《信息安全系统设计基础》第6周学习总结
教材内容学习
一、Y86指令集体系结构
定义一个指令集体系结构,包括定义各种状态元素、指令集和它们的编码、一组编程规范和异常事件处理。
二、程序员可见状态
Y86处理器状态类似于I32。可以访问和修改程序寄存器、条件码、程序计数器和存储器,状态码指明程序是否运行正常。
RF:程序寄存器 %eax,%ecx,%edx,%ebx,%esi,%edi,%esp(出栈、入栈、调用和返回指令作为栈指针),%ebp
CC:条件码 ZF、SF、OF(都是一位条件码,用来保存最近的算术或逻辑指令所造成影响的有关信息。)
PC:程序计数器 存放当前正在执行的指令
DMEM:存储器 很大的字节数组,保存着程序和数据。Y86程序用虚拟地址来引用存储器位置。
Stat:程序状态码 它表明程序执行的总体状态。它会指示是正常运行还是出现了某种异常。
三、Y86指令
IA32指令集的一个子集,只包括四字节整数操作。寻址方式比较少,操作也比较少。
IA32的movl指令分成了4个不同的指令:irmovl、rrmovl、mrmovl和rmmovl。分别显示地指明源和目的的格式。
源操作数: 立即数i、寄存器r、存储器m
目的操作数: 寄存器r、存储器m
两个存储器传送指令中的存储器引用方式是简单的基址和偏移量形式。
在地址计算中,不支持第二变址寄存器和任何寄存器值的伸缩。
不允许从一个存储器地址直接传送到另一个存储器地址。也不允许将立即数传送到存储器。
4个整数操作指令
addl、subl、andl、xorl
7个跳转指令(jXX)
jmp、jle、jl、je、jne、jge、jg
有6个条件传送指令(cmovXX)
cmovle、cmovl、cmove、cmovne、cmovge、cmovg
只有当条件码满足所需要的约束时,才会更新目的寄存器的值。
call指令将返回地址入栈,然后跳到目的地址。ret指令从这样的过程调用中返回。
pushl和popl指令实现了入栈和出栈。
halt指令停止指令的执行。对于Y86来说,执行halt指令会导致处理器停止,并将状态码设置为HLT。
四、指令编码
每条指令的第一个字节表明指令的类型。这个字节分为两个部分,每部分4位:高4位是代码部分,低4位是功能部分。功能值只有在一组相关指令共用一个代码时才有用。
整数操作里代码部分均为6,功能部分区分addl,subl,andl,xorl
分支指令里代码部分均为7
传送指令里代码部分均为2
8个程序寄存器中每个都有相应的0~7的寄存器标识符。
程序寄存器存在一个寄存器文件中,这个寄存器文件就是一个小的、以寄存器ID作为地址的随机访问存储器。当需要指明不应访问任何寄存器时,用ID值0xF表示。
没有寄存器操作数(分支指令和call指令),就没有寄存器指示符字节。
只需要一个寄存器操作数的指令(irmovl、pushl、popl),将另一个寄存器指示符设为0xF。
一个附加的4字节常数字,可作为:irmovl的立即数数据,rmmovl和mrmovl的地址指示符偏移量,分支指令和调用指令的目的地址。
注意:
分支指令和调用指令的目的地址是一个相对地址,而不是相对寻址方式。
所有整数采用小端法编码。当指令按反汇编格式书写时这些字节就以相反的顺序出现。
五、stat代码可能取值反应了机器的不同状态——
- AOK:正常操作(除此之外的任何状态都会使得处理器停止执行指令)
- HLT:处理器执行halt指令
- ADR:遇到非法地址2015/10/14 17:16:25
- INS:遇到非法指令
六、逻辑设计和硬件控制语言HCL
要实现一个数字系统需要三个主要的组成部分:
计算对位进行操作的函数的组合逻辑
存储位的存储器元素
控制存储器元素更新的时钟信号
七、逻辑门
AND &&
OR ||
NOT !
逻辑门只对单个位的数进行操作,而不是整个字。
八、组合电路和HCL布尔表达式
储器来存储程序数据。
处理器还包括另外一个只读存储器,用来读指令。
在大多数实际系统中,这两个存储器被合并为一个具有双端口的存储器:一个用来读指令,一个用来读或写数据。
九、Y86的顺序实现
SEQ 顺序处理器
每个时钟周期上,SEQ执行一条完整指令所需的所有步骤
六个基本阶段
- 取指 - 译码 - 执行 - 访存 - 写回 - 更新PC
十、SEQ
SEQ抽象视图
程序计数器放在寄存器中
信息沿线流动
各个阶段相关的硬件单元负责执行这些处理
反馈线路包括要写到存储器文件的更新值,以及更新的程序计数器值
在SEQ中,所有硬件单元的处理都在一个时钟周期内完成
画图惯例
浅灰色方块表示硬件单元
控制逻辑块是用灰色圆角矩形表示的
线路的名字在白色椭圆中说明
宽度为字长的数据连接用中等粗度的线表示
宽度为字节或更窄的数据连接用细线表示
单个位的连接用虚线
十一、SEQ的时序
SEQ的实现包括组合逻辑和两种存储器设备
时钟寄存器 程序计数器和条件码寄存器
随机访问存储器 寄存器文件、指令存储器和数据存储器
组合逻辑不需要任何时序或控制
由于指令存储器只用来读指令,我们可以将这个单元看成是组合逻辑
剩下四个(程序计数器、条件码寄存器、数据存储器和寄存器文件)需要对他们的时序进行明确的控制。
条件码寄存器 只在执行整数运算指令时装载 数据存储器 只在执行rmmovl、pushl或call时写入 寄存器文件 两个写端口允许每个时钟周期更新两个程序寄存器。(特殊寄存器ID 0xF表明此端口不应执行写操作)1. 组织计算原则
处理器从来不需要为了完成一条指令的执行而去读由该指令更新了的状态。
用时钟来控制状态元素的更新,值通过组合逻辑传播。
2. 取指阶段
以PC为第一个字节的地址,一次读6个字节。
icode 控制逻辑块计算指令
ifun 功能码
3. 三个一位的信号(根据icode值计算)
instr_valid 发现不合法的指令
need_regids 包含寄存器指示符字节吗
need_valC 包括常数字吗
后五个字节是寄存器指示符字节和常数字的组合编码。
4. 译码和写回阶段
都需要访问寄存器文件,根据四个端口的情况,判断应该读哪个寄存器产生信号valA、valB。
寄存器文件,支持同时进行两个读和两个写,每个端口有一个地址连接(寄存器ID)和一个数据连接(32根线路),既可以作为寄存器文件的输出字,又可以作为他的输入字。
5. 执行阶段
包括算数/逻辑单元(ALU),输出为valE信号。
ALU通常作为加法器使用
包括条件码寄存器
每次运行产生:
符号、溢出、产生信号set_cc
6. 访存阶段
读或者写程序数据。
两个数据块产生存储器地址和存储器输入证据的值,两个产生控制信号表明应该是读还是写。当执行读操作时,数据存储器产生valM。
根据icode,imem_error,instr_valid,dmem_error,从指令执行的结果计算状态码Stat。
7. 更新PC阶段
产生程序计数器的新值,依据指令的类型和是否要选择分支,新的PC可能是valC、valM或者valP。
课后习题解答
4.1
确定下面的Y86指令序列的字节编码。.pos 0x100表明这段代码的起始地址应该是0x100
irmovl $15,%ebx
rrmovl %ebx,%ecx loop: rmmovl %ecx,-3(%ebx) addl %ebx,%ecx jmp loop
答:
irmovl $15,%ebx ##30f30f000000
rrmovl %ebx,%ecx ##2031 loop: rmmovl %ecx,-3(%ebx) ##4013fdffff addl %ebx,%ecx ##6031 jmp loop
4.2
确定下列每一个字节序列所编码的Y86指令序列。如果有不合法字节,指出其位置。
A.
0x100:30f3fcffffff ##irmovl $-4,%ebx
0x106:406300080000 ##rmmovl %esi,0x800(%ebx) 0x10c:00 ##halt
B.
0x200:a06f ##pushl %esi
0x202:8008020000 ##call proc 0x207:00 ##halt 0x208: 0x208:30f30a000000 ##irmovl $10,%ebx 0x20e:90 ##ret 【此题应该是在熟练掌握各项指令(尤其是特殊指令)的编码基础上进行尝试分解编码序列。例如:在以“a0”开头的编码处,就应该意识到这是一个之后2字节的pushl指令;然后下一个以“80”开头的即call指令(只有五字节),这样就可以得出剩余的00一定是halt。】
C.
0x300:505407000000 ##mrmovl 0x7(%esp),%ebp
0x306:10 ##nop 0x307:f0 ##非法 0x308:b01f ##popl %ecx
4.5
修改P238图4-6中的Y86代码,使其能计算一个数组的绝对值的和。内循环中使用条件传送。
源代码:
int Sum(int Start,int Count) { int sum = 0; while(Count) { sum+=Start;
Start++; Count--; } return sum; } 【对于内循环的Y86代码,如要计算绝对值之和,只需要在相对于“sum+=*Start”的部分做下改动,加上条件分支(负数取反,正数不变)。如果要用条件传送,则适合cmovg或者cmovge】
loop:
mrmovl (%ecx),%esi irmovl $0,%edi subl %esi,%edi cmovg %edi,%esi ##以上代码处理了x<0的情况,下面即和原代码相同 addl %esi,%eax irmovl $4,%ebx addl %ebx,%ecx irmovl $-1,%ebx addl %ebx,%edx jne loop
4.8
写出信号xor的HCL表达式。输入为a和b
bool xor = (a && !b) || (!a && b)
与bool eq = (!a && !b) || (a && b)的区别为,二者互补
4.10
写一个电路的HCL代码,对于输入字A,B,C,选择中间值。
int Med3 = [
A<=B && A>=C :A; A<=C && A>=B :A; B<=A && B>=C :B; B<=C && B>=A :B; 1:C;];
4.16
根据图4-17中目标代码第9行call处理情况填写具体的处理阶段。
- 取指
icode:ifun<-M1[0x023]=8:0
valC<-M4[0x024] = 0x29
valP<-0x028
- 译码
valB<-R[%esp]=128
- 执行
valE<-128-4=124
- 写回
R[%esp]<-124
- 更新PC
PC<-0x029
效果是将%esp设置为124,将0x028(返回地址)存放到该存储器地址
4.17
写出SEQ中实现need_valC的HCL代码
bool need_valC = icode in {IIROMVL,IRMMOVL,IMRMOVL,IJXX,ICALL};
4.19
寄存器IDdestM表明写端口M的目的寄存器,从存储器中读出来的值valM将放在那里。写出destM的HCL代码。
int dstM = [
icode in {IMRMOVL,IPOPL} :rA; 1:RNONE;]
实验楼实验
一、要求:下载Y86模拟器,验证教材上不少于三组HCL代码;构建YIS环境(操作性的代码见下);YIS手册下载;YIS测试
cd ~/Code/shiyanlou_cs413
wget
tar -xvf sim.tar
cd sim
sudo apt-get install tk
sudo ln -s /usr/lib/x86_64-linux-gnu/libtk8.6.so /usr/lib/libtk.so
sudo ln -s /usr/lib/x86_64-linux-gnu/libtk8.6.so /usr/lib/libtk.so
make
二、构建YIS环境的过程如下(实验楼环境下)
三、汇编所有代码结果
进入测试代码 cd y86-code
进行汇编 make asuml.yo
汇编所有代码结果 make all
四、测试书中P239代码asuml.ys,得到的代码如下:
| /* $begin code-yso */ | /* $begin code-ysa */ | # Execution begins at address 0 | # This version uses a leave instruction 0x000: | .pos 0 0x000: 30f400040000 | init: irmovl Stack, %esp # Set up Stack pointer 0x006: 30f500040000 | irmovl Stack, %ebp # Set up base pointer 0x00c: 7024000000 | jmp Main # Execute main program | | # Array of 4 elements 0x014: | .align 4 0x014: 0d000000 | array: .long 0xd 0x018: c0000000 | .long 0xc0 0x01c: 000b0000 | .long 0xb00 0x020: 00a00000 | .long 0xa000 | 0x024: 30f004000000 | Main: irmovl $4,%eax 0x02a: a00f | pushl %eax # Push 4 0x02c: 30f214000000 | irmovl array,%edx 0x032: a02f | pushl %edx # Push array 0x034: 803a000000 | call rSum # Sum(array, 4) 0x039: 00 | halt | | # int Sum(int *Start, int Count) 0x03a: a05f | rSum: pushl %ebp 0x03c: 2045 | rrmovl %esp,%ebp 0x03e: 30f014000000 | irmovl $20,%eax 0x044: 6104 | subl %eax,%esp 0x046: a03f | pushl %ebx 0x048: 503508000000 | mrmovl 8(%ebp),%ebx 0x04e: 50050c000000 | mrmovl 12(%ebp),%eax 0x054: 6200 | andl %eax,%eax 0x056: 718b000000 | jle L38 0x05b: 30f2f8ffffff | irmovl $-8,%edx 0x061: 6024 | addl %edx,%esp 0x063: 30f2ffffffff | irmovl $-1,%edx 0x069: 6020 | addl %edx,%eax 0x06b: a00f | pushl %eax 0x06d: 30f204000000 | irmovl $4,%edx 0x073: 2030 | rrmovl %ebx,%eax 0x075: 6020 | addl %edx,%eax 0x077: a00f | pushl %eax 0x079: 803a000000 | call rSum 0x07e: 502300000000 | mrmovl (%ebx),%edx 0x084: 6020 | addl %edx,%eax 0x086: 708d000000 | jmp L39 0x08b: 6300 | L38: xorl %eax,%eax 0x08d: 5035e8ffffff | L39: mrmovl -24(%ebp),%ebx 0x093: d0 | leave 0x094: 90 | ret | 0x400: | .pos 0x400 0x400: | Stack: # The stack goes here | /* $end code-ysa */ | /* $end code-yso */