操作系统实验报告3:Linux 下 x86 汇编语言2
操作系統(tǒng)實(shí)驗(yàn)報(bào)告3
實(shí)驗(yàn)內(nèi)容
- 驗(yàn)證實(shí)驗(yàn) Blum’s Book: Sample programs in Chapter 06, 07 (Controlling Flow and Using Numbers)
實(shí)驗(yàn)環(huán)境
- 架構(gòu):Intel x86_64 (虛擬機(jī))
- 操作系統(tǒng):Ubuntu 20.04
- 匯編器:gas (GNU Assembler) in AT&T mode
- 編譯器:gcc
技術(shù)日志
Chapter 06
跳轉(zhuǎn)指令
跳轉(zhuǎn)指令使用單一指令碼:
jmp location其中l(wèi)ocation是要跳轉(zhuǎn)到的內(nèi)存地址
- 驗(yàn)證實(shí)驗(yàn)jumptest.s
1.構(gòu)建一般可執(zhí)行程序:
程序的源代碼略。
執(zhí)行程序命令:
as --32 -o jumptest.o jumptest.s ld -m elf_i386 -o jumptest jumptest.o ./jumptest echo $?執(zhí)行截圖:
分析:程序先把寄存器eax賦值為1,然后使用跳轉(zhuǎn)指令跳過把寄存器ebx賦值為10,跳轉(zhuǎn)到了把寄存器ebx賦值為20的語句,可以看到跳轉(zhuǎn)確實(shí)發(fā)生了。
2.使用objdump程序進(jìn)行反匯編:
執(zhí)行程序命令:
as --32 -gstabs -o jumptest.o jumptest.s ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o jumptest jumptest.o objdump -D jumptest執(zhí)行截圖:
分析:程序開始時(shí)使用的第一個(gè)內(nèi)存位置是0x8049001,overhere標(biāo)簽指向的內(nèi)存位置是0x8048083。
3.使用gdb運(yùn)行程序:
執(zhí)行程序命令:
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執(zhí)行結(jié)果如下:
分析:
在程序的開始位置設(shè)置斷點(diǎn),并運(yùn)行程序,查看使用的第一個(gè)內(nèi)存位置,顯示在寄存器eip中,這個(gè)值是0x8049001,它和objdump輸出中顯示的相同內(nèi)存位置相對(duì)應(yīng),單步調(diào)試至執(zhí)行了跳轉(zhuǎn)指令,再次顯示寄存器eip中的值,這個(gè)值是0x8048083,在objdump輸出中顯示,這是overhere標(biāo)簽指向的位置,說明實(shí)現(xiàn)跳轉(zhuǎn)。
- 驗(yàn)證實(shí)驗(yàn)calltest.s
執(zhí)行程序命令:
as --32 -o calltest.o calltest.s ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o calltest -lc calltest.o ./calltest執(zhí)行結(jié)果如下:
This is section 1 This is section 2 This is section 3執(zhí)行截圖:
分析:在程序的開始,使用prinif顯示第一個(gè)文本行,顯示程序處于什么位位置。下一步, 使用call指令把控制轉(zhuǎn)移到overhere標(biāo)簽。在overhere標(biāo)簽,寄存器esp的值被復(fù)制給指針ebp,以便在函數(shù)的結(jié)尾可以恢復(fù)它.再次使用prinf函數(shù)顯示第二行文本,然后恢復(fù)esp和ebp寄存器。程序的控制返回到緊跟在call指令后面的指令,并且再次使用printf函數(shù)顯示第三個(gè)文本行。
比較指令
CMP指令的格式如下:
cmp operand1, operand2CMP指令把第二個(gè)操作數(shù)和第一個(gè)操作數(shù)進(jìn)行比較。在幕后,它對(duì)兩個(gè)操作數(shù)執(zhí)行減法操作(operand2-operand1),比較指令不會(huì)修改這兩個(gè)操作數(shù),但是如果發(fā)生減法操作,就設(shè)置EFLAGS寄存器.
- 驗(yàn)證實(shí)驗(yàn)cmptest.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -o cmptest.o cmptest.s ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o cmptest cmptest.o ./cmptest echo $?執(zhí)行結(jié)果如下:
分析:程序首先把15賦給寄存器eax,把10賦給寄存器ebx,再使用CMP指令比較這兩個(gè)寄存器,按照比較的結(jié)果,使用JGB指令進(jìn)行分支操作,因?yàn)榧拇嫫鱡bx的值小于寄存器eax的值,所以不執(zhí)行條件分支,轉(zhuǎn)向下一條指令執(zhí)行,將1存放到寄存器eax中,可以看到,寄存器ebx中的值確實(shí)仍是10,沒有進(jìn)行分支操作。
使用奇偶校檢標(biāo)志
奇偶校驗(yàn)標(biāo)志表明數(shù)學(xué)運(yùn)算答案中應(yīng)該為1的位的數(shù)目??梢允褂盟鳛榇致缘腻e(cuò)誤檢查系統(tǒng).確保數(shù)學(xué)操作成功執(zhí)行。
如果結(jié)果中被設(shè)況為1的位的數(shù)目是偶數(shù),則設(shè)置奇偶校驗(yàn)位(置1)。如果設(shè)置為1的位的數(shù)目是奇數(shù),則不設(shè)置奇偶校驗(yàn)位(置0)。
- 驗(yàn)證實(shí)驗(yàn)paritytest.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -o paritytest.o paritytest.s ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o paritytest paritytest.o ./paritytest echo $?執(zhí)行結(jié)果如下:
分析:減法的結(jié)果為1,以二進(jìn)制表示是00000001。因?yàn)闉?的位的數(shù)目是奇數(shù),所以不設(shè)置奇偶校檢位,JP指令不會(huì)跳轉(zhuǎn)到分支,程序退出,并且以減法的結(jié)果1作為結(jié)果代碼。
為了測(cè)試相反的情況,把原程序中的:
subl $3, %ebx改為:
subl $1, %ebx分析:減法的結(jié)果是3,以二進(jìn)制表示是00000011,因?yàn)闉?的位的數(shù)目是偶數(shù),所以設(shè)置奇偶校檢位,并且JP指令應(yīng)該轉(zhuǎn)到overhere標(biāo)簽的分支,設(shè)置結(jié)果代碼為100。
使用符號(hào)標(biāo)志
符號(hào)標(biāo)志使用在帶符號(hào)數(shù)中,用于表示寄存器中包含的值的符號(hào)改變。在帶符號(hào)數(shù)中,最后一位(最高位)用作符號(hào)位。它表明數(shù)字表示是負(fù)值(設(shè)置為1)還是正值(設(shè)置為0)。
- 驗(yàn)證實(shí)驗(yàn)signtest.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -o signtest.o signtest.s ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o signtest -lc signtest.o ./signtest執(zhí)行結(jié)果如下:
分析:signtest.s程序反向遍歷數(shù)據(jù)數(shù)組,使用寄存器edi作為變址,處理每個(gè)數(shù)組元素時(shí)遞減這個(gè)寄存器。使用JNS指令檢杳寄存器edi的值什么時(shí)候變成負(fù)值,如果不是負(fù)值,則返回到循環(huán)的開頭。
循環(huán)指令
循環(huán)指令基本格式:
loop address其中address是要跳轉(zhuǎn)到的程序代碼位置的標(biāo)簽名稱。循環(huán)開始前,必須在寄存器ecx中設(shè)置執(zhí)行迭代的次數(shù)。
- 驗(yàn)證實(shí)驗(yàn)loop.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -o loop.o loop.s ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o loop -lc loop.o ./loop執(zhí)行結(jié)果如下:
分析:循環(huán)指令執(zhí)行100以及以內(nèi)的正整數(shù)的相加指令,利用循環(huán)實(shí)現(xiàn)直到寄存器ecx的值為0,可以看到,結(jié)果為5050。
- 驗(yàn)證實(shí)驗(yàn)betterloop.s
把loop.s原程序代碼中的:
movl $100, %ecx改為:
movl $0, %ecx執(zhí)行程序:
分析:將寄存器ecx設(shè)置為0時(shí)LOOP指令會(huì)將其遞減為-1,然后繼續(xù)執(zhí)行下去,顯示錯(cuò)誤的值。所以需要使用JCXZ指令執(zhí)行條件分支避免出錯(cuò)。
執(zhí)行程序命令:
as --32 -o betterloop.o betterloop.s ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o betterloop -lc betterloop.o ./betterloop執(zhí)行結(jié)果如下:
分析:結(jié)果輸出為0,確實(shí)正確的循環(huán)。
- 驗(yàn)證實(shí)驗(yàn)ifthen.c
程序的源代碼略。
執(zhí)行程序命令:
gcc -m32 -S ifthen.c cat ifthen.s執(zhí)行結(jié)果如下:
分析:實(shí)現(xiàn)if-then語句的匯編語言代碼邏輯
- 驗(yàn)證實(shí)驗(yàn)for.c
程序的源代碼略。
執(zhí)行程序命令:
gcc -m32 -S for.c cat for.s執(zhí)行結(jié)果如下:
分析:實(shí)現(xiàn)for語句的匯編語言代碼邏輯
Chapter 07
使用帶符號(hào)整數(shù)
- 驗(yàn)證實(shí)驗(yàn)inttest.s
程序的源代碼略。
執(zhí)行程序命令:
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執(zhí)行結(jié)果如下:
分析:調(diào)試器假設(shè)寄存器ebx和ecx包含帶符號(hào)整數(shù),并且使用我們期望的數(shù)據(jù)類型顯示答案。但是寄存器edx出現(xiàn)了問題。因?yàn)檎{(diào)試器試圖把整個(gè)寄存器edx作為帶符號(hào)整數(shù)數(shù)據(jù)值顯示,所以它假設(shè)整個(gè)寄存器edx包含一個(gè)雙字帶符號(hào)整數(shù)(32位)。因?yàn)榧拇嫫鱡dx只包含一個(gè)單字整數(shù)(16位),所以解釋出的值是錯(cuò)誤的。寄存器中的數(shù)據(jù)仍然是正確的(0xFFB1),但是調(diào)試器認(rèn)為的這個(gè)數(shù)字表示的內(nèi)容是錯(cuò)誤的.
MOVZE指令
MOVZX指令把長(zhǎng)度小的無符號(hào)整數(shù)值(可以在寄存器中,也可以在內(nèi)存中)傳送給長(zhǎng)度大的無符號(hào)整數(shù)值(只能在寄存器中)。
MOVZX指令格式:
movzx source, destination其中source可以是8位或16位寄存器或者內(nèi)存位置,destination可以是16位或者32位寄存器。
- 驗(yàn)證實(shí)驗(yàn)movzxtest.s
程序的源代碼略。
執(zhí)行程序命令:
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執(zhí)行結(jié)果如下:
分析:movzxtest.s程序簡(jiǎn)單地把一個(gè)大的值存放到寄存器ecx中,然后使用MOVZX指令把低8位 復(fù)制到寄存器ebx。因?yàn)榇娣旁诩拇嫫鱡cx中的值使用長(zhǎng)度為字的無符號(hào)整數(shù)表示它(它大于255),所以CL中的值只表示完整值的一部分。
通過輸出寄存器ebx和ecx的十進(jìn)制值,馬上就能發(fā)現(xiàn)無符號(hào)整數(shù)值沒有被正確地復(fù)制,原始值為279,但是新的值只是23。通過按照十六進(jìn)制顯示值,可以發(fā)現(xiàn)為什么會(huì)這樣。十六進(jìn)制格式的原始值為0x0117,它占用一個(gè)雙字。MOVZX指令只傳送了寄存器ecx的低位字節(jié),而用0填充了寄存器ebx中剩余的字節(jié),這樣就在寄存器ebx中生成了0x17這個(gè)值。
MOVSX指令
MOVSX指令允許擴(kuò)展帶符號(hào)整數(shù)并且保留符號(hào),它假設(shè)要傳送的字節(jié)是帶符號(hào)整數(shù)格式,并且試圖在傳送過程中保持帶符號(hào)整數(shù)的值不變。
- 驗(yàn)證實(shí)驗(yàn)movsxtest.s
程序的源代碼略。
執(zhí)行程序命令:
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執(zhí)行結(jié)果如下:
分析:movsxtest.s程序在寄存器cx中(雙字長(zhǎng)度)定義一個(gè)負(fù)值。然后試圖把這個(gè)值復(fù)制到寄存器ebx中,程序首先使用零填充寄存器ebx,然后使用MOV指令。下一步,使用MOVSX指令把寄存器cx的值傳送給寄存器eax。
單步運(yùn)行程序,一直運(yùn)行到MOVSX指令之后,可以使用調(diào)試器的info命令顯示寄存器值。寄存器ecx包含的值是0x0000FFB1,低16位包含的值是0xFFB1,它是帶符號(hào)整數(shù)格式的-79。當(dāng)寄存器cx被傳送給寄存器ebx時(shí),寄存器ebx包含的值是0x0000FFB1,它是帶符號(hào)整數(shù)格式 的65457,這是不對(duì)的。
使用MOVSX指令把寄存器cx傳送給寄存器eax之后,寄存器eax包含的值是0xFFFFFFB1,它是帶符號(hào)整數(shù)格式的-79,MOVSX指令正確地為這個(gè)值添加了高位部分的1。
- 驗(yàn)證實(shí)驗(yàn)movsxtest2.s
程序的源代碼略。
執(zhí)行程序命令:
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執(zhí)行結(jié)果如下:
分析:movsxtest2.s和movsxtest.s完成相同的工作,但是使用的是帶符號(hào)整數(shù)正值。當(dāng)寄存器cx被傳送給空的寄存器ebx時(shí)。值的格式是正確的(因?yàn)楦呶徊糠值牧銓?duì)正數(shù)是沒有問題的)。另外,MOVSX指令正確地使用零填充了寄存器eax,生成了正確的32位帶符號(hào)整數(shù)值。
在GNU匯編器中定義整數(shù)
.quad命令可以定義一個(gè)或者多個(gè)帶符號(hào)整數(shù)值
- 驗(yàn)證實(shí)驗(yàn)quadtest.s
程序的源代碼略。
執(zhí)行程序命令:
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執(zhí)行結(jié)果如下:
分析:程序簡(jiǎn)單地在標(biāo)簽data1的位置定義一個(gè)包含5個(gè)雙子帶符號(hào)整數(shù)的數(shù)組,在標(biāo)簽data2的位置定義一個(gè)包含5個(gè)四字帶符號(hào)整數(shù)的數(shù)組,然后退出程序,為了查看執(zhí)行情況,再次對(duì)程序進(jìn)行匯編并且在調(diào)試器中運(yùn)行它。
首先,顯示調(diào)試器認(rèn)為的data1和data2數(shù)組的十進(jìn)制值,data1數(shù)組的如期望,data2數(shù)組中的值不是程序中使用的值,這是因?yàn)檎{(diào)試器假設(shè)這些值是雙字的帶符號(hào)整數(shù)值。
然后查看內(nèi)存中標(biāo)簽data1位置的數(shù)組值是如何存儲(chǔ)的,可以看到,每個(gè)數(shù)組元素使用4個(gè)字節(jié),并且按照小尾數(shù)格式存放。
接著查看存儲(chǔ)在標(biāo)簽data2位置的數(shù)組值,可以看到,標(biāo)簽data2位置的數(shù)據(jù)值是使用四字編碼的,所以每個(gè)值使用8個(gè)字節(jié),匯編器把這些值放到了正確的位置,但是調(diào)試器不知道僅僅通過x/d命令如何顯示這些值,需要使用gd選項(xiàng)顯示這些值。
- 驗(yàn)證實(shí)驗(yàn)mmxtest.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -gstabs -o mmxtest.o mmxtest.s ld -m elf_i386 -o mmxtest mmxtest.o gdb -q mmxtest執(zhí)行結(jié)果如下:
分析:程序定義了兩個(gè)數(shù)據(jù)數(shù)組。第一個(gè)數(shù)組(value1)定義2個(gè)雙字帶符號(hào)整數(shù),第二個(gè)數(shù)組(value2)定義8個(gè)字節(jié)帶符號(hào)整數(shù)值。使用MOVQ指令把這些值加載到前2個(gè)MMX寄存器中。
可以看到,單步運(yùn)行到MOVQ指令之后,可以顯示MM0和MM1寄存器中的值,顯示寄存器時(shí),調(diào)試器不知道寄存器中數(shù)據(jù)的格式是什么,所以它會(huì)顯示所有可能的情況,第一個(gè)pprint命令把MM0寄存器的內(nèi)容顯示為雙字整數(shù)值。因?yàn)榍懊娴睦邮褂秒p字整數(shù)值,所以唯一有意義的顯示格式是int32,它顯示正確的信息??梢允褂胮rint/f命令使調(diào)試器只生成這一格式。
但是MM1寄存器包含字節(jié)整數(shù)值,所以不能按照十進(jìn)制模式顯示它。可以使用print命令的x參數(shù)顯示寄存器中的原始字節(jié),可以看到,各個(gè)字節(jié)被正確地存放到了MM1寄存器中。
傳送SSE整數(shù)
MOVDQA和MOVDQU指令的簡(jiǎn)單格式:
movdqa source, destinationMOVDQA和MOVDQU指令用于把128位數(shù)據(jù)傳送到XMM寄存器中,或者在XMM寄存器之間傳送數(shù)據(jù),對(duì)于對(duì)準(zhǔn)16個(gè)字節(jié)邊界的數(shù)據(jù),就使用A選項(xiàng),否則,就使用U選項(xiàng),其中source和destination可以是SSE128位寄存器或者128位的內(nèi)存地址。
- 驗(yàn)證實(shí)驗(yàn)ssetest.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -gstabs -o ssetest.o ssetest.s ld -m elf_i386 -o ssetest ssetest.o gdb -q ssetest執(zhí)行結(jié)果如下:
分析:程序定義了兩個(gè)包含不同整數(shù)數(shù)據(jù)類型的數(shù)據(jù)數(shù)組。第一個(gè)數(shù)組(value1)定義4個(gè)雙字帶符號(hào)整數(shù),第二個(gè)數(shù)組(value2)定義2個(gè)四字帶符號(hào)整數(shù)值。使用MOVDQU指令把這兩個(gè)數(shù)據(jù)數(shù)組傳送到SSE寄存器中。
可以看到,MOVDQU指令執(zhí)行之后,XMM0和XMM1寄存器包含數(shù)據(jù)段中定義的數(shù)據(jù)值。XMM0寄存器包含4個(gè)雙字帶符號(hào)整數(shù)數(shù)據(jù)值,XMM1寄存器包含2個(gè)四字帶符號(hào)整數(shù)數(shù)據(jù)值。
傳送BCD值
IA-32指令集包含處理80位打包BCD值的指令。可以使用FBLD和FBSTP指令把80位打包 BCD值加載到FPU寄存器中以及從FPU寄存器獲取這些值。
使用FPU寄存器的方式和使用通用寄存器稍微有些區(qū)別。8個(gè)FPU寄存器的行為類似于內(nèi)存中的堆棧區(qū)域??梢园阎祲喝牒蛷棾鯢PU寄存器池。ST0引用位于堆棧頂部的寄存器。當(dāng)值被壓
入FPU寄存器堆棧時(shí).它被存放在ST0寄存器中,ST0中原來的值被加載到ST1中。
- 驗(yàn)證實(shí)驗(yàn)bcdtest.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -gstabs -o bcdtest.o bcdtest.s ld -m elf_i386 -o bcdtest bcdtest.o gdb -q bcdtest執(zhí)行結(jié)果如下:
分析:bcdtest.s程序在標(biāo)簽data1定義的內(nèi)存位置創(chuàng)建一個(gè)表示十進(jìn)制值1234的簡(jiǎn)單的BCD值(記住Intel使用小尾數(shù)表示法)。使用FBLD指令把這個(gè)值加載到FPU寄存器堆棧的頂部( ST0)。使用FIMUL指令把ST0寄存器和data2所在的內(nèi)存位置中的整數(shù)值相乘。最后,使用FBSTP指令把堆棧中新的值傳送回data1所在的內(nèi)存位置中。
首先,在執(zhí)行程序前,使用x/10xb &data1查看data1所在的內(nèi)存位置值。1234的BCD值被加載到了data1所在的內(nèi)存位置。
然后,單步執(zhí)行FBLD指令,并且使用info all命令查看ST0寄存器中的值,ST0寄存器的值應(yīng)該顯示它加載了十進(jìn)制值1234,但是這個(gè)寄存器的十六進(jìn)制不是80位打包BCD格式,在FPU中,BCD值被轉(zhuǎn)換成了浮點(diǎn)表示方式。
接著單步執(zhí)行下一條指令(FIMUL),并且再次查看寄存器,發(fā)現(xiàn)ST0寄存器中的值和2相乘了。
最后把ST0中的值存放回data1所在的內(nèi)存位置中,使用x/10xb &data1顯示這個(gè)內(nèi)存位置,可以看到,新的值被存放到了data1所在的內(nèi)存位置,并且轉(zhuǎn)換回了BCD格式。
傳送浮點(diǎn)值
FLD指令用于把浮點(diǎn)值傳送入和傳送出FPU寄存器。FLD指令的格式是:
fld source其中source可以是32位、64位或者80位內(nèi)存位置。
- 驗(yàn)證實(shí)驗(yàn)floattest.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -gstabs -o floattest.o floattest.s ld -m elf_i386 -o floattest floattest.o gdb -q floattest執(zhí)行結(jié)果如下:
分析:標(biāo)簽value1指向存儲(chǔ)在4個(gè)字節(jié)內(nèi)存中的單精度浮點(diǎn)值。標(biāo)簽value2指向存儲(chǔ)在8個(gè)字節(jié)內(nèi)存 中的雙精度浮點(diǎn)值。標(biāo)簽data指向內(nèi)存中的空緩沖區(qū),它將被用于傳輸雙精度浮點(diǎn)值。
IA-32的FLD指令用于把存儲(chǔ)在內(nèi)存中的單精度和雙精度浮點(diǎn)數(shù)加載到FPU寄存器堆棧中。為了區(qū)分這兩種數(shù)據(jù)長(zhǎng)度,GNU匯編器使用FLDS指令加載單精度浮點(diǎn)數(shù),而使用FLDL指令加載雙精度浮點(diǎn)數(shù)。
類似地,FST指令用于獲取FPU寄存器堆棧中頂部的值,并且把這個(gè)值存放到內(nèi)存位置中。對(duì)于單精度數(shù)字,使用的指令是FSTS,雙精度數(shù)字使用的指令是FSTL。
首先使用x/f &value1和x/gf &value2查看十進(jìn)制值。f選項(xiàng)顯示單精度數(shù)字,需要使用gf選項(xiàng)顯示雙精度值,顯示四字值,當(dāng)調(diào)試器試圖計(jì)算要顯示的值時(shí),已經(jīng)存在舍入錯(cuò)誤。
接著,使用x/4xb &value1和x/8xb &value2查看浮點(diǎn)值是如何存儲(chǔ)在內(nèi)存位置的。
然后,單步運(yùn)行第一條FLDS指令,使用print $st0查看ST0寄存器的值,可以看到,位于value1內(nèi)存位置中的值12.340000152587890625被正確存放到了ST0寄存器中。
接下來,單步運(yùn)行下一條指令,并且查看ST0寄存器中的值,這個(gè)值已經(jīng)被替換為新加載的雙精度值2353.63099999999985812
為了查看對(duì)原來加載的值進(jìn)行了什么處理,查看ST1寄存器,發(fā)現(xiàn)當(dāng)加載新的值時(shí),ST0中的值被下移到了ST1寄存器中。
再查看data標(biāo)簽的值,單步執(zhí)行FSTL指令,并且再次查看,發(fā)現(xiàn)FSTL指令把ST0寄存器中的值加載到了data標(biāo)簽指向的內(nèi)存位置。
- 驗(yàn)證實(shí)驗(yàn)fpuvals.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -gstabs -o fpuvals.o fpuvals.s ld -m elf_i386 -o fpuvals fpuvals.o gdb -q fpuvals執(zhí)行結(jié)果如下:
分析:程序簡(jiǎn)單地把各個(gè)浮點(diǎn)常量壓入到FPU寄存器堆棧中。值的順序和它們被存放到堆棧中的順序是相反的。
- 驗(yàn)證實(shí)驗(yàn)ssefloat.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -gstabs -o ssefloat.o ssefloat.s ld -m elf_i386 -o ssefloat ssefloat.o gdb -q ssefloat執(zhí)行結(jié)果如下:
分析:程序創(chuàng)建兩個(gè)數(shù)據(jù)數(shù)組,每個(gè)數(shù)組由4個(gè)單精度浮點(diǎn)值組成。它們將成為被存儲(chǔ)到XMM寄存器中的打包數(shù)據(jù)值,還創(chuàng)建了一個(gè)數(shù)據(jù)緩沖區(qū)。它有足夠的空間保存4個(gè)單精度浮點(diǎn)值(即一個(gè)打包的值)。然后程序使用MOVUPS指令在XMM寄存器和內(nèi)存之間傳送打包單精度浮點(diǎn)值。
可以看到,所以數(shù)據(jù)都被正確地加載到了XMM寄存器中。v4_float格式顯示使用的打包單精度浮點(diǎn)值。最后是把XMM寄存器的值復(fù)制到data位置。可以使用x/4f命令顯示結(jié)果。
按照十六進(jìn)制復(fù)查答案,發(fā)現(xiàn)內(nèi)存位置data和內(nèi)存位置value1中的值是匹配的。
- 驗(yàn)證實(shí)驗(yàn)sse2float.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -gstabs -o sse2float.o sse2float.s ld -m elf_i386 -o sse2float sse2float.o gdb -q sse2float執(zhí)行結(jié)果如下:
分析:存儲(chǔ)在內(nèi)存中的數(shù)值被改為雙精度浮點(diǎn)值。因?yàn)槌绦驅(qū)鬏敶虬?#xff0c;所以創(chuàng)建了一個(gè)包含2個(gè)值的數(shù)組。
可以看到,對(duì)于v2_double數(shù)據(jù)類型,正確的值已經(jīng)被傳送到了寄存器中。因?yàn)閮?nèi)存位置data包含2個(gè)雙精度浮點(diǎn)值,所以使用x/2gf命令顯示存儲(chǔ)在這個(gè)內(nèi)存位置的2個(gè)值,發(fā)現(xiàn)確實(shí)正確的值也被復(fù)制到了這里。
轉(zhuǎn)換指令
- 驗(yàn)證實(shí)驗(yàn)convtest.s
程序的源代碼略。
執(zhí)行程序命令:
as --32 -gstabs -o convtest.o convtest.s ld -m elf_i386 -o convtest convtest.o gdb -q convtest執(zhí)行結(jié)果如下:
分析:convtest.s程序在內(nèi)存位置value1定義一個(gè)打包單精度浮點(diǎn)值,在內(nèi)存位置value2定義一個(gè)打包雙字整數(shù)值。第一對(duì)指令可以比較CVTPS2DQ和CVTTPS2DQ指令的結(jié)果。第一條指令執(zhí)行一般的舍入,第二條指令通過向零方向舍入進(jìn)行截?cái)唷?/p>
按照v4_int32格式,值被正確地顯示出來,正如所見,一般轉(zhuǎn)換把浮點(diǎn)值124.79舍入為125。但是截?cái)噢D(zhuǎn)換把它向零方向舍入,使之成為124。內(nèi)存位置data的值被轉(zhuǎn)換為打包雙字整數(shù)后,可以使用x/4d命令顯示它??梢钥吹?#xff0c;顯示出了舍入后的整數(shù)值。
遇到問題
1.當(dāng)運(yùn)行signtest.s時(shí),會(huì)發(fā)生報(bào)錯(cuò),顯示Error: can't open signtest.s for reading: No such file or directory
原因是課本提供的原來的代碼的有一行的的寄存器出錯(cuò):
add $8, $esp解決方案:這行代碼應(yīng)該改為:
add $8, %esp執(zhí)行截圖:
2.有時(shí)候按照課本的方式進(jìn)行g(shù)db調(diào)試,比如運(yùn)行quadtest.s程序,使用x/20b &data1想以十六進(jìn)制顯示data1數(shù)組里的數(shù)值時(shí),最后顯示的是十進(jìn)制的數(shù)值。
解決方法:把x/20b &data1改為x/20xb &data1,在代表顯示數(shù)值長(zhǎng)度的n=20后面加上x,就可以以十六進(jìn)制顯示數(shù)字,其它的程序顯示時(shí)也是一樣。
執(zhí)行截圖:
3.當(dāng)運(yùn)行convtest.s時(shí),會(huì)發(fā)生報(bào)錯(cuò),顯示Error: symbol `data' is already defined
原因是課本提供的原來的代碼的data命名重復(fù)了
data:.lcomm data, 16和
movdqu %xmm0, data都與開頭的.section .data重復(fù)
解決方案:代碼應(yīng)該改為:
data1:.lcomm data, 16和
movdqu %xmm0, data1執(zhí)行截圖:
4.當(dāng)運(yùn)行ifthen.c和for.c時(shí),如果按照課本上給的指令編譯:
gcc -S ifthen.c cat ifthen.sgcc -S for.c cat for.s生成的.s文件是64位的,與課本上的代碼不符。
解決方案:
應(yīng)該使用32位命令進(jìn)行編譯:
gcc -m32 -S ifthen.c cat ifthen.sgcc -m32 -S for.c cat for.s這樣生成的.s文件就是32位的,與課本上的代碼相符。
總結(jié)
以上是生活随笔為你收集整理的操作系统实验报告3:Linux 下 x86 汇编语言2的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统实验报告2:Linux 下 x8
- 下一篇: 操作系统实验报告4:Linux 下 x8