Linux內(nèi)核完全剖析---math_emulate.c程序
math_emulate.c程序中的所有函數(shù)可分為3部分:第一類(lèi)是設(shè)備不存在異常處理程序接口函數(shù)math_emulate(),只有這一個(gè)函數(shù);第二類(lèi)是浮點(diǎn)指令仿真處理主函數(shù)do_emu(),也只有一個(gè)函數(shù);另外所有函數(shù)都是仿真運(yùn)算輔助類(lèi)函數(shù),包括其余幾個(gè)C語(yǔ)言程序中的函數(shù)。
在一臺(tái)不包含80387協(xié)處理器芯片的PC中,如果內(nèi)核初始化時(shí)在CR0中設(shè)置了仿真標(biāo)志EM = 1,那么當(dāng)CPU遇到一條浮點(diǎn)指令時(shí)就會(huì)引起CPU產(chǎn)生異常中斷int 7,并且在該中斷處理過(guò)程中調(diào)用本程序中第476行處的math_emulate(long ___false)函數(shù)。
在math_emulate()函數(shù)中,若判斷出當(dāng)前進(jìn)程還沒(méi)有使用過(guò)仿真的協(xié)處理運(yùn)算時(shí)就會(huì)對(duì)仿真的80387控制字、狀態(tài)字和特征字(Tag Word)進(jìn)行初始化操作,設(shè)置控制字中所有6種協(xié)處理器異常屏蔽位并復(fù)位狀態(tài)字和特征字。然后調(diào)用仿真處理主函數(shù)do_emu()。使用的參數(shù)是作為如下info結(jié)構(gòu)的中斷處理過(guò)程中調(diào)用math_emulate()函數(shù)的返回地址指針。info結(jié)構(gòu)實(shí)際上就是棧中自從CPU產(chǎn)生中斷int7后逐漸入棧的一些數(shù)據(jù)構(gòu)成的一個(gè)結(jié)構(gòu),因此它與系統(tǒng)調(diào)用時(shí)內(nèi)核棧中數(shù)據(jù)的分布情況基本相同。參見(jiàn)include/linux/math_emu.h文件第 11 行和kernel/sys_call.s開(kāi)始部分。
do_emu()函數(shù)(第52行)首先根據(jù)狀態(tài)字來(lái)判斷有沒(méi)有發(fā)生仿真的協(xié)處理器內(nèi)部異常。若有則設(shè)置狀態(tài)字的忙位B(位15),否則就復(fù)位忙位B。然后從上述info結(jié)構(gòu)中EIP字段處取得產(chǎn)生協(xié)處理器異常的二字節(jié)浮點(diǎn)指令代碼code,并在屏蔽掉每條浮點(diǎn)指令碼中都相同的ESC碼(二進(jìn)制11011)位部分后,根據(jù)此時(shí)的code值對(duì)具體的浮點(diǎn)指令進(jìn)行軟件仿真運(yùn)算處理。為便于處理,該函數(shù)按5種類(lèi)型浮點(diǎn)指令碼分別使用了五個(gè)switch語(yǔ)句進(jìn)行處理。例如,第一個(gè)switch語(yǔ)句(第75行)用于處理那些不涉及尋址內(nèi)存操作數(shù)的浮點(diǎn)指令。而最后兩個(gè)switch語(yǔ)句(第419、432行)則專(zhuān)門(mén)用來(lái)處理操作數(shù)與內(nèi)存相關(guān)的指令。對(duì)于后一種類(lèi)型的指令,其處理過(guò)程的基本流程是首先根據(jù)指令代碼中的尋址模式字節(jié)取得內(nèi)存操作數(shù)的有效地址,然后從該有效地址處讀取相應(yīng)的數(shù)據(jù)(整型數(shù)、實(shí)數(shù)或BCD碼數(shù)值)。接著把讀取的值轉(zhuǎn)換成80387內(nèi)部處理使用的臨時(shí)實(shí)數(shù)格式。在計(jì)算完畢后,再把臨時(shí)實(shí)數(shù)格式的數(shù)值轉(zhuǎn)換為原數(shù)據(jù)類(lèi)型,最后保存到用戶數(shù)據(jù)區(qū)中。
另外,在具體仿真一條浮點(diǎn)指令時(shí),若發(fā)現(xiàn)浮點(diǎn)指令無(wú)效,則程序會(huì)立刻調(diào)用放棄執(zhí)行函數(shù)__math_abort()。該函數(shù)會(huì)向當(dāng)前執(zhí)行進(jìn)程發(fā)送指定的信號(hào),同時(shí)修改棧指針esp指向中斷過(guò)程中調(diào)用math_emulate()函數(shù)的返回地址(___math_ret),并立刻返回到中斷處理過(guò)程中去。
【編輯推薦】