Search code examples
linuxassemblylinux-kernelpage-tables

What is exactly happening in Linux page table pointers dereferencing?


I’m trying to investigate page table walk in Linux kernel. I used standard way to walk through page table to find PFN (just for example, not actual code):

    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);

The system is

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

CPU @ 3.40GHz

OS: Linux Fedora release 22 (Twenty Two) Kernel: 4.4.4-200.fc22.x86_64

I’m trying to understand how pgd pointer dereferenced to pud pointer. I put simple code into getPud function:

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

And try to disassemble it by 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)

My assembler knowledge is not enough to understand constructions like callq *0x0

Could someone please shed some light on what is going on in getPud?

Thank you

Sergey

Update 1

I used objdump to disassemble LKM (cpes.ko) module I created to walk through page table.

>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)

Solution

  • You're looking at disassembly of the .o, right? Not the final linked binary? The 0x0 address is just a placeholder that the linker will fill in. (This is a memory-indirect call through a static/global function pointer). pud_offset is getting inlined into your function.

    Try objdump -dr or -dR to show the relocation entries in with the disassembly output.

    Or better, look at gcc -S output to have symbolic names. (-fverbose-asm is sometimes helpful). Figure out the command line that make builds your file with, and modify it to use -S -o- instead of -c