Lab Week 03.
实验要求
验证实验 Blum’s Book: Sample programs in Chapter 06, 07(Controlling Flow and Using Numbers)
实验报告
相关内容的技术日志、遇到问题和解决方案。
实验内容
程序一:jumptest.s
无条件跳转汇编指令
jmp location
命令行
as --32 --gstabs -o jumptest.o jumptest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o jumptest jumptest.o
gdb -q jumptest
根据执行的行数,说明发生了跳转。
程序二:calltest.s
程序调用及返回后,返回的地址常存在%esp中(esp寄存器储存栈指针)。可以将其赋值到其他寄存器中,以便记录程序当前指令所在的内存的位置。
push指令为操作数压入内存的栈中,同时esp-=4,当对象确定是push不会有歧义。而pushl为压入一个long(双字),规定了长度。
pop与popl为出栈同理。
call为调用,即将当前指令压入栈,并指定下条指令。
ret为返回call之前的地址。
运行程序命令行
as --32 -o calltest.o calltest.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o calltest -lc calltest.o
./calltest
程序三:cmptest.s
cmp指令比较两个操作数的值,并根据比较结果设置机器状态字中的条件码。
条件转移指令
je (jump when equal)
jne (jump when not equal)
jz (jump when last result was zero)
jg (jump when greaterthan)
jge (jump when greater than or equal to)
jl (jump when less than)
jle
as --32 -o cmptest.o cmptest.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o cmptest -lc cmptest.o
./cmptest
echo $?
因为EBX寄存器的值小于EAX所以没有执行条件跳转指令,执行下一条,故查看的寄存器中的值为10。
程序四:paritytest.s
JPE指令,如果此时标志位PF=1,则跳转到指定的地址,如果PF=0,不跳转。
JP指令相反。
as --32 -o paritytest.o paritytest.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o paritytest -lc paritytest.o
./paritytest
echo $?
减法结果为1,JP指令不会发生跳转,结果代码为1。符合预期。
程序五:signtest.s
JNS 如果无符号则跳转。
as --32 -o signtest.o signtest.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o signtest -lc signtest.o
./signtest
有error,应将第15行从add $8, $esp
改为add $8, %esp
程序在反向遍历数组,使用EDI寄存器作为变址,JNS指令检查EDI寄存器为数组下标,若数组下标为负数,则停止遍历。
程序六:loop.s
loop指令用ECX寄存器作为计数器,一轮循环结束使其递减
LOOP :循环直到ECX寄存器为0
LOOPE/LOOPZ :循环直到ECX寄存器为0,或者没有设置ZF标志
LOOPNE/LOOPNZ:循环直到ECX寄存器为0,或者设置了ZP标志
as --32 -o loop.o loop.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o loop -lc loop.o
./loop
1加到100为5050,符合预期。
程序七:betterloop.s
若ECX初始值为负,则会死循环,出现寄存器溢出
JCXZ(Jump if CX register is zero)指令可用来规避上述错误
as --32 -o betterloop.o betterloop.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o betterloop -lc betterloop.o
./betterloop
加个换行并删去第11行才通过汇编
结果为0,直到ecx为0才结束程序。
程序八:ifthen.c
编译c语言程序,转化为汇编语言
gcc -S ifthen.c
cat ifthen.s
程序九:for.c
汇编循环结构的c语言程序
gcc -S for.c
cat for.s
可见语言越高级,程序效率越低。
程序十:inttest.s
将带符号整数存入寄存器的三种方法
as --32 --gstabs -o inttest.o inttest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o inttest inttest.o
gdb -q inttest
第一个movl为存入一个long(双字)。
第二个movw为存入一个word。
第三个movl使用标签存入寄存器。
可以看到对应寄存器成功存入值。
程序十一:movzxtest.s
movzx指令为把长度小的无符号整数值传给长度大的无符号整数
as --32 --gstabs -o movzxtest.o movzxtest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o movzxtest movzxtest.o
gdb -q movzxtest
可见相应寄存器的被成功赋值。
程序十二: movsxtest.s
movsx指令,允许扩展符号整数并且保留符号
as --32 --gstabs -o movsxtest.o movsxtest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o movsxtest movsxtest.o
gdb -q movsxtest
可见有符号数扩展后值不变,仍能保持符号不变。存在了对应寄存器里。
程序十三: movsxtest2.s
as --32 --gstabs -o movsxtest2.o movsxtest2.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o movsxtest2 movsxtest2.o
gdb -q movsxtest2
寄存器的值的变化符合预期。
程序十四: quadtest.s
quad指令定义若干个带符号整数值,为每一个值分配8位。
as --32 --gstabs -o quadtest.o quadtest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o quadtest quadtest.o
gdb -q quadtest
可见由于分配内存不够,data2数组只保留了低8位。
程序十五:mmxtest.s
MOVQ指令用于移动64位数据。
MMX为64位寄存器。
as --32 --gstabs -o mmxtest.o mmxtest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o mmxtest mmxtest.o
gdb -q mmxtest
数组values1为两个int,共64位,合成int64为-4294967295。
values2为8个byte,共64位,合成int64为0x100e44732ff0510,从低位到高位合成。values2[0]为最低位。
程序十六:ssetest.s
movdqa/movdqu用于传输128位数据,不能在两个内存地址间传输。
SSE是128位寄存器。
as --32 --gstabs -o ssetest.o ssetest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o ssetest ssetest.o
gdb -q ssetest
可见该指令成功将给定起始地址的128位转移到了对应的128位寄存器。
程序十七 bcdtest.s
fbld指令 tbyte ptr mem 装入mem的十字节BCD数 用于将BCD值加载到FPU寄存器堆栈中
fbstp tbyte ptr mem 保存十字节BCD数到mem并出栈。
fimul应该为浮点乘法指令。
as --32 --gstabs -o bcdtest.o bcdtest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o bcdtest bcdtest.o
gdb -q bcdtest
可见data1进栈后,栈顶为1234,乘以data2之后变成2468,pop之后变成0。
程序十八:floattest.s
fld指令用来把浮点值传送入和传送出FPU寄存器。
flds传输单精度
fldl传输双精度
fldb传输10字节浮点数。
as --32 --gstabs -o floattest.o floattest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o floattest floattest.o
gdb -q floattest
可见fld指令能把对于的浮点数存入st0寄存器。
程序十九:fpuvals.s
一些预置的浮点数的应用
FLD1 把+1.0压入FPU堆栈中
FLDL2T 压入 log_2 10
FLDL2E 压入 log_2 e
FLDPI 压入pi
FLDLG2 压入 log_10 2
FLDLN2 压入 ln_2
FLDZ 压入 ln_2
as --32 --gstabs -o fpuvals.o fpuvals.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o fpuvals fpuvals.o
gdb -q fpuvals
可见成功存入了各个预设的浮点数。
程序二十: ssefloat.s
有8个128位XMM寄存器,可以使用这些寄存器打包浮点数。
movups 用于把4个不对准单精度打包传入XMM或者内存。
as --32 --gstabs -o ssefloat.o ssefloat.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o ssefloat ssefloat.o
gdb -q ssefloat
可见起始地址后的128位打包成浮点数,存到相应的xmm寄存器。
程序二十一: sse2float.s
movupd 用于打包并传送2个双精度值。
as --32 --gstabs -o sse2float.o sse2float.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o sse2float sse2float.o
gdb -q sse2float
可见对应起始位置的连续128位被打包成浮点数存到相应位置。
程序二十二:convtest.s
各种打包转化的指令
cvtps2dq 打包单精度FP到双字整数
cvttps2dq 打包单精度FP到双字整数(截断)
cvtdq2ps 打包双字整数到打包单精度FP
as --32 --gstabs -o convtest.o convtest.s
ld -m elf_i386 -dynamic-linker/lib/ld-linux.so.2 -o convtest convtest.o
gdb -q convtest
可见相应的打包能合成新的浮点数并且能存入对应寄存器与内存。
实验总结
这次实验熟悉了很多X86汇编指令,有浮点数的处理和转化,系统栈st0
的进栈和出栈。
这次由于熟悉了Linux的汇编环境,除了源代码需要稍微修改才能过汇编以外,没有遇到很大的困难。