【问题标题】:What is exactly happening in Linux page table pointers dereferencing?Linux页表指针取消引用到底发生了什么?
【发布时间】:2016-03-25 03:50:51
【问题描述】:

我正在尝试研究 Linux 内核中的页表遍历。我使用标准方法遍历页表以查找 PFN(仅举例,不是实际代码):

    pgd_t *pgd; pte_t *ptep; pte_t pte; pud_t *pud; pmd_t *pmd;
    struct page *pagePtr = NULL;
    struct mm_struct *mm = current->mm;
    pgd = pgd_offset(mm, addr);
    pud = getPud(pgd, addr);
    pmd = pmd_offset(pud, addr);
    ptep = pte_offset_map(pmd, addr);
    size_t pfn = pte_pfn(pte);

系统是

CPU:Intel(R) Core(TM) i7-3770

CPU @ 3.40GHz

操作系统:Linux Fedora 发行版 22(二十二)内核:4.4.4-200.fc22.x86_64

我试图了解 pgd 指针是如何取消引用到 pud 指针的。我将简单的代码放入 getPud 函数中:

noinline pud_t *getPud(pgd_t *pgdPtr, unsigned long addr).
{
    return pud_offset(pgdPtr, addr);
}

并尝试通过objdump进行反汇编

00000000000000b0 <getPud>:
  b0:   e8 00 00 00 00          callq  b5 <getPud+0x5>
  b5:   55                      push   %rbp
  b6:   48 8b 3f                mov    (%rdi),%rdi
  b9:   48 89 e5                mov    %rsp,%rbp
  bc:   ff 14 25 00 00 00 00    callq  *0x0
  c3:   48 c1 ee 1b             shr    $0x1b,%rsi
  c7:   48 ba 00 00 00 00 00    movabs $0xffff880000000000,%rdx
  ce:   88 ff ff
  d1:   81 e6 f8 0f 00 00       and    $0xff8,%esi
  d7:   48 01 d6                add    %rdx,%rsi
  da:   48 ba 00 f0 ff ff ff    movabs $0x3ffffffff000,%rdx
  e1:   3f 00 00
  e4:   48 21 d0                and    %rdx,%rax
  e7:   48 01 f0                add    %rsi,%rax
  ea:   5d                      pop    %rbp
  eb:   c3                      retq
  ec:   0f 1f 40 00             nopl   0x0(%rax)

我的汇编知识不足以理解 callq *0x0 这样的结构

有人可以解释一下 getPud 中发生了什么吗?

谢谢

谢尔盖

更新 1

我使用 objdump 来反汇编我创建的 LKM (cpes.ko) 模块以遍历页表。

>objdump -dr ./cpes.ko

./cpes.ko:     file format elf64-x86-64
Disassembly of section .text:
00000000000000b0 <getPud>:
  b0:   e8 00 00 00 00          callq  b5 <getPud+0x5>
                        b1: R_X86_64_PC32       __fentry__-0x4
  b5:   55                      push   %rbp
  b6:   48 8b 3f                mov    (%rdi),%rdi
  b9:   48 89 e5                mov    %rsp,%rbp
  bc:   ff 14 25 00 00 00 00    callq  *0x0
                        bf: R_X86_64_32S        pv_mmu_ops+0xf8
  c3:   48 c1 ee 1b             shr    $0x1b,%rsi
  c7:   48 ba 00 00 00 00 00    movabs $0xffff880000000000,%rdx
  ce:   88 ff ff
  d1:   81 e6 f8 0f 00 00       and    $0xff8,%esi
  d7:   48 01 d6                add    %rdx,%rsi
  da:   48 ba 00 f0 ff ff ff    movabs $0x3ffffffff000,%rdx
  e1:   3f 00 00
  e4:   48 21 d0                and    %rdx,%rax
  e7:   48 01 f0                add    %rsi,%rax
  ea:   5d                      pop    %rbp
  eb:   c3                      retq
  ec:   0f 1f 40 00             nopl   0x0(%rax)

【问题讨论】:

    标签: linux assembly linux-kernel page-tables


    【解决方案1】:

    您正在查看.o 的反汇编,对吗?不是最终链接的二进制文件? 0x0 地址只是链接器将填充的占位符。(这是通过静态/全局函数指针的内存间接调用)。 pud_offset 正在内联到您的函数中。

    尝试objdump -dr-dR 在反汇编输出中显示重定位条目。

    或者更好的是,查看gcc -S 输出以获取符号名称。 (-fverbose-asm 有时很有帮助)。找出make 用于构建文件的命令行,并将其修改为使用-S -o- 而不是-c

    【讨论】:

    • 感谢您的评论。我为带有反汇编细节的开始主题制作了 update。这是否意味着call *0x0 意味着call pv_mmu_ops?这个pv_mmu_ops 做了什么?为什么内核使用如此复杂的程序来进行解引用?谢尔盖
    • @shs_sf:注意 0xf8pv_mmu_ops 的偏移量,这是重定位的一部分。函数指针的结构在内核中非常常见。例如文件系统通过调用向跟踪所有可用文件系统的内核部分注册函数指针结构的函数来注册自己以供​​内核的其余部分使用。在该 FS 类型的挂载上的 FS 操作会导致通过这些函数指针进行调用。
    【解决方案2】:

    最好查找来源code。如您所见,有一些platform-specific address 魔术。

    【讨论】:

      猜你喜欢
      • 2011-04-29
      • 1970-01-01
      • 1970-01-01
      • 2010-11-02
      • 2013-04-29
      • 1970-01-01
      • 1970-01-01
      • 2020-11-15
      相关资源
      最近更新 更多