中山大学操作系统lab week 3

操作系统实验报告2

一、实验内容

验证实验 Blum’s Book: Sample programs in Chapter 06, 07 (Controlling Flow and Using Numbers)

二、实验环境

Ubuntu 18.04(64位)

三、实验过程

1、Sample programs in Chapter 06

第六章介绍了汇编语言如何通过跳转和循环,完成控制执行流程。要实现跳转和循环两个功能,需要对指令指针进行操作。当指令指针在程序指令中移动时,EIP寄存器会递增。程序不能直接修改指令指针,但是,可以利用能够改动指令指针值的分支指令(branch)来改动EIP寄存器的值。

分支指令分为无条件分支条件分支,它们能控制指令指针和程序逻辑的执行路径。

1)jumptest.s

无条件分支分为跳转、调用、中断。

跳转指令使用单一指令码:

 jmp location

location是要跳转到的内存地址。

在终端中输入命令行:

 #编译链接运行
 $ as --32 -o jumptest.o jumptest.s
 $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o jumptest -lc jumptest.o
 $ ./jumptest
 $ echo $?
 #使用反汇编器查看每条指令的内存位置
 $ objdump -D jumptest
 #使用调试器对代码进行汇编,查看EIP寄存器的值
 $ as --32 -gstabs -o jumptest.o jumptest.s
 $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o jumptest -lc jumptest.o
 $ gdb -q jumptest

输出结果:

2)calltest.s

调用指令:

 call address
 ​
 address:
     ret

调用指令分为两部分,第一部分为CALL指令,address操作数引用程序中的标签;调用指令的第二部分是返回指令,在函数中的助记符RET使函数可以返回代码的原始部分。

在终端中输入命令行:

 $ as --32 -o calltest.o calltest.s
 $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o calltest -lc calltest.o
 $ ./calltest

输出结果:

3)cmptest.s

条件跳转按照EFLAGS寄存器的当前值来确定是否进行跳转。

条件跳转指令:

 jxx address

其中xx是1个到3个字符的条件代码,address是程序要跳转到的位置。

比较指令:

 cmp operand1, operand2

当前者<=后者时,执行跳转,反之不进行。

在终端中输入命令行:

 $ 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寄存器的值不变。

修改文件,将15赋值给EAX寄存器和EBX寄存器,再次编译链接运行:

可见此时比较双方相等,跳转到greater标签,将20赋给了EBX寄存器。

4)paritytest.s

奇偶校验可以作为跳转条件的标志。奇偶校验标志表明数学运算答案中应该为1的位的数目。偶置1,奇置0。

在终端中输入命令行:

 $ 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的位数是奇数,不设置奇偶校验位,指令不跳转。

修改文件中SUBL指令,改为:subl $1, %ebx

输出结果:

生成的结果中为1的位数为偶数,奇偶校验位置1,指令跳转。

5)signtest.s

符号标志可以作为跳转条件的标志。符号标志用于表示寄存器中包含的值的符号改变,在带符号数中,最高位为符号位。

用as进行编译时会出现编译错误,将文件中的add $8, $esp改为add $8, %esp

即可解决。

在终端中输入命令行:

$ as --32 -o signtest.o signtest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o signtest -lc signtest.o
$ ./signtest

输出结果:

6)loop.s

循环是改变程序内指令路径的另外一种方式,循环可以使用单一循环函数编写重复性任务的代码。

循环指令:

loop address

address是要跳转到的程序代码位置的标签名称,由于循环指令只支持8位偏移量,所以只能进行短跳转。

在终端中输入命令行:

$ as --32 -o loop.o loop.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o loop -lc loop.o
$ ./loop

输出结果:

修改文件,将ECX寄存器初值设为0:movl $0, %ecx

由于在执行LOOP指令时,它首先把ECX中的值递减1,后检查其是否为0。因此,当ECX初值为0时,LOOP指令会递减使其成为-1,由于其非零,LOOP指令会一直执行,最终在寄存器溢出时退出,并显示错误的值。

7)betterloop.s

为纠正上述问题,在上一个loop程序的基础上,加入检查ECX寄存器包含零值时的特殊条件。

文件有误,按照书本原文程序修改后,在终端中输入命令行:

$ as --32 -o betterloop.o betterloop.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o betterloop -lc betterloop.o
$ ./betterloop

输出结果:

解决了ECX寄存器包含零的初值的问题。

8)ifthen.c

在终端中输入命令行:

$ gcc -S ifthen.c
$ cat ifthen.s

查看代码如何转换为汇编语言。

输出结果:

➜  chap06 gcc -S ifthen.c
➜  chap06 cat ifthen.s
        .file   "ifthen.c"
        .text
        .section        .rodata
.LC0:
        .string "The higher value is %d\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    $100, -8(%rbp)
        movl    $25, -4(%rbp)
        movl    -8(%rbp), %eax
        cmpl    -4(%rbp), %eax
        jle     .L2
        movl    -8(%rbp), %eax
        movl    %eax, %esi
        leaq    .LC0(%rip), %rdi
        movl    $0, %eax
        call    printf@PLT
        jmp     .L3
.L2:
        movl    -4(%rbp), %eax
        movl    %eax, %esi
        leaq    .LC0(%rip), %rdi
        movl    $0, %eax
        call    printf@PLT
.L3:
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
        .section        .note.GNU-stack,"",@progbits

9)for.c

在终端中输入命令行:

$ gcc -S for.c
$ cat for.s

查看代码如何转换为汇编语言。

输出结果:

➜  chap06 gcc -S for.c
➜  chap06 cat for.s
        .file   "for.c"
        .text
        .section        .rodata
.LC0:
        .string "The answer is %d\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    $0, -8(%rbp)
        movl    $0, -8(%rbp)
        jmp     .L2
.L3:
        movl    -8(%rbp), %edx
        movl    %edx, %eax
        sall    $2, %eax
        addl    %edx, %eax
        movl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax
        movl    %eax, %esi
        leaq    .LC0(%rip), %rdi
        movl    $0, %eax
        call    printf@PLT
        addl    $1, -8(%rbp)
.L2:
        cmpl    $999, -8(%rbp)
        jle     .L3
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
        .section        .note.GNU-stack,"",@progbits

2、Sample programs in Chapter 07

第七章介绍了汇编语言程序中可用的不同数字格式,并演示了如何使用它们。

1)inttest.s

该程序演示把带符号整数存储在寄存器中的3种不同方式,在该过程中,内存或者寄存器中表示的带符号整数经常是难以识别的。

在终端中输入命令行:

$ as --32 -gstabs -o inttest.o inttest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o inttest -lc inttest.o
$ gdb -q inttest

输出结果:

EDX寄存器中的值是错误的数字。

2)movzxtest.s

为解决上述问题,需要对无符号整数或有符号整数做扩展。MOVZX指令把长度小的无符号整数(可以在寄存器中,也可以在内存中)传送给长度大的无符号整数值(只能在寄存器中)。

MOVZX指令:

movzx source, destination

其中source可以是8位或16位的寄存器或内存位置,destination可以是16位或32位寄存器。

在终端中输入命令行:

$ as --32 -gstabs -o movzxtest.o movzxtest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o movzxtest -lc movzxtest.o
$ gdb -q movzxtest

输出结果:

3)movsxtest.s

MOVSX指令和MOVZX指令类似,但它允许扩展带符号整数并保留符号。

MOVSX指令:

movsx source, destination

其中source可以是8位或16位的寄存器或内存位置,destination可以是16位或32位寄存器。

在终端中输入命令行:

$ as --32 -gstabs -o movsxtest.o movsxtest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o movsxtest -lc movsxtest.o
$ gdb -q movsxtest

输出结果:

4)movsxtest2.s

在终端中输入命令行:

$ as --32 -gstabs -o movsxtest2.o movsxtest2.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o movsxtest2 -lc movsxtest2.o
$ gdb -q movsxtest2

输出结果:

5)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 -lc quadtest.o
$ gdb -q quadtest

输出结果:

6)mmxtest.s

MOVQ指令能将数据传送到MMX寄存器中。

MOVQ指令:

movq source, destination

source和destination可以是MMX寄存器、SSE寄存器或者64位的内存位置(但是不能在内存位置之间传送MMX整数)。

在终端中输入命令行:

$ as --32 -gstabs -o mmxtest.o mmxtest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o mmxtest -lc mmxtest.o
$ gdb -q mmxtest

输出结果:

7)ssetest.s

MOVDQA指令和MOVDQU指令用于把128位数据传送到XMM寄存器,或者在XMM寄存器之间传送数据。助记符的A和U部分代表对准和不对准。对于对准16个字节边界的数据,使用A(此时SSE指令执行得更快,并且,若对未对准的数据使用该指令,会造成硬件异常),反之使用U。

MOVDQA指令和MOVDQU指令:

movdqa source, destination
movdqu source, destination

source和destination可以是SSE 128位寄存器或者128位的内存位置(但是不能再在内存位置之间传送数据)。

在终端中输入命令行:

$ as --32 -gstabs -o ssetest.o ssetest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o ssetest -lc ssetest.o
$ gdb -q ssetest

输出结果:

8)bcdtest.s

FBLD指令用于把打包80位BCD值传送到FPU寄存器堆栈中。

FBLD指令:

fbld source

source是80位的内存位置。

该程序演示将BCD值加载到FPU寄存器中以及从FPU寄存器获取BCD值的基本操作。

在终端中输入命令行:

$ as --32 -gstabs -o bcdtest.o bcdtest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o bcdtest -lc bcdtest.o
$ gdb -q bcdtest

输出结果:

st0            1234     (raw 0x40099a40000000000000)

st0            2468     (raw 0x400a9a40000000000000)

9)floattest.s

FLD指令用于把浮点值传送出入FPU寄存器。

FLD指令:

fld source

source可以是32位,64位或者80位的内存位置。

该程序演示将BCD值加载到FPU寄存器中以及从FPU寄存器获取BCD值的基本操作。

在终端中输入命令行:

$ as --32 -gstabs -o floattest.o floattest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o floattest -lc floattest.o
$ gdb -q floattest

输出结果:

10)fpuvals.s

该程序演示如何使用预置的浮点值,简单地将各个浮点常量压入到FPU寄存器堆栈中。

在终端中输入命令行:

$ as --32 -gstabs -o fpuvals.o fpuvals.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o fpuvals -lc fpuvals.o
$ gdb -q fpuvals

输出结果:

11)ssefloat.s

该程序演示如何传送SSE打包单精度浮点值

在终端中输入命令行:

$ as --32 -gstabs -o ssefloat.o ssefloat.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o ssefloat -lc ssefloat.o
$ gdb -q ssefloat

输出结果:

12)sse2float.s

该程序演示如何传送SSE2打包双精度浮点值。

在终端中输入命令行:

$ as --32 -gstabs -o sse2float.o sse2float.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o sse2float -lc sse2float.o
$ gdb -q sse2float

输出结果:

13)convtest.s

该程序演示转换数据类型。

将第8行的 data: 删除后,在终端中输入命令行:

$ as --32 -gstabs -o convtest.o convtest.s
$ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o convtest -lc convtest.o
$ gdb -q convtest

输出结果:

四、实验总结

通过这次实验,我验证了书本中关于控制执行和数据类型的案例汇编程序,对相关汇编语言知识的了解更深入。同时,对linux环境和命令使用更加熟悉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值