内核 sys_call_table 地址与 system.map 中指定的地址不匹配

Kernel sys_call_table address does not match address specified in system.map

我正在尝试温习 C,所以我一直在研究 linux 内核的系统调用 table(在 3.13.0-32-generic 上)。我在网上找到了一个资源,它使用以下函数搜索系统调用 table,我在 LKM 中将其加载到内核中:

static uint64_t **aquire_sys_call_table(void)
{
    uint64_t offset = PAGE_OFFSET;
    uint64_t **sct;

    while (offset < ULLONG_MAX) {
        sct = (uint64_t **)offset;

        if (sct[__NR_close] == (uint64_t *) sys_close) {
            printk("\nsys_call_table found at address: 0x%p\n", sys_call_table);
            return sct;
        }

        offset += sizeof(void *);
    }

    return NULL;
}

功能有效。我可以使用地址 returns 来操纵系统调用 table。我不明白的是为什么这个函数返回的地址与 /boot/System.map-(KERNEL)

中的地址不匹配

函数输出如下:

sys_call_table found at address: 0xffff880001801400

这是我搜索时得到的结果 system.map

$ sudo cat /boot/System.map-3.13.0-32-generic | grep sys_call_table 
  ffffffff81801400 R sys_call_table
  ffffffff81809cc0 R ia32_sys_call_table

为什么这两个地址不匹配?我的理解是模块运行在内核的地址space,所以系统调用的地址table应该是一样的。

通过虚拟内存映射的魔力,你使用的地址取决于你所在的位置。符号 table 文件 System.map 用于帮助将 gdb 或崩溃实用程序附加到 运行 系统。在内核里面,嗯,在内核里面。

您可能还有一个 /proc/kallsym 文件以获得更多值:)

两个虚拟地址具有相同的物理地址。

来自Documentation/x86/x86_64/mm.txt

<previous description obsolete, deleted>

Virtual memory map with 4 level page tables:

0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm
hole caused by [48:63] sign extension
ffff800000000000 - ffff87ffffffffff (=43 bits) guard hole, reserved for hypervisor
ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all phys. memory
ffffc80000000000 - ffffc8ffffffffff (=40 bits) hole
ffffc90000000000 - ffffe8ffffffffff (=45 bits) vmalloc/ioremap space
ffffe90000000000 - ffffe9ffffffffff (=40 bits) hole
ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)
... unused hole ...
ffffec0000000000 - fffffc0000000000 (=44 bits) kasan shadow memory (16TB)
... unused hole ...
ffffff0000000000 - ffffff7fffffffff (=39 bits) %esp fixup stacks
... unused hole ...
ffffffff80000000 - ffffffffa0000000 (=512 MB)  kernel text mapping, from phys 0
ffffffffa0000000 - ffffffffff5fffff (=1525 MB) module mapping space
ffffffffff600000 - ffffffffffdfffff (=8 MB) vsyscalls
ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole

The direct mapping covers all memory in the system up to the highest
memory address (this means in some cases it can also include PCI memory
holes).

vmalloc space is lazily synchronized into the different PML4 pages of
the processes using the page fault handler, with init_level4_pgt as
reference.

Current X86-64 implementations only support 40 bits of address space,
but we support up to 46 bits. This expands into MBZ space in the page tables.

->trampoline_pgd:

We map EFI runtime services in the aforementioned PGD in the virtual
range of 64Gb (arbitrarily set, can be raised if needed)

0xffffffef00000000 - 0xffffffff00000000

-Andi Kleen, Jul 2004

我们知道虚拟地址spaceffff880000000000-ffffc7ffffffffff是所有物理内存的直接映射。当内核要访问所有物理内存时,它使用直接映射。这也是您用于搜索的内容。

ffffffff80000000-ffffffffa0000000是内核文本映射。当内核代码执行时,rip寄存器使用内核文本映射。

arch/x86/include/asm/page_64.h中可以得到虚拟地址和物理地址的关系

static inline unsigned long __phys_addr_nodebug(unsigned long x)
{
    unsigned long y = x - __START_KERNEL_map;

    /* use the carry flag to determine if x was < __START_KERNEL_map */
    x = y + ((x > y) ? phys_base : (__START_KERNEL_map - PAGE_OFFSET));

    return x;
}

// arch/x86/include/asm/page_types.h
#define PAGE_OFFSET     ((unsigned long)__PAGE_OFFSET)
// arch/x86/include/asm/page_64_types.h
#define __START_KERNEL_map  _AC(0xffffffff80000000, UL)
#define __PAGE_OFFSET           _AC(0xffff880000000000, UL)


至于上面问题提到的地址:

函数打印的内容,

sys_call_table found at address: 0xffff880001801400

system.map 给予什么,

$ sudo cat /boot/System.map-3.13.0-32-generic | grep sys_call_table 
  ffffffff81801400 R sys_call_table
  ffffffff81809cc0 R ia32_sys_call_table

它们都解析到相同的物理地址。

virt->phys 转换以 'direct' 映射区域和 'kernel text' 映射区域中的相应地址解析为相同物理地址的方式发生。

只有 root 可以显示 /proc/kallsyms 文件中的地址!它很少被禁用,但如果它被禁用,您可以启用它。但是 System.mapkallsyms 文件中相同 sys_call 的地址不同。

如果一个人使用的是自己构建的内核,那么 System.map 更可取,但如果您使用的是预构建的内核(就像我们大多数人所做的那样),那么 kallsyms 是正确的为你安排位置!