如何从程序中访问控制寄存器cr0、cr2、cr3?出现分段错误。

14

我编写了一个程序,试图读取和写入控制寄存器。

该程序编译正常,但是当内联汇编即将执行时,会产生分段错误。

代码:

void instructions(int val)
{
    int i;
    int value;
    for(i = 0; i < val; i++)
         __asm__("mov %cr0, %eax");
}
我使用了GDB并逐步执行每个汇编指令,发现在mov %cr0,%eax处出现了段错误。请问有人知道这是什么问题吗?

1
@DavidWohlferd 我是这样的。或许有一天他会回来。但我并不会等待他的回答。在 Stack Overflow 上的问题永远都是有用的。我经常回答那些问题,有时也会得到声望值。 :-) - Ciro Santilli OurBigBook.com
@DavidWohlferd 我发现这里说的一切都很有用 (: - Roi
1个回答

21

引用自Intel® 64和IA-32架构软件开发人员手册3-650 Vol. 2A关于控制寄存器的移动:

只有当前特权级为0时,才能执行此指令。

这意味着该指令只能在内核模式下执行。

一个最小的内核模块,记录cr0、cr2和cr3的内容,可能看起来像这样(32位代码路径未经测试):

/* hello.c */
#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void)
{
#ifdef __x86_64__
    u64 cr0, cr2, cr3;
    __asm__ __volatile__ (
        "mov %%cr0, %%rax\n\t"
        "mov %%eax, %0\n\t"
        "mov %%cr2, %%rax\n\t"
        "mov %%eax, %1\n\t"
        "mov %%cr3, %%rax\n\t"
        "mov %%eax, %2\n\t"
    : "=m" (cr0), "=m" (cr2), "=m" (cr3)
    : /* no input */
    : "%rax"
    );
#elif defined(__i386__)
    u32 cr0, cr2, cr3;
    __asm__ __volatile__ (
        "mov %%cr0, %%eax\n\t"
        "mov %%eax, %0\n\t"
        "mov %%cr2, %%eax\n\t"
        "mov %%eax, %1\n\t"
        "mov %%cr3, %%eax\n\t"
        "mov %%eax, %2\n\t"
    : "=m" (cr0), "=m" (cr2), "=m" (cr3)
    : /* no input */
    : "%eax"
    );
#endif
    printk(KERN_INFO "cr0 = 0x%8.8X\n", cr0);
    printk(KERN_INFO "cr2 = 0x%8.8X\n", cr2);
    printk(KERN_INFO "cr3 = 0x%8.8X\n", cr3);
    return 0;
}

void cleanup_module(void)
{
}

 

# Makefile

obj-m += hello.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

test: all
    sudo insmod ./hello.ko
    sudo rmmod hello
    dmesg | tail

你是不是指 u64?至少在Intel 64的手册中,cr3被记录在位0到63。 - corny
@corny:这只是一个快速的示例,展示如何访问控制寄存器,所以为了简单起见,即使在64位模式下,我只打印低32位。我认为在长模式下所有控制寄存器都是64位的,如果您实际上正在使用内容,则绝对需要CR2 / CR3的所有位。 - user786653
在64位模式下,所有控制寄存器都是64位的。请参阅英特尔® 64和IA-32体系结构软件开发人员手册第3A卷第2.5节“控制寄存器”。我编辑了您的答案,让我们删除我们的评论,只保留您改进后的答案。 - corny
1
我目前没有轻松检查代码的方法,但我认为在64位模式下格式化字符串应该是%016lx(或者如果将cr0 / cr2 / cr3转换为void *,则可以使用%p)。 - user786653
由于这是一个64位寄存器,因此应为%016llx(附加长)。 - Phidelux
@user786653 - 你能演示如何写入 cr0 吗?我可以读取它,但在写入时出现分段错误。 - Shuzheng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接