<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      實模式到保護模式:第13章讀書筆記

      這一章的內容是加載內核程序和用戶程序的基本流程

      關于程序的相關信息一般是放在文件的開頭,程序本來就是從最開頭的讀取,所以放在開頭也就符合一般的思維習慣,這本章中,作者創建了一個簡單的文件頭,用于存儲該程序的信息,用于加載程序的使用

      在這一章中,作者代碼演示的加載內核和用戶程序的方式可能與真正的操作系統不同,但是這個過程大體是相似的,只是真實操作系統的頭部更為復雜

      1. 接下來我來讀一下內核加載的程序代碼:

      ;代碼清單13-2
      ;文件名:c13_core.asm
      ;文件說明:保護模式微型核心程序
      ;創建日期:2011-10-26 12:11

      ;以下常量定義部分。內核的大部分內容都應當固定 

      core_code_seg_sel equ 0x38 ;內核代碼段選擇子
      core_data_seg_sel equ 0x30 ;內核數據段選擇子 
      sys_routine_seg_sel equ 0x28 ;系統公共例程代碼段的選擇子 
      video_ram_seg_sel equ 0x20 ;視頻顯示緩沖區的段選擇子
      core_stack_seg_sel equ 0x18 ;內核堆棧段選擇子
      mem_0_4_gb_seg_sel equ 0x08 ;整個0-4GB內存的段的選擇子
      
      ;-------------------------------------------------------------------------------
      

        

      ;以下是系統核心的頭部,用于加載核心程序 

      core_length dd core_end ;核心程序總長度#00
      
      sys_routine_seg dd section.sys_routine.start
      ;系統公用例程段位置#04
      
      core_data_seg dd section.core_data.start
      ;核心數據段位置#08
      
      core_code_seg dd section.core_code.start
      ;核心代碼段位置#0c
      
      core_entry dd start ;核心代碼段入口點#10
      dw core_code_seg_sel
      
      ;==============================================================================
      

        

      內核程序聲明了段選擇子的內容,所以引導程序在讀取內核,創建描述符時,便應該根據這個標準來創建內核的描述符

       

      [bits 32] 
      flush: 
      mov eax,0x0008 ;加載數據段(0..4GB)選擇子
      mov ds,eax
      
      mov eax,0x0018 ;加載堆棧段選擇子 
      mov ss,eax
      xor esp,esp ;堆棧指針 <- 0 
      mov edi,core_base_address ;讀取出來的扇區將存儲于何處
      

        


      初始化ss和ds的值,將其初始化對應的段選擇子

      mov eax,core_start_sector ;內核的起始扇區號
      mov ebx,edi ;起始地址 
      call read_hard_disk_0 ;以下讀取程序的起始部分(一個扇區) 
      

        


      ;以下判斷整個程序有多大 ;內核程序將它所具有的大小填入到了第一個扇區的第一個字節處,只需要讀取出來,將
      ;其與512相除,即能得到扇區的個數
      mov eax,[edi] ;核心程序尺寸

      首先,將內核將存入的內存的位置加載到edi寄存器中,將待讀取扇區加載eax中去,調用read_hard_disk_0例程讀取內核的第一個扇區,讀取完畢之后,因為內核的首地址便存儲了內核程序的長度,所以通過mov eax,[edi]能夠將內核程序的長度存放到eax中

      xor edx,edx 
      mov ecx,512 ;512字節每扇區
      div ecx
      
      or edx,edx
      jnz @1 ;未除盡,因此結果比實際扇區數少1 
      dec eax ;已經讀了一個扇區,扇區總數減1 
      @1:
      or eax,eax ;考慮實際長度≤512個字節的情況 
      jz setup ;EAX=0 ?
      
      ;讀取剩余的扇區
      mov ecx,eax ;32位模式下的LOOP使用ECX
      mov eax,core_start_sector
      inc eax ;從下一個邏輯扇區接著讀
      @2:
      call read_hard_disk_0
      inc eax
      loop @2 ;循環讀,直到讀完整個內核 
      

        


      div 除數:指令默認被除數為edx:eax,商存儲在eax中,edx存儲余數
      首先將edx的值清零,將512存入ecx中,作為除數,因為一個扇區存儲512個字節
      上面的代碼成功得到總扇區數目,并且成功讀取了剩余的扇區

      ;到此處便將內核程序全部從硬盤中讀取出來了,也是位于內存0x40000處的內存開始位置處

      setup:
      mov esi,[0x7c00+pgdt+0x02] ;不可以在代碼段內尋址pgdt,但可以
      ;通過4GB的段來訪問
      ;建立公用例程段描述符
      mov eax,[edi+0x04] ;公用例程代碼段起始匯編地址
      mov ebx,[edi+0x08] ;核心數據段匯編地址
      sub ebx,eax
      dec ebx ;公用例程段界限
      add eax,edi ;公用例程段基地址,edi是內核加載的位置,而eax是公用例程段定義的基地址
      mov ecx,0x00409800 ;字節粒度的代碼段描述符
      call make_gdt_descriptor
      mov [esi+0x28],eax
      mov [esi+0x2c],edx
      
      ;建立核心數據段描述符
      mov eax,[edi+0x08] ;核心數據段起始匯編地址
      mov ebx,[edi+0x0c] ;核心代碼段匯編地址 
      sub ebx,eax
      dec ebx ;核心數據段界限
      add eax,edi ;核心數據段基地址
      mov ecx,0x00409200 ;字節粒度的數據段描述符 
      call make_gdt_descriptor
      mov [esi+0x30],eax
      mov [esi+0x34],edx 
      
      ;建立核心代碼段描述符
      mov eax,[edi+0x0c] ;核心代碼段起始匯編地址
      mov ebx,[edi+0x00] ;程序總長度
      sub ebx,eax
      dec ebx ;核心代碼段界限
      add eax,edi ;核心代碼段基地址
      mov ecx,0x00409800 ;字節粒度的代碼段描述符
      call make_gdt_descriptor
      mov [esi+0x38],eax
      mov [esi+0x3c],edx
      
      mov word [0x7c00+pgdt],63 ;描述符表的界限
      lgdt [0x7c00+pgdt] 
      

        


      上面的代碼創建了屬于內核的數據段,代碼段,公共例程段的值,放入gdt表的位置與內核聲明的常數的值一致,在最后將新的gdt表的界限寫入對應的內存區域中,lgdt指令將新的gdt表的基地址和表的大小加載到GDT寄存器中去

      jmp far [edi+0x10]

      可以看一下上面內核頭部的定義,內核中偏移量為0x10中存儲的值為

      core_entry dd start ;核心代碼段入口點#10
      dw core_code_seg_sel
      

        



      因此通過遠跳轉指令最終跳轉到內核標號為start的位置繼續執行

      2. 進入內核程序的start開始運行,接下來進入內核加載用戶程序的步驟

      mov esi,50 ;用戶程序位于邏輯50扇區
      call load_relocate_program ;加載用戶程序

      將用戶程序所在扇區放入esi中,開始加載用戶程序,接下來調用load_relocate_program來執行加載用戶程序


      load_relocate_program:
      
      push ebx
      push ecx
      push edx
      push esi
      push edi
      
      push ds
      push es
      
      mov eax,core_data_seg_sel
      mov ds,eax 
      mov eax,esi ;讀取程序頭部數據 
      mov ebx,core_buf 
      call sys_routine_seg_sel:read_hard_disk_0 ;讀取出來的將會被放到core_buf緩沖區中,讀第一個扇區
      
      ;以下判斷整個程序有多大
      mov eax,[core_buf] ;程序尺寸,用戶程序的第一個字節記錄了該程序的大小
      mov ebx,eax
      and ebx,0xfffffe00 ;使之512字節對齊(能被512整除的數, 
      add ebx,512 ;低9位都為0 
      test eax,0x000001ff ;程序的大小正好是512的倍數嗎? 
      cmovnz eax,ebx ;不是。使用湊整的結果
      

        

      首先,ds指向內核數據段的選擇子,加載用戶程序的第一個扇區到內核數據段中的緩沖區中,通過讀取第一個字節,得到該用戶程序的長度,其中用到了cmovnz指令,避免了跳轉指令

      mov ecx,eax ;實際需要申請的內存數量,eax存儲了該程序大小,而且該大小是512的整數倍
      call sys_routine_seg_sel:allocate_memory	;返回時,ecx返回了分配內存的首地址
      mov ebx,ecx ;ebx -> 申請到的內存首地址
      push ebx ;保存該首地址,被分配內存的首地址
      xor edx,edx
      mov ecx,512
      div ecx
      mov ecx,eax ;總扇區數 
      
      mov eax,mem_0_4_gb_seg_sel ;切換DS到0-4GB的段
      mov ds,eax
      

        


      上面得到的eax是用戶程序的長度,并且是512字節的倍數,將其除以512,得到用戶程序所占據的扇區數,接下來就要將硬盤中的數據加載到內存中去,而通過allocate_memory分配的內存是線性地址,所以將ds切換到0-4GB段

      mov eax,esi ;起始扇區號,那么第一個扇區將會被連續讀取兩次,eax指向要被讀取的扇區號
      .b1:
      call sys_routine_seg_sel:read_hard_disk_0
      inc eax
      loop .b1 ;循環讀,直到讀完整個用戶程序
      

        

      讀取所有的扇區到為它分配的內存處,接下來就可以讀取用戶程序的頭部段,創建相對應的描述符,為用戶程序使用的例程進行重定位到正確的位置

      ;建立程序頭部段描述符

      pop edi ;恢復程序裝載的首地址 
      mov eax,edi ;程序頭部起始線性地址
      mov ebx,[edi+0x04] ;段長度
      dec ebx ;段界限 
      mov ecx,0x00409200 ;字節粒度的數據段描述符
      call sys_routine_seg_sel:make_seg_descriptor
      call sys_routine_seg_sel:set_up_gdt_descriptor
      mov [edi+0x04],cx
      ;建立程序代碼段描述符
      mov eax,edi
      add eax,[edi+0x14] ;代碼起始線性地址
      mov ebx,[edi+0x18] ;段長度
      dec ebx ;段界限
      mov ecx,0x00409800 ;字節粒度的代碼段描述符
      call sys_routine_seg_sel:make_seg_descriptor
      call sys_routine_seg_sel:set_up_gdt_descriptor
      mov [edi+0x14],cx
      
      ;建立程序數據段描述符
      mov eax,edi
      add eax,[edi+0x1c] ;數據段起始線性地址
      mov ebx,[edi+0x20] ;段長度
      dec ebx ;段界限
      mov ecx,0x00409200 ;字節粒度的數據段描述符
      call sys_routine_seg_sel:make_seg_descriptor
      call sys_routine_seg_sel:set_up_gdt_descriptor
      mov [edi+0x1c],cx
      
      ;建立程序堆棧段描述符
      mov eax,edi ;得到程序加載的基地址
      add eax,[edi+0x08] ;得到棧段的基地址,可是對于棧是要得到其高端地址作為基地址	;
      add eax,[edi+0x0c]	;得到堆棧的高端地址
      mov ebx,0xffffffff	
      sub ebx,[edi+0x0c]	;兩者相減得出棧的界限值,esp > ebx
      mov ecx,0x00409600 
      call sys_routine_seg_sel:make_seg_descriptor
      call sys_routine_seg_sel:set_up_gdt_descriptor
      mov [edi+0x08],cx
      

        

        

       

      根據用戶程序的頭部的內容,為用戶程序創建了代碼段,數據段,堆棧段,將所創建的描述符選擇子,并且將其放回用戶程序的頭部中去,用戶程序可以直接引用這些選擇子

      ;重定位SALT
      mov eax,[edi+0x04]
      mov es,eax ;es -> 用戶程序頭部 
      mov eax,core_data_seg_sel
      mov ds,eax
      
      cld
      
      mov ecx,[es:0x24] ;用戶程序的SALT條目數
      mov edi,0x28 ;用戶程序內的SALT位于頭部內0x2c處
      .b2: 
      push ecx
      push edi
      
      mov ecx,salt_items
      mov esi,salt
      .b3:
      push edi
      push esi
      push ecx
      
      mov ecx,64 ;檢索表中,每條目的比較次數 
      repe cmpsd ;每次比較4字節 
      jnz .b4
      mov eax,[esi] ;若匹配,esi恰好指向其后的地址數據
      mov [es:edi-256],eax ;將字符串改寫成偏移地址 
      mov ax,[esi+4]
      mov [es:edi-252],ax ;以及段選擇子 
      .b4:
      
      pop ecx
      pop esi
      add esi,salt_item_len
      pop edi ;從頭比較 
      loop .b3
      
      pop edi
      add edi,256
      pop ecx
      loop .b2
      
      mov ax,[es:0x04] ;將用戶頭部的選擇子放到ax中返回
      
      pop es ;恢復到調用此過程前的es段 
      pop ds ;恢復到調用此過程前的ds段
      
      pop edi
      pop esi
      pop edx
      pop ecx
      pop ebx
      
      ret
      

        


      為用戶程序使用到的系統調用進行重定位,將例程段在內核中的選擇子和實際例程的偏移量放置到用戶程序的salt中去

      此時已經成功加載完成了用戶程序,返回內核程序

      總結:這一章通過作者通過一個實例來說明了用戶程序加載和內核程序加載的大致流程。

      posted @ 2019-12-12 17:11  菜鳥額  閱讀(190)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产精品第一二三区久久| 久久这里只有精品好国产| 国产99视频精品免费专区| 韩国V欧美V亚洲V日本V| 国产成人亚洲综合| 欧美成本人视频免费播放| 精品一区二区久久久久久久网站| 亚洲av激情五月性综合| 日韩精品福利一区二区三区| 亚洲av激情久久精品人| 国产一区二区日韩在线| 亚洲V天堂V手机在线| 亚洲高清WWW色好看美女| 国产精品无遮挡一区二区| 国产性一交一乱一伦一色一情 | 欧美极品色午夜在线视频| 久久综合开心激情五月天| 午夜福利国产盗摄久久性| 精选国产av精选一区二区三区 | 日本伊人色综合网| 亚洲一区成人在线视频| 精品91在线| 成人3D动漫一区二区三区| 久久99国产乱子伦精品免费| 国产精品一区在线蜜臀| 深夜释放自己在线观看| 亚洲国产精品综合久久20| 久久人妻少妇嫩草av无码专区| 国产精品一区二区小视频| 亚洲婷婷综合色香五月| 九九热在线视频免费播放| 玩弄人妻少妇500系列| 久久国产精品伊人青青草| 在线播放亚洲成人av| 精品91在线| 一区二区三区鲁丝不卡| 亚洲区成人综合一区二区| 日韩精品一区二区三区无| 国产亚洲AV电影院之毛片| 国产在线观看91精品亚瑟| 日韩欧美国产aⅴ另类|