理解x86_64 Paging(Page Map Level 4)

2024-01-15 22:12
文章标签 x86 理解 map level page 64 paging

本文主要是介绍理解x86_64 Paging(Page Map Level 4),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文
一个方便编译内核的小工具

page

在 x86_64 上,页面是 0x1000 字节的内存片,按 0x1000 字节对齐。
这就是为什么,如果查看 /proc/<pid>/maps ,会发现所有地址范围的地址开始和结束都将以 0x000 结尾,因为 x86_64 上内存映射的最小大小是页面大小(0x1000字节)并且页面需要“页面对齐”(最后 12 位必须为零)。

尽管许多虚拟页面可能引用相同的物理页面,但“虚拟页面”将被 MMU 解析为单个“物理页面”(也称为“页面框架”)。

虚拟地址中有什么

正如人们可能猜到的那样,PML4 有四级分页结构,这些分页结构称为“页表”。页表是一个页大小的内存区域,包含 512 个 8 字节页表条目。页表的每个条目将引用下一级页表或虚拟地址解析到的最终物理地址。

页表中用于地址转换的条目基于内存访问的虚拟地址。每层有 512 个条目,这意味着每层都使用 9 位虚拟地址来索引相应的页表。

假设我们有一个这样的地址:

0x7ffe1c9c9000

该地址的最后 12 位表示物理页内的偏移量:

0x7ffe1c9c9000 & 0xfff = 0x0

这意味着一旦我们确定了该虚拟地址解析到的页面的物理地址,我们将在结果中添加零以获得最终的物理地址。

最后 12 位(同样也是最终页内的偏移量)之后,虚拟地址由页表中的索引组成。如前所述,每个级别的分页使用 9 位虚拟地址,因此分页结构的最低级别(页表)由地址的接下来 9 位进行索引(通过使用 & 0x1ff 进行位掩码)移动后的值)。对于以下级别,我们只需每次右移另外九位,并再次屏蔽掉较低的九位作为我们的索引。对上面的地址执行此操作可以得到以下指示:

Level 1, Page Table (PT):
Index = (0x7ffe1c9c9000 >> 12) & 0x1ff = 0x1c9Level 2, Page Middle Directory (PMD):
Index = (0x7ffe1c9c9000 >> 21) & 0x1ff = 0x0e4Level 3, Page Upper Directory (PUD):
Index = (0x7ffe1c9c9000 >> 30) & 0x1ff = 0x1f8Level 4, Page Global Directory (PGD):
Index = (0x7ffe1c9c9000 >> 39) & 0x1ff = 0x0ff

页表基址寄存器

现在我们知道如何索引页表并模糊地了解它们包含的内容,它们实际上在哪里???
CPU 的每个线程都有一个名为 cr3 的页表基址寄存器。
cr3 保存分页结构最高层的物理地址,也称为页面全局目录(PGD)。
从gdb中,调试内核时,可以像这样读取 cr3 的内容:

gef➤  p/x $cr3
$1 = 0x10d664000

cr3 寄存器除了 PGD 地址之外还可以保存一些附加信息,具体取决于所使用的处理器功能,因此从 cr3 获取 PGD 物理地址的更通用方法寄存器的作用是屏蔽掉其内容的低 12 位,如下所示:

gef➤  p/x $cr3 & ~0xfff
$2 = 0x10d664000

页表条目 page table entries

让我们看看从 gdb 中的 cr3 获得的物理地址是什么。
QEMU 监视器向 gdb 公开的 monitor xp/... 命令让我们打印出虚拟机的物理内存,执行 monitor xp/512gx ... 将打印 PGD 的全部内容(所有 512 个条目) cr3 引用:

gef➤  monitor xp/512gx 0x10d664000
...
000000010d664f50: 0x0000000123fca067 0x0000000123fc9067
000000010d664f60: 0x0000000123fc8067 0x0000000123fc7067
000000010d664f70: 0x0000000123fc6067 0x0000000123fc5067
000000010d664f80: 0x0000000123fc4067 0x0000000123fc3067
000000010d664f90: 0x0000000123fc2067 0x000000000b550067
000000010d664fa0: 0x000000000b550067 0x000000000b550067
000000010d664fb0: 0x000000000b550067 0x0000000123fc1067
000000010d664fc0: 0x0000000000000000 0x0000000000000000
000000010d664fd0: 0x0000000000000000 0x0000000000000000
000000010d664fe0: 0x0000000123eab067 0x0000000000000000
000000010d664ff0: 0x000000000b54c067 0x0000000008c33067

这会产生大量输出,其中大部分为零,所以我在这里只包含输出的尾部。
此输出可能对您来说还没有多大意义,但我们可以观察数据中的一些模式,例如,许多 8 字节条目以 0x67 结尾。

解码 PGD 条目

从上面的 PGD 输出中,我们以 0x000000010d664f50 处值为 0x0000000123fca067 的 PGD 条目为例,了解如何解码条目。
让我们用该条目值的二进制表示来完成此操作:

gef➤  p/t 0x0000000123fca067
$6 = 100100011111111001010000001100111

这是一个小图,显示条目中的每一位代表什么:

~ PGD Entry ~                                                   Present ──────┐Read/Write ──────┐|User/Supervisor ──────┐||Page Write Through ──────┐|||Page Cache Disabled ──────┐ ||||Accessed ──────┐| ||||Ignored ──────┐|| ||||Reserved ──────┐||| ||||
┌─ NX          ┌─ Reserved                             Ignored ──┬──┐ |||| ||||
|┌───────────┐ |┌──────────────────────────────────────────────┐ |  | |||| ||||
||  Ignored  | ||               PUD Physical Address           | |  | |||| ||||
||           | ||                                              | |  | |||| ||||
0000 0000 0000 0000 0000 0000 0000 0001 0010 0011 1111 1100 1010 0000 0110 011156        48        40        32        24        16         8         0

以下是每个标签的含义:

  • NX(不可执行)——如果设置了该位,则该 PGD 条目的后代内存映射将无法执行
  • Reserved – 这些值必须为零
  • PUD Physical Address - 与该 PGD 条目关联的 PUD 的物理地址
  • Accessed – 如果此条目或其后代引用了任何页面,则该位将由 MMU 置位,并可由操作系统清除
  • Page Cache Disabled (PCD)-页面缓存禁用 (PCD) 该 PGD 条目的后代页面不应进入 CPU 的缓存层次结构,有时也称为“不可缓存”(UC) 位
  • Page Write Through (WT) – 页直写 (WT) – 对此 PGD 条目的后代页的写入应立即写入 RAM,而不是在最终更新 RAM 之前缓冲写入 CPU 高速缓存
  • User/Supervisor – 如果该位未设置,则除非处于管理员模式,否则无法访问该 PGD 的后代页面
  • Read/Write – 如果该位未设置,则无法写入该 PGD 的后代页
  • Present – 如果该位未设置,则处理器将不会使用该条目进行地址转换,并且其他位均不适用

我们真正关心的位是Present位,代表分页结构下一级的物理地址、PUD 物理地址位和权限位:NX、用户/管理员和读/写。

  • Present 位非常重要,因为如果没有设置它,条目的其余部分将被忽略
  • PUD 物理地址通过告诉我们下一级分页结构的物理地址所在位置,让我们能够继续分页
  • 权限位全部应用于 PGD 条目的后代页面,并确定如何访问这些页面

其余部分对于我们的目的来说并不那么重要:

  • Accessed位:如果该条目用于转换内存访问,则设置访问位,这对于页面遍历并不重要
  • Page Cache Disabled and Page Write Through:页面缓存禁用和页面写入不用于正常内存映射,并且不会影响页面转换或权限,因此让我们忽略它们

因此,解码此条目,我们了解到:
PUD 存在:

gef➤  p/x 0x0000000123fca067 & 0b0001
$18 = 0x1

PUD 及以下的映射可能是可写的:

gef➤  p/x 0x0000000123fca067 & 0b0010
$19 = 0x2

用户可以访问 PUD 及以下内容中的映射:

gef➤  p/x 0x0000000123fca067 & 0b0100
$20 = 0x4

PUD 的物理地址(位 (51:12] )是 0x123fca000 :

gef➤  p/x 0x0000000123fca067 & ~((1ull<<12)-1) & ((1ull<<51)-1)
$21 = 0x123fca000

PUD 中及以下的映射可能是可执行的:

gef➤  p/x 0x0000000123fca067 & (1ull<<63)
$22 = 0x0

解码所有级别的条目

现在我们已经了解了如何解码 PGD 条目,解码其余级别并没有太大不同,至少在常见情况下是这样。
对于所有这些图,“X”表示该位可以是 0 或 1,否则,如果将某个位设置为特定值,则该值要么是架构所需要的,要么是图中所示的特定编码所需要的。
PGD

~ PGD Entry ~                                                   Present ──────┐Read/Write ──────┐|User/Supervisor ──────┐||Page Write Through ──────┐|||Page Cache Disabled ──────┐ ||||Accessed ──────┐| ||||Ignored ──────┐|| ||||Reserved ──────┐||| ||||
┌─ NX          ┌─ Reserved                             Ignored ──┬──┐ |||| ||||
|┌───────────┐ |┌──────────────────────────────────────────────┐ |  | |||| ||||
||  Ignored  | ||               PUD Physical Address           | |  | |||| ||||
||           | ||                                              | |  | |||| ||||
XXXX XXXX XXXX 0XXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 0XXX XXXX56        48        40        32        24        16         8         0

这个我们已经看到了,我在上一节中详细描述了它,但这里没有填写特定的 PGD 条目。

PUD

~ PUD Entry, Page Size unset ~                                  Present ──────┐Read/Write ──────┐|User/Supervisor ──────┐||Page Write Through ──────┐|||Page Cache Disabled ──────┐ ||||Accessed ──────┐| ||||Ignored ──────┐|| ||||Page Size ──────┐||| ||||
┌─ NX          ┌─ Reserved                             Ignored ──┬──┐ |||| ||||
|┌───────────┐ |┌──────────────────────────────────────────────┐ |  | |||| ||||
||  Ignored  | ||               PMD Physical Address           | |  | |||| ||||
||           | ||                                              | |  | |||| ||||
XXXX XXXX XXXX 0XXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 0XXX XXXX56        48        40        32        24        16         8         0

正如您所看到的,上图的 PUD 与 PGD 非常相似,唯一的区别是引入了页面大小位。
设置的页面大小位极大地改变了我们解释 PUD 条目的方式。
对于此图,我们假设它未设置,这是最常见的情况。
PMD

~ PMD Entry, Page Size unset ~                                  Present ──────┐Read/Write ──────┐|User/Supervisor ──────┐||Page Write Through ──────┐|||Page Cache Disabled ──────┐ ||||Accessed ──────┐| ||||Ignored ──────┐|| ||||Page Size ──────┐||| ||||
┌─ NX          ┌─ Reserved                             Ignored ──┬──┐ |||| ||||
|┌───────────┐ |┌──────────────────────────────────────────────┐ |  | |||| ||||
||  Ignored  | ||                PT Physical Address           | |  | |||| ||||
||           | ||                                              | |  | |||| ||||
XXXX XXXX XXXX 0XXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 0XXX XXXX56        48        40        32        24        16         8         0

同样,PMD 图与上图非常相似,并且与 PUD 条目一样,我们现在忽略页面大小位。

PT

~ PT Entry ~                                                    Present ──────┐Read/Write ──────┐|User/Supervisor ──────┐||Page Write Through ──────┐|||Page Cache Disabled ──────┐ ||||Accessed ──────┐| ||||
┌─── NX                                                    Dirty ──────┐|| ||||
|┌───┬─ Memory Protection Key              Page Attribute Table ──────┐||| ||||
||   |┌──────┬─── Ignored                               Global ─────┐ |||| ||||
||   ||      | ┌─── Reserved                          Ignored ───┬─┐| |||| ||||
||   ||      | |┌──────────────────────────────────────────────┐ | || |||| ||||
||   ||      | ||            4KB Page Physical Address         | | || |||| ||||
||   ||      | ||                                              | | || |||| ||||
XXXX XXXX XXXX 0XXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX56        48        40        32        24        16         8         0

在页表条目中,事情变得更加有趣,有一些以前级别中没有的新字段/属性。
这些新字段/属性是:

  • Memory Protection Key (MPK or PK): 内存保护密钥(MPK 或 PK),这是一个 x86_64 扩展,允许将 4 位密钥分配给页面,该页面可用于配置具有该密钥的所有页面的内存权限
  • Global:全局,这与 TLB(转换后备缓冲区,用于虚拟到物理地址转换的 MMU 缓存)如何缓存页面的转换有关,设置该位意味着在上下文切换时该页面不会从 TLB 中刷新,这通常在内核页面上启用,以减少 TLB 未命中
  • Page Attribute Table (PAT): 页面属性表(PAT),如果设置,MMU 在确定页面的“内存类型”是否为“内存类型”时应参考页面属性表 MSR。该页面是否是“不可缓存”、“直写”或其他几种内存类型之一
  • Dirty:该位与访问位类似,如果该页被写入,则由 MMU 置位,并且必须由操作系统重置

这些实际上都不会影响地址转换本身,但内存保护密钥的配置可能意味着该条目引用的页面的预期内存访问权限可能比该条目本身编码的权限更严格。

与前面的级别不同,由于这是最后一个级别,因此该条目保存与我们正在转换的虚拟地址关联的页面的最终物理地址。一旦您应用位掩码来获取物理地址字节并添加原始虚拟地址的最后 12 位(页面内的偏移量),您就拥有了物理地址!

希望这看起来没那么糟糕,页面遍历的一般情况只需几个步骤:

  • 通过移位地址和应用位掩码将虚拟地址转换为索引和页面偏移量
  • 读取 cr3 得到PGD的物理地址
  • 对于每个级别,直到最后一个
    • 使用从虚拟地址计算出的索引来了解要使用页表中的哪个条目
    • 对条目应用位掩码以获得下一级的物理地址
  • 在最后一层,再次从虚拟地址中找到索引对应的条目
  • 应用位掩码来获取与虚拟地址关联的页面的物理地址
  • 添加页面内从虚拟地址到页面物理地址的偏移量
  • 完毕

大页

如前所述,前面的 PUD 和 PMD 图表适用于未设置页面大小位时的常见情况。
那么,什么时候设置呢?
当它被设置时,它有效地告诉MMU,打包它,我们就完成了,不要继续页面遍历,当前条目保存我们正在寻找的页面的物理地址。
但除此之外,页面大小位设置的条目中页面的物理地址不是普通的 4KB(0x1000 字节)页面,它是一个“大页面”,有两个变体:1GB 大页和 2MB 大页。
当 PUD 条目设置了页面大小位时,它指的是 1GB 大页面,而当 PMD 设置了页面大小位时,它指的是 2MB 大页面。

但 1GB 和 2MB 的数字从何而来呢?
每个页表级别最多可容纳 512 个条目,这意味着一个 PT 最多可以引用 512 个页面和 512 * 4KB = 2MB 。因此,PMD 级别的大页面实际上意味着该条目引用与完整 PT 大小相同的页面。

将其扩展到 PUD 级别,我们只需再次乘以 512 即可获得具有完整 PT 的完整 PMD 的大小: 512 * 512 * 4KB = 1GB

Huge Page PUD

~ PUD Entry, Page Size set ~                                     Present ─────┐Read/Write ─────┐|User/Supervisor ─────┐||Page Write Through ─────┐|||Page Cache Disabled ─────┐ ||||Accessed ─────┐| ||||Dirty ─────┐|| ||||
┌─── NX                                                Page Size ─────┐||| ||||
|┌───┬─── Memory Protection Key                         Global ─────┐ |||| ||||
||   |┌──────┬─── Ignored                             Ignored ───┬─┐| |||| ||||
||   ||      | ┌─── Reserved           Page Attribute Table ───┐ | || |||| ||||
||   ||      | |┌────────────────────────┐┌───────────────────┐| | || |||| ||||
||   ||      | || 1GB Page Physical Addr ||      Reserved     || | || |||| ||||
||   ||      | ||                        ||                   || | || |||| ||||
XXXX XXXX XXXX 0XXX XXXX XXXX XXXX XXXX XX00 0000 0000 0000 000X XXXX 1XXX XXXX56        48        40        32        24        16         8         0

当设置页面大小位时,请注意 PUD 条目看起来更像 PT 条目而不是普通的 PUD 条目,这是有道理的,因为它也引用页面而不是页表。
不过,与 PT 条目有一些区别:

  • 页大小位是 PT 上页属性表 (PAT) 位的位置,因此 PAT 位被重新定位到位 12。
  • 1GB 大页的物理地址需要在物理内存中具有 1GB 对齐,这就是为什么存在新的保留位以及为什么位 12 能够重新用作 PAT 位的原因。

总的来说,这里并没有太多新内容,处理大页面时唯一的其他区别是,需要对地址应用不同的位掩码来获取页面物理地址的位,同时计算 1GB 对齐意味着页内虚拟地址的物理地址我们需要使用基于 1GB 对齐而不是 4KB 对齐的掩码。

Huge Page PMD

~ PMD Entry, Page Size set ~                                     Present ─────┐Read/Write ─────┐|User/Supervisor ─────┐||Page Write Through ─────┐|||Page Cache Disabled ─────┐ ||||Accessed ─────┐| ||||Dirty ─────┐|| ||||
┌─── NX                                                Page Size ─────┐||| ||||
|┌───┬─── Memory Protection Key                         Global ─────┐ |||| ||||
||   |┌──────┬─── Ignored                             Ignored ───┬─┐| |||| ||||
||   ||      | ┌─── Reserved         Page Attribute Table ─────┐ | || |||| ||||
||   ||      | |┌───────────────────────────────────┐┌────────┐| | || |||| ||||
||   ||      | ||     2MB Page Physical Address     ||Reserved|| | || |||| ||||
||   ||      | ||                                   ||        || | || |||| ||||
XXXX XXXX XXXX 0XXX XXXX XXXX XXXX XXXX XXXX XXXX XXX0 0000 000X XXXX 1XXX XXXX56        48        40        32        24        16         8         0

这与设置了页面大小位的 PUD 条目非常相似,唯一改变的是,由于该级别 2MB 页面的对齐较小,因此设置的保留位较少。

2MB 对齐意味着大页内的偏移量应使用基于 2MB 对齐的掩码来计算。

手动执行页表遍历

所以最后一节有很多图表,在本节中让我们看看如何在 gdb 中实际手动执行页面遍历。

连接启动的 vm 和 gdb 后,我首先将选择一个地址来进行页面遍历,作为示例,我将在内核中运行时使用当前堆栈指针:

gef➤  p/x $rsp
$42 = 0xffffffff88c07da8

现在我们有了要遍历的地址,我们还可以从 cr3 获取 PGD 的物理地址:

gef➤  p/x $cr3 & ~0xfff
$43 = 0x10d664000

我将使用这个小 python 函数从虚拟地址中提取页表偏移量:

def get_virt_indicies(addr):pageshift = 12addr = addr >> pageshiftpt, pmd, pud, pgd = (((addr >> (i*9)) & 0x1ff) for i in range(4))return pgd, pud, pmd, pt

输出如下

In [2]: get_virt_indicies(0xffffffff88c07da8)
Out[2]: (511, 510, 70, 7)

PGD
我们根据虚拟地址获得的 PGD 索引为 511,将 511 乘以 8 将让我们获得 PGD 中的字节偏移量,虚拟地址的 PGD 条目起始位置为:

gef➤  p/x 511*8
$44 = 0xff8

将该偏移量添加到 PGD 的物理地址即可得到 PGD 条目的物理地址:

gef➤  p/x 0x10d664000+0xff8
$45 = 0x10d664ff8

读取该地址的物理内存可以获取 PGD 条目本身:

gef➤  monitor xp/gx 0x10d664ff8
000000010d664ff8: 0x0000000008c33067

看起来条目的最后三位(当前、用户和可写)已设置,最高位 (NX) 未设置,这意味着到目前为止与此虚拟地址关联的页面的权限没有任何限制。

屏蔽位 [12, 51) 给出了 PUD 的物理地址:

gef➤  p/x 0x0000000008c33067 & ~((1<<12)-1) & ((1<<51) - 1)
$46 = 0x8c33000

PUD
我们根据虚拟地址获得的 PUD 索引为 510,将 510 乘以 8 将让我们获得 PUD 中的字节偏移量,该偏移量是虚拟地址的 PUD 条目的起始位置:

gef➤  p/x 510*8
$47 = 0xff0

将该偏移量添加到 PUD 的物理地址即可得到 PUD 条目的物理地址:

gef➤  p/x 0x8c33000+0xff0
$48 = 0x8c33ff0

读取该地址的物理内存可以获取 PUD 条目本身:

gef➤  monitor xp/gx 0x8c33ff0
0000000008c33ff0: 0x0000000008c34063

在这个级别,我们需要开始关注大小位(位 7),因为如果它是 1GB 页面,我们将在此停止页面遍历。

gef➤  p/x 0x0000000008c34063 & (1<<7)
$49 = 0x0

似乎该条目未设置,因此我们将继续页面行走。
另请注意,PUD 条目以 0x3 结尾,而不是像上一级别那样以 0x7 结尾,底部两位(存在、可写)仍然设置,但第三位(用户位)现在未设置。这意味着用户模式访问属于该 PUD 条目的页面将由于访问权限检查失败而导致页面错误。
NX 位仍未设置,因此属于该 PUD 的页面仍然可执行。
屏蔽位 [12, 51) 给出了 PMD 的物理地址:

gef➤  p/x 0x0000000008c34063 & ~((1ull<<12)-1) & ((1ull<<51)-1)
$50 = 0x8c34000

PMD
我们根据虚拟地址获得的 PMD 索引为 70,将 70 乘以 8 将让我们获得虚拟地址的 PMD 条目开始处的 PMD 字节偏移量:

gef➤  p/x 70*8
$51 = 0x230

将该偏移量添加到 PMD 的物理地址即可得到 PMD 条目的物理地址:

gef➤  p/x 0x8c34000+0x230
$52 = 0x8c34230

读取该地址的物理内存可以获取 PMD 条目本身:

gef➤  monitor xp/gx 0x8c34230
0000000008c34230: 0x8000000008c001e3

同样,在这个级别我们需要注意大小位,因为如果它是 2MB 页面,我们将在这里停止页面遍历。

gef➤  p/x 0x8000000008c001e3 & (1<<7)
$53 = 0x80

看起来我们的虚拟地址指的是一个2MB的大页面!因此该 PMD 条目中的物理地址就是该大页的物理地址。
另外,查看权限位,看起来该页面仍然存在并且可写,并且用户位仍然未设置,因此该页面只能从管理员模式(ring-0)访问。

与之前的级别不同,最高位 NX 位被设置:

gef➤  p/x 0x8000000008c001e3 & (1ull<<63)
$54 = 0x8000000000000000

所以这个Huge Page并不是可执行内存。

在位 [21:51) 上应用位掩码可以得到大页的物理地址:

gef➤  p/x 0x8000000008c001e3 & ~((1ull<<21)-1) & ((1ull<<51)-1)
$56 = 0x8c00000

现在我们需要对基于 2MB 页面对齐的虚拟地址应用掩码,以获取大页的偏移量。

2MB 相当于 1<<21 ,因此应用 (1ull<<21)-1 位掩码将为我们提供偏移量:

gef➤  p/x 0xffffffff88c07da8 & ((1ull<<21)-1)
$57 = 0x7da8

现在将此偏移量添加到 2MB 大页的基地址中将得到与我们开始的虚拟地址关联的物理地址:

gef➤  p/x 0x8c00000 + 0x7da8
$58 = 0x8c07da8

看起来虚拟地址: 0xffffffff88c07da8 的物理地址为: 0x8c07da8 !

检查

有几种方法可以测试我们的页面行走是否正确,一个简单的检查是将内存转储到虚拟地址和物理地址并进行比较,如果它们看起来相同,我们可能是对的:
Physical

gef➤  monitor xp/10gx 0x8c07da8
0000000008c07da8: 0xffffffff810effb6 0xffffffff88c07dc0
0000000008c07db8: 0xffffffff810f3685 0xffffffff88c07de0
0000000008c07dc8: 0xffffffff8737dce3 0xffffffff88c3ea80
0000000008c07dd8: 0xdffffc0000000000 0xffffffff88c07e98
0000000008c07de8: 0xffffffff8138ab1e 0x0000000000000000

Virtual

gef➤  x/10gx 0xffffffff88c07da8
0xffffffff88c07da8:	0xffffffff810effb6	0xffffffff88c07dc0
0xffffffff88c07db8:	0xffffffff810f3685	0xffffffff88c07de0
0xffffffff88c07dc8:	0xffffffff8737dce3	0xffffffff88c3ea80
0xffffffff88c07dd8:	0xdffffc0000000000	0xffffffff88c07e98
0xffffffff88c07de8:	0xffffffff8138ab1e	0x0000000000000000

另一种检查方法是使用 QEMU 监视器向 gdb 公开的 monitor gva2gpa (guest virtual address to guest physical address)命令:

gef➤  monitor gva2gpa 0xffffffff88c07da8
gpa: 0x8c07da8

假设 QEMU 正确地进行地址转换(可能是一个合理的假设),那么看起来我们已经双重确认了我们的页面遍历是成功的!

这篇关于理解x86_64 Paging(Page Map Level 4)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/610371

相关文章

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是

Collection List Set Map的区别和联系

Collection List Set Map的区别和联系 这些都代表了Java中的集合,这里主要从其元素是否有序,是否可重复来进行区别记忆,以便恰当地使用,当然还存在同步方面的差异,见上一篇相关文章。 有序否 允许元素重复否 Collection 否 是 List 是 是 Set AbstractSet 否

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

分布式系统的个人理解小结

分布式系统:分的微小服务,以小而独立的业务为单位,形成子系统。 然后分布式系统中需要有统一的调用,形成大的聚合服务。 同时,微服务群,需要有交流(通讯,注册中心,同步,异步),有管理(监控,调度)。 对外服务,需要有控制的对外开发,安全网关。