久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

x86_64內(nèi)存尋址 - 一個完整的例子

 張宏韜 2021-04-24

之前的章節(jié)中了解了邏輯地址通過分段到線性地址的轉換,線性地址通過分頁到物理地址的轉換,,在這個章節(jié)講以linux的一個內(nèi)存尋址將之前的知識寫成一個完整的例子,。在學習的過程中,,學習的知識不是很重要,我了解一個知識,,我喜歡先學習使用方式,,然后學習理論知識,最后我會完整的將學習的理論知識將知識應用到實際中,,驗證理論知識的正確性,。

這個章節(jié)很長,首先提供一個邏輯地址(虛擬地址)如何從分段機制中得到線性地址,,然后線性地址如何映射到真正的物理地址,,最后處理器通過這個物理地址進行尋址。而且我們會區(qū)分內(nèi)核空間和用戶空間的尋址,。我們提供一個虛擬地址 0xffffffff81bd6b60,。那么,處理器是如何找到對應在物理內(nèi)存中的地址呢,?下面就讓我們慢慢揭開這個面紗,。

這個章節(jié)基于x86_64架構的處理器運行。

邏輯地址轉換為線性地址

其實,,在之前的知識中我們知道在x86_64架構,,處理器已經(jīng)弱化了段寄存器的地位,通常我們訪問的虛擬地址都被處理器轉換為相同的物理地址,,但在操作系統(tǒng)的實現(xiàn)中,,仍然需要提供段描述符。如果需要尋址一個代碼數(shù)據(jù),,那么在處理器默認使用的是 CS 段寄存器,,我們看看 `CS`的內(nèi)容

(gdb) info registers cscs 0x10 16

我們將這個數(shù)據(jù)解析成處理器理解的數(shù)據(jù)結構:

(gdb) gdt-tool print-selector 0x10Index:                                                                 0x2Table indicator(TI):                                                   0x0Requested Privilege Level(RPL):                                        0x0

`Table indicator(TI)`說明代碼段寄存器使用的是GDT描述符表,RPL為0,,查找在GDT描述表的索引位置為2,,實際上,處理器將這個索引乘以8,,得到的是16,,處理器將從這個位置獲取8個字節(jié)的代碼段描述符。

下面我們詳細查看一下Linux系統(tǒng)中段描述符表的數(shù)據(jù)內(nèi)容,。這里我們使用的qemu模擬處理器的運行,,得到gdt地址,將這個地址的128字符輸出成二進制

x86_64內(nèi)存尋址 - 一個完整的例子

這一串慕名奇妙的二進制數(shù)據(jù)表示什么意思吶(在計算機中本質上就是二進制,,但上下文賦予了這些數(shù)據(jù)不同的含義),。按照Intel文檔中對段描述符表進行解析后

NULL [0]: {limit = 0x00000000, base = 0x00000000, type = 0x00, s = 0x0, dpl = 0x0, p = 0x0, avl = 0x0, l = 0x0, d = 0x0, g = 0x0}GDT_ENTRY_KERNEL32_CS [1]: {limit = 0xffffffff, base = 0x00000000, type = 0x0b, s = 0x1, dpl = 0x0, p = 0x1, avl = 0x0, l = 0x0, d = 0x1, g = 0x1}GDT_ENTRY_KERNEL_CS [2]: {limit = 0xffffffff, base = 0x00000000, type = 0x0b, s = 0x1, dpl = 0x0, p = 0x1, avl = 0x0, l = 0x1, d = 0x0, g = 0x1}GDT_ENTRY_KERNEL_DS [3]: {limit = 0xffffffff, base = 0x00000000, type = 0x03, s = 0x1, dpl = 0x0, p = 0x1, avl = 0x0, l = 0x0, d = 0x1, g = 0x1}GDT_ENTRY_DEFAULT_USER32_CS [4]: {limit = 0xffffffff, base = 0x00000000, type = 0x0b, s = 0x1, dpl = 0x3, p = 0x1, avl = 0x0, l = 0x0, d = 0x1, g = 0x1}GDT_ENTRY_DEFAULT_USER_DS [5]: {limit = 0xffffffff, base = 0x00000000, type = 0x03, s = 0x1, dpl = 0x3, p = 0x1, avl = 0x0, l = 0x0, d = 0x1, g = 0x1}GDT_ENTRY_DEFAULT_USER_CS [6]: {limit = 0xffffffff, base = 0x00000000, type = 0x0b, s = 0x1, dpl = 0x3, p = 0x1, avl = 0x0, l = 0x1, d = 0x0, g = 0x1}Unused [7]: {limit = 0x00000000, base = 0x00000000, type = 0x00, s = 0x0, dpl = 0x0, p = 0x0, avl = 0x0, l = 0x0, d = 0x0, g = 0x0}GDT_ENTRY_TSS [8]: {limit = 0x00004087, base = 0xfffffe0000003000, type = 0x0b, dpl = 0x0, p = 0x1, zero0 = 0x0, g = 0x0, zero1 = 0x0}GDT_ENTRY_LDT [10]: {limit = 0x00000000, base = 0x0000000000000000, type = 0x00, dpl = 0x0, p = 0x0, zero0 = 0x0, g = 0x0, zero1 = 0x0}GDT_ENTRY_TLS_MIN [12]: {limit = 0x00000000, base = 0x00000000, type = 0x00, s = 0x0, dpl = 0x0, p = 0x0, avl = 0x0, l = 0x0, d = 0x0, g = 0x0}GDT_ENTRY_TLS [13]: {limit = 0x00000000, base = 0x00000000, type = 0x00, s = 0x0, dpl = 0x0, p = 0x0, avl = 0x0, l = 0x0, d = 0x0, g = 0x0}GDT_ENTRY_TLS_MAX [14]: {limit = 0x00000000, base = 0x00000000, type = 0x00, s = 0x0, dpl = 0x0, p = 0x0, avl = 0x0, l = 0x0, d = 0x0, g = 0x0}GDT_ENTRY_CPUNODE [15]: {limit = 0x00000000, base = 0x00000000, type = 0x05, s = 0x1, dpl = 0x3, p = 0x1, avl = 0x0, l = 0x0, d = 0x1, g = 0x0}

在`x86_64` 架構體系中,Linux使用了16個段描述符,,第一個位空描述符,,這個是Intel要求的,,后面的描述符就是Linux系統(tǒng)本身的設計需要了,我們在上文中知道代碼段寄存器(CS)選擇子的索引值為2,,索引處理器在此應用是是 GDT_ENTRY_KERNEL_CS 段描述符

GDT_ENTRY_KERNEL_CS [2]:          {limit = 0xffffffff, base = 0x00000000, type = 0x0b, s = 0x1, dpl = 0x0, p = 0x1, avl = 0x0, l = 0x1, d = 0x0, g = 0x1}

由于這個段描述符中S被置位,,所以這個段是代碼段或數(shù)據(jù)段,根據(jù)代碼段和數(shù)據(jù)段類型表

x86_64內(nèi)存尋址 - 一個完整的例子

在這里Type的二進制為 1011,,可以知道 CS 段選擇子訪問的是一個代碼段,,可執(zhí)行可讀可訪問的段,也就是我們所說的代碼,。有于段選擇子的RPL為0,,而段描述符中的DPL也是0,也就是說有權限訪問,。由于標志L被置位了,說明這個段執(zhí)行的是64位模式代碼,。同時P位也被置位了,,說明訪問的數(shù)據(jù)在內(nèi)存中。在這里由于G被置位,,說明段大小為4G,,基址為0,說明Linux采用的是平坦模式,,邏輯地址(虛擬地址)等于線性地址,,雖然這里提供了limit,但在64位模式下的CPU通常會忽略段大小校驗,。

所以經(jīng)過分段地址轉換,,我們計算出了訪問內(nèi)存的線性地址,在開始計算分頁轉換前,,我們先將線性地址轉換為各個分頁的分級索引,。假設將虛擬地址 0xffffffff81bd6b60,在開始這個分割之前,,我們先了解一下Linux的虛擬內(nèi)存地址布局

======================================================================================================================== Start addr | Offset | End addr | Size | VM area description ======================================================================================================================== | | | | 0000000000000000 | 0 | 00007fffffffffff | 128 TB | user-space virtual memory, different per mm __________________|____________|__________________|_________|___________________________________________________________ | | | | 0000800000000000 | 128 TB | ffff7fffffffffff | ~16M TB | ... huge, almost 64 bits wide hole of non-canonical | | | | virtual memory addresses up to the -128 TB | | | | starting offset of kernel mappings. __________________|____________|__________________|_________|___________________________________________________________ | | Kernel-space virtual memory, shared between all processes: ____________________________________________________________|___________________________________________________________ | | | | ffff800000000000 | -128 TB | ffff87ffffffffff | 8 TB | ... guard hole, also reserved for hypervisor ffff880000000000 | -120 TB | ffff887fffffffff | 0.5 TB | LDT remap for PTI ffff888000000000 | -119.5 TB | ffffc87fffffffff | 64 TB | direct mapping of all physical memory (page_offset_base) ffffc88000000000 | -55.5 TB | ffffc8ffffffffff | 0.5 TB | ... unused hole ffffc90000000000 | -55 TB | ffffe8ffffffffff | 32 TB | vmalloc/ioremap space (vmalloc_base) ffffe90000000000 | -23 TB | ffffe9ffffffffff | 1 TB | ... unused hole ffffea0000000000 | -22 TB | ffffeaffffffffff | 1 TB | virtual memory map (vmemmap_base) ffffeb0000000000 | -21 TB | ffffebffffffffff | 1 TB | ... unused hole ffffec0000000000 | -20 TB | fffffbffffffffff | 16 TB | KASAN shadow memory __________________|____________|__________________|_________|____________________________________________________________ | | Identical layout to the 56-bit one from here on: ____________________________________________________________|____________________________________________________________ | | | | fffffc0000000000 | -4 TB | fffffdffffffffff | 2 TB | ... unused hole | | | | vaddr_end for KASLR fffffe0000000000 | -2 TB | fffffe7fffffffff | 0.5 TB | cpu_entry_area mapping fffffe8000000000 | -1.5 TB | fffffeffffffffff | 0.5 TB | ... unused hole ffffff0000000000 | -1 TB | ffffff7fffffffff | 0.5 TB | %esp fixup stacks ffffff8000000000 | -512 GB | ffffffeeffffffff | 444 GB | ... unused hole ffffffef00000000 | -68 GB | fffffffeffffffff | 64 GB | EFI region mapping space ffffffff00000000 | -4 GB | ffffffff7fffffff | 2 GB | ... unused hole ffffffff80000000 | -2 GB | ffffffff9fffffff | 512 MB | kernel text mapping, mapped to physical address 0 ffffffff80000000 |-2048 MB | | | ffffffffa0000000 |-1536 MB | fffffffffeffffff | 1520 MB | module mapping space ffffffffff000000 | -16 MB | | | FIXADDR_START | ~-11 MB | ffffffffff5fffff | ~0.5 MB | kernel-internal fixmap range, variable size and offset ffffffffff600000 | -10 MB | ffffffffff600fff | 4 kB | legacy vsyscall ABI ffffffffffe00000 | -2 MB | ffffffffffffffff | 2 MB | ... unused hole __________________|____________|__________________|_________|___________________________________________________________

從這個虛擬地址,,從內(nèi)存布局中知道這個地址是一個存放內(nèi)核代碼的地址,內(nèi)核將內(nèi)核代碼映射到虛擬內(nèi)存 0xffffffff80000000 開始的2G空間,。另外我們還需注意 0xffff888000000000 開始的虛擬地址空間,,這個虛擬地址空間大小為119.5G,用于直連映射到物理地址,,這個機制為我們后續(xù)的分析提供了一些便利設施,。比如我們想訪問物理地址為 0x1000000,那么我們直接通過虛擬地址 0xffff888001000000 訪問內(nèi)存即可,。

另外還需要注意的是,,Intel的處理器已經(jīng)支持5級分頁,,支持更大的內(nèi)存空間,但在這里我們的模擬環(huán)境只是一個4級分頁的模擬處理器而已,。

那么,,進入正題,我們?nèi)绾螌⑻摂M地址 0xffffffff81bd6b60 劃分為各級分頁的索引呢,,64位系統(tǒng)支持4K,,2M,1G的內(nèi)存分頁,,這個地址是處在分頁大小是多少的內(nèi)存空間呢,,我們現(xiàn)在并不知道這個地址是處在分頁大小為多少的空間,我們先按照4K的分頁換分分頁索引,。將之前64位4K的索引處理重新看一下,,在Linux的劃分如下

47-------38 39-------30 29-------21 20-------12 11-------0    PDG          PUD        PMD         PTE        offset

這是一個支持4級映射的劃分,在Linux頁支持5級分頁,,第五級分頁叫做p4d,,這里我們忽略。我么按照這個規(guī)則處理后如下,,

pgd_index = 511, pud_index = 510, pmd_index= 013, pte_index = 470, offset = 001d6b60

通過使用GDB查找cr3寄存器的物理地址,,我們知道 CR3 寄存器中存放了第一個PGD的物理地址

(gdb) info registers cr3cr3            0x2610000           [ PDBR=1 PCID=0 ]

我們需要將 CR3`的低12位屏蔽,得到了地址 `0x2610000,,這個地址就是PDG表的存放位置,,PGD表是存放PDG項的內(nèi)存空間,是一個每項為8個字節(jié)的數(shù)組,,如果想訪問這個地址在保護模式下,,我們是無法直接使用物理地址訪問內(nèi)存數(shù)據(jù)的,但前面說到`0xffff888000000000` 開始的虛擬地址空間映射到物理空間,,我們可以根據(jù)這個規(guī)則訪問對應的物理內(nèi)存,,查看物理內(nèi)存中的內(nèi)容。這樣訪問虛擬地址 0xffff888002610000 實際上訪問的就是物理內(nèi)存`0x2610000`的數(shù)據(jù),。

(gdb) x/8bx ((pgd_t *)0xffff888002610000 511 )0xffff888002610ff8: 0x67 0x50 0x61 0x02 0x00 0x00 0x00 0x00

在這里,,我們使用pdg_index加到物理地址上,(pgd_t *) 是一個8字節(jié)的指針,,加上511實際地址為 0xffff888002610000 511 * 8,。由于Intel處理器是小端序,所以這8個字節(jié)的實際數(shù)據(jù)為 0x0000000002615067,。這個數(shù)據(jù)將低12位屏蔽,,得到地址為 0x2615000。這個物理地址就是PUD表的基址。同樣的我們訪問對應的數(shù)據(jù),,根據(jù)索引pud_index獲得PUD項,。

(gdb) x/8bx ((pgd_t *)0xffff888002615000   510)0xffff888002615ff0:     0x63    0x60    0x61    0x02    0x00    0x00    0x00    0x00

這個表項中 PS 沒有被置位,所以這個表項指向PMD表,,這個地址的數(shù)據(jù)獲得對應的PMD表的基址 0x2616000,,根據(jù)pmd_index獲得PMD項

(gdb) x/8bx ((pgd_t *)0xffff888002616000 13)0xffff888002616068: 0xe3 0x01 0xa0 0x01 0x00 0x00 0x00 0x00

我們根據(jù)這個表項值 0x0000000001a001e3,由于 PS 被置位,,說明這個表指向的是頁大小的位2M的頁,,那么剩下的線性地址的21位作為偏移量,處理器將根據(jù)頁表中的項決定是否有權限訪問等額外的工作,。

0xffffffff81bd6b60 & 0x1fffff = 0x1d6b60

同時這個表項的基址為 0x1a00000,,將基址加上偏移量就是最終的物理地址

0x1a00000 0x1d6b60 = 0x1bd6b60

最終的結果 0x1bd6b60 就是虛擬地址 0xffffffff81bd6b60 訪問的物理地址為 0x1bd6b60。

寫個一個小工具,,解釋這個過程,,這個工具根據(jù)內(nèi)核地址翻譯成中間的頁表轉換過程

(gdb) hack-tool print-x86_64-gpt 0xffffffff81bd6b60pdg: 0x2615000cr3(pgt) => pud: [cr3: 0x2610000, index: 511] => [pud : 0x2615000]pud => pmd:      [pud: 0x2615000, index: 510] => [pmd : 0x2616000],? page_size=2G : Falsepud => pmd:      [pmd: 0x2616000, index: 13] => [pa : 0x1bd6b60] | ? page_size=2G : Trueva -> pa:        ffffffff81bd6b60 => 1bd6b60
(gdb) hack-tool print-x86_64-gpt 0xffffffff81bd6b60pdg: 0x2615000cr3(pgt) => pud: [cr3: 0x4a9e000, index: 511] => [pud : 0x2615000]pud => pmd: [pud: 0x2615000, index: 510] => [pmd : 0x2616000],,? page_size=2G : Falsepud => pmd: [pmd: 0x2616000, index: 13] => [pa : 0x1bd6b60] | ? page_size=2G : Trueva -> pa: ffffffff81bd6b60 => 1bd6b60

這兩個例子,,第一個是系統(tǒng)啟動過程中進行斷點調(diào)試的打印結果,第二個是系統(tǒng)已經(jīng)啟動進行斷點調(diào)試的打印結果,,可以看到雖然CR3寄存器不一樣,但相同的內(nèi)核地址映射到相同的物理地址,。CR3寄存器之所以不一樣是因為Linux系統(tǒng)的復制機制,,CR3寄存器通常和進程綁定,一個進程會有自己的頁表,,但系統(tǒng)統(tǒng)一為進程提供了內(nèi)核相同的布局,,我們可以理解為進程雖然有自己的用戶內(nèi)存空間,但使用相同視圖的內(nèi)核空間,。

總結

上文就是一個虛擬地址轉換為物理地址的過程,,在這個過程我們應該意識到分段轉換為線性地址,在64位中,,雖然處理器弱化了段的作用,,但仍提供了使用的方式。處理器也強化了分頁的機制,,分頁為現(xiàn)代操作系統(tǒng)支持虛擬內(nèi)存提供了堅實的基礎,。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙,。如發(fā)現(xiàn)有害或侵權內(nèi)容,,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多