操作系统实验报告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, destinationsource和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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统实验报告1:ucore Lab
- 下一篇: 操作系统实验报告3:Linux 下 x8