操作系统实验报告2:Linux 下 x86 汇编语言1
操作系统实验报告2
实验内容
- 了解 Linux 下 x86 汇编语言编程环境;
- 验证实验 Blum’s Book: Sample programs in Chapter 04, 05 (Moving Data)。
实验环境
- 架构:Intel x86_64 (虚拟机)
- 操作系统:Ubuntu 20.04
- 汇编器:gas (GNU Assembler) in AT&T mode
- 编译器:gcc
技术日志
Chapter 04
- 验证实验cpuid.s
程序的源代码略。
1.构建一般可执行程序:
执行程序命令:
as -o cpuid.o cpuid.s
ld -o cpuid cpuid.o
./cpuid
执行结果如下:
The processor Vendor ID is 'GenuineIntel'
执行截图:
2.使用编译器进行汇编:
将原程序代码中的:
.globl _start
_start:
改为:
.globl main
main:
安装32位的gcc库:
sudo apt-get install libc6-dev-i386
执行程序命令:
gcc cpuid.s -m32 -o cpuid
执行结果如下:
The processor Vendor ID is 'GenuineIntel'
执行截图:
3.使用gdb运行程序:
执行程序命令:
as -gstabs -o cpuid.o cpuid.s
ld -o cpuid cpuid.o
gdb cpuid
执行结果如下:
分析:
一开始在程序开始处设置断点,然后输入run
运行,输入命令next\n\step\s
可以看见单步调试程序,输入cont
程序直接运行完毕,输出
The processor Vendor ID is 'GenuineIntel'
重新输入run
,输入s
单步执行至cpuid语句,输入info registers
,可以看见所有寄存器中的值,再输入s
执行至下一语句,输入info registers
,可以看见寄存器中值的变化,可以看见,在执行cpuid语句前寄存器rbx,rcx,rdx的值都为0,执行cpuid后,它们包含从厂商ID字符串得来的值。
print/x $ebx
, print/x $edx
,print/x $ecx
分别以十六进制形式显示寄存器ebx,edx和ecx中的值,可以看到,寄存器ebx中的值为0x756e6547,寄存器edx中的值为0x49656e69,寄存器ecx中的值为0x6c65746e。
x/42cd &output
以字符变量的形式显示变量output的前42个字节
gdb基本指令总结:
break *_start:在程序开始处设置断点
break *end:在程序结束处设置断点
run:在gdb内运行启动程序(碰到断点便停止)
step/s/next/n:单步调试程序
cont:使程序继续运行
info registers:显示全部寄存器的值
print:显示某一寄存器或变量的值
print/d:显示十进制的值
print/t:显示二进制的值
print/x:显示十六进制的值
x/nyz:显示特定内存位置的值,n是要显示的字段数,y是输出格式,z是要显示字段的长度
- 验证实验cpuid2.s
在程序的源代码开头之前加上:
.code32
并安装程序运行所需32位库:
sudo apt-get update
sudo apt install lib32z1 lib32ncurses5 g++-multilib libc6-dev-i386
执行程序命令:
as --32 -o cpuid2.o cpuid2.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o cpuid2 -lc cpuid2.o
./cpuid2
执行结果如下:
The processor Vendor ID is 'GenuineIntel'
执行截图:
Chapter 05
定义数据元素
数据段:数据段是最常见的定义数据元素的位置。用于存储项目的特定内存位置,可以被程序的指令码引用,并且可以被随意读取和修改,在数据段中定义数据时,它必须被包含在可执行程序中,因为要用特定值初始化它。
bss段:在bss段定义数据元素无须声明特定的数据类型,不需要初始化,内存区域被保留在运行时使用,并且不必包含在最终的程序中。
- 验证实验sizetest1.s
程序的源代码略。
执行程序命令:
as -o sizetest1.o sizetest1.s
ld -o sizetest1 sizetest1.o
ls -al sizetest1
执行结果如下:
分析:可执行程序文件的总长度为4640字节
- 验证实验sizetest2.s
程序的源代码略。
执行程序命令:
as -o sizetest2.o sizetest2.s
ld -o sizetest2 sizetest2.o
ls -al sizetest2
执行结果如下:
分析:在bss段声明添加了10000字节的缓冲区后,可执行程序文件的总长度为4800字节,比原来只增加了160字节,说明在bss段声明数据不必包含在可执行程序中。
- 验证实验sizetest3.s
程序的源代码略。
执行程序命令:
as -o sizetest3.o sizetest3.s
ld -o sizetest3 sizetest3.o
ls -al sizetest3
执行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-goPlbdyE-1626174832894)(http://stugeek.gitee.io/operating-system/Labwork2-pictures/8.png)]
分析:用.fill
命令在数据段声明添加了10000字节的缓冲区后,可执行程序文件的总长度为18880字节,比原来增加了14240字节,.fill命令使汇编器自动地创建了10000个数据元素,使它比必要的长度大了很多,说明在数据段定义数据时,其必须被包含在可执行程序中。
传送数据元素
MOV指令基本格式:
movx source, destination
source和destination可以是内存地址,存储在内存中的数据值,指令语句中定义的数据值,或者是寄存器
- 验证实验movetest1.s
程序的源代码略。
执行程序命令:
as -gstabs -o movtest1.o movtest1.s
ld -o movtest1 movtest1.o
gdb -q movtest1
执行结果如下:
分析:可以看到,执行了movl value, %ecx
命令后,内存中存储的值1被传送到了ecx寄存器,ecx寄存器的值从原来的0变成了1,内存位置中的值被传送到了另一寄存器中
- 验证实验movetest2.s
程序的源代码略。
执行程序命令:
as -gstabs -o movtest2.o movtest2.s
ld -o movtest2 movtest2.o
gdb -q movtest2
执行结果如下:
分析:一开始查看value中的值,发现初始值为1,单步执行程序,一直到eax寄存器中的值被传送给了value内存中的位置后,再次查看value中的值,发现值为100,寄存器中的值被传送到了内存位置中
- 验证实验movetest3.s
程序的源代码略。
执行程序命令:
as --32 -o movtest3.o movtest3.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -lc -o movtest3 movtest3.o
./movtest3
执行结果如下:
分析:程序遍历了values标签指定的数据数组,用edi寄存器作为遍历数组用的变址,每个值显示后,edi寄存器的值被递增,依次从10每次增加5打印到60
- 验证实验movetest4.s
程序的源代码略。
执行程序命令:
as -gstabs -o movtest4.o movtest4.s
ld -o movtest4 movtest4.o
gdb -q movtest4
执行结果如下:
分析:程序开始时,首先查看values标签引用的内存位置中存储的值,前4个元素为10,15,20,25。
然后单步运行程序,发现第一个元素从values数组中加载到eax寄存器,即10,现在eax寄存器中的值为10。
继续单步执行,发现values标签引用的内存地址加载到了edi寄存器中,下一条指令又将100传送到了edi寄存器保存的地址之后4字节位置的内存地址,使用寄存器间接寻址,查看发现100保存到了values数组中的第二个元素的位置。
再下一条指令把数组的第二个元素加载到了ebx寄存器中,使用echo $?命令查看第二个数据数组元素的值,也是100。
条件传送指令
指令格式:
cmovx source, destination
其中x是一个或者两个字母的代码,表示将触发传送操作的条件,取决于EFLAGS寄存器的当前值。
- 验证实验cmovetest.s
程序的源代码略。
执行程序命令:
as --32 -gstabs -o cmovtest.o cmovtest.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -lc -o cmovtest cmovtest.o
./cmovtest
执行结果如下:
分析:寄存器ebx用来保存当前找到的最大整数,然后数组元素被逐个加载到寄存器eax中,并且和寄存器ebx中的值比较,如果寄存器eax中的值更大,就用寄存器eax中的值代替寄存器ebx中的值。
程序一开始,数组的第一个值被加载到寄存器ebx中。为105,第二个值被加载到寄存器eax中,为235,运行cmp
和cmova
指令,发现寄存器ebx寄存器中的值变成了更大的235,持续操作,直到数组的数全部被遍历完,最后寄存器ebx中的数就是数组中的数的最大值,为315。
数据交换指令
基本指令:
XCHG:在两个寄存器之间或者寄存器和内存位置之间交换它们的值
BSWAP:反转一个32位寄存器中的字节顺序
XADD:交换两个值并且把它们的总和存储在目标操作数中
CMPXCHG:把一个值和一个外部的 值进行比较,并且交换它和另一个的值
CMPXCHG8B:比较两个64位的值并且交换它们的值
- 验证实验swaptest.s
程序的源代码略。
执行程序命令:
as --gstabs -o swaptest.o swaptest.s
ld -o swaptest swaptest.o
gdb -q swaptest
执行结果如下:
分析:程序在第一条movl
指令后停止,查看寄存器ebx中的值,为0x12345678
,单步执行bswap
指令后,显示寄存器ebx中的值,为0x78563412
,和原始值尾数顺序相反。
- 验证实验cmpxchgtest.s
程序的源代码略。
执行程序命令:
as --gstabs -o cmpxchgtest.o cmpxchgtest.s
ld -o cmpxchgtest cmpxchgtest.o
gdb -q cmpxchgtest
执行结果如下:
分析:在执行cmpxchg
指令前,寄存器ebx中的值为5,data中的值为10,执行cmpxchg
指令后,data中的值变为5,寄存器ebx中的值被传送到data的内存位置
- 验证实验cmpxchg8btest.s
程序的源代码略。
执行程序命令:
as -gstabs -o cmpxchg8btest.o cmpxchg8btest.s
ld -o cmpxchg8btest cmpxchg8btest.o
gdb -q cmpxchg8btest
执行结果如下:
分析:cmpxchg8b data
使data引用一个内存位置,其中的8字节值会与寄存器edx和寄存器eax进行比较,如果目标值和edx:eax中包含的值匹配,就把位于ecx:ebx中的64位值传送给目标内存位置,如果不匹配,就把目标内存位置地址中的值加载到edx:eax寄存器对中,从输出可以看出,ecx:ebx中的值确实传送给了data目标内存位置
- 验证实验bubble.s
程序的源代码略。
执行程序命令:
as -gstabs -o bubble.o bubble.s
ld -o bubble bubble.o
gdb -q bubble
执行结果如下:
分析:程序为冒泡排序算法,程序运行前,values数组为乱序,程序运行完毕后,values数组为升序排序
压入数据和弹出数据
PUSH指令的简单格式:
pushx source
其中x表示数据元素的长度,source是要放入堆栈的数据元素
POP指令的格式:
popx destination
其中x表示数据元素的长度,destination是接收数据的位置
- 验证实验pushpop.s
程序的源代码略。
执行程序命令:
as --32 -gstabs -o pushpop.o pushpop.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -lc -o pushpop pushpop.o
gdb -q pushpop
执行结果如下:
分析:启动程序前,寄存器esp中的值为0xffffd0d0
,当执行完所有的push指令后,寄存器esp中的值为0xffffd0be
,开始的值和最后的值相差了18个字节,所有经过push指令的操作数据加起来总长度也是18字节,说明执行push操作时寄存器esp会递减,指向堆栈新的起始位置。
遇到问题
1.一开始当使用gcc运行cpuid.s时,会发生错误:
原因是gcc库是64位的,不能编译运行32位的程序
2.当按照课本上命令运行cpuid2.s,会发生错误:
原因是源代码是32位的,在64位的系统上会生成64位的程序,运行时会发生兼容性错误,导致程序无法运行。
解决方法:
1.需要安装32位的库:
sudo apt-get install libc6-dev-i386
执行程序命令改为:
gcc cpuid.s -m32 -o cpuid
执行结果如下:
The processor Vendor ID is 'GenuineIntel'
执行截图:
2.可以将文件从64位强行编译成32位的程序,然后再运行。
在程序的源代码开头之前加上:
.code32
并安装程序运行所需32位库:
sudo apt-get update
sudo apt install lib32z1 lib32ncurses5 g++-multilib libc6-dev-i386
执行程序命令改为:
as --32 -o cpuid2.o cpuid2.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o cpuid2 -lc cpuid2.o
./cpuid2
执行结果如下:
The processor Vendor ID is 'GenuineIntel'
执行截图:
操作系统实验报告2:Linux 下 x86 汇编语言1相关推荐
- 操作系统实验报告4:Linux 下 x86 汇编语言3
操作系统实验报告4 实验内容 验证实验 Blum's Book: Sample programs in Chapter 08, 10 (Basic Math Functions and Using S ...
- 操作系统实验报告3:Linux 下 x86 汇编语言2
操作系统实验报告3 实验内容 验证实验 Blum's Book: Sample programs in Chapter 06, 07 (Controlling Flow and Using Numbe ...
- linux应用程序的编写实验原理,操作系统实验 1.在linux下编写一个应用程序 联合开发网 - pudn.com...
操作系统实验 所属分类:Linux/Unix编程 开发工具:C/C++ 文件大小:1KB 下载次数:3 上传日期:2019-05-01 20:34:21 上 传 者:烟雨南风起 说明: 1.在lin ...
- 操作系统实验六:Linux下的C语言编程
实验六 Linux下的C语言编程 一.实验要求 (1)熟悉Linux环境下C语言应用程序开发的基本过程: (2)熟悉基本库函数的使用: (3)具有初步的应用程序设计能力. 二.实验内容 (1)有三个程 ...
- 操作系统实验报告linux进程管理,计算机操作系统实验报告三Linux进程基本管理.doc...
GDOU-B-11-112广东海洋大学学生实验报告书(学生用表) GDOU-B-11-112 实验名称 Linux进程基本管理 课程名称 计算机操作系统 课程号 学院(系) 专业 统 班级 学生姓名 ...
- linux系统实训总结报告,Linux操作系统实验报告.doc
Linux操作系统实验报告.doc LINUX 操作系统实验报告课 程 Linux 操作系统 专 业 学 号 姓 名 指导教师 XXXXX 系20 年 月 日实验一 LINUX 基本命令实验目的1.掌 ...
- 山东大学linux实验四CSDN,山东大学操作系统实验报告材料4进程同步实验
<山东大学操作系统实验报告材料4进程同步实验>由会员分享,可在线阅读,更多相关<山东大学操作系统实验报告材料4进程同步实验(15页珍藏版)>请在人人文库网上搜索. 1.实用标准 ...
- linux系统编程界面实验报告,操作系统实验报告-Linux操作使用编程.doc
操作系统实验报告-Linux操作使用编程 实 验 报 告( 2012/ 2013 学年 第二学期) 课程名称操 作 系 统A实验名称Linux操作.使用.编程实验时间2013年 5 月 6日指导单位计 ...
- 操作系统 实验报告 linux 内核,linux操作系统内核实验报告.doc
linux操作系统内核实验报告.doc linux操作系统内核实验报告 篇一:linux操作系统实验报告 LINUX操作系统实验报告 姓 名 班级学号 指导教师 2011 年 05月 16 日 实验一 ...
最新文章
- switch能使用的数据类型有6种
- ORB-SLAM(八)ORBmatcher 特征匹配
- 用开源组件构建属于你的 PHP 框架
- ZYNQ7000-AXI GPIO详解
- Vue绑定数据v-bind缩写:字段名 双向绑定v-model缩写:model 监听动作v-on缩写@ 记住:与数据相关用冒号 与动作相关用@
- SQL.H 通过此文件寻找sqlAPI编程的一种捷径
- 前端存储 (2) - sessionStorage ,localStorage
- 图像膨胀、腐蚀算法实现 python源码
- 如何学好游戏编程 一
- 小程序 房租水电费记录管理_一点通房租物业收据打印软件下载
- [FPGA基础应用]基于CPLD+ARM架构模拟PC104总线时序
- hdoj3085 / acwing177 Nightmare
- TOM邮箱容量满了收发不了邮件?你应该快速做到这些事
- JS简单总结(前端ES6和JQ)
- 从零开始学WEB前端——HTML实战练习
- 看到这类代码,别再说你不认识了!手把手带你认识初阶结构体(结构体类型的声明、初始化、成员访问与传参,全在这篇文章里)
- Linux里get命令,Linux apt-get 命令用法详解-Linux命令大全(手册)
- 工作6年多的码农ios职位面试总结(续)
- 工程效能部门如何讲好故事做好事
- 关于条件概率公式,乘法公式,全概率公式,贝叶斯概率公式
热门文章
- Oracle顶级认证OCM考试实战总结
- ASM_POWER_LIMIT 参数
- 《Do Neural Dialog Systems Use the Conversation History Effectively? An Empirical Study》
- 最新版安全狗打狗棒法
- How to hide index.php on nginx
- sql2012简体中文版安装
- 前端编码风格规范(3)—— JavaScript 规范
- 【C/C++】实型变量
- JUnit3 结合一个除法的单元测试说明Assert.fail()的用法
- [转]内存泄漏简单检测