mer*_*011 5 linux performance x86 assembly
我试图rdmsr在用户模式下执行特权指令,我希望得到某种特权错误,但我得到一个段错误.我检查了asm,我装0x186成ecx,这被认为是PERFEVTSEL0,基于上手动,1171页.
segfault的原因是什么,如何修改下面的代码来修复它?
我想在破解内核模块之前解决这个问题,因为我不希望这个段错误炸毁我的内核.
更新:我正在运行Intel(R) Xeon(R) CPU X3470.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sched.h>
#include <assert.h>
uint64_t
read_msr(int ecx)
{
unsigned int a, d;
__asm __volatile("rdmsr" : "=a"(a), "=d"(d) : "c"(ecx));
return ((uint64_t)a) | (((uint64_t)d) << 32);
}
int main(int ac, char **av)
{
uint64_t start, end;
cpu_set_t cpuset;
unsigned int c = 0x186;
int i = 0;
CPU_ZERO(&cpuset);
CPU_SET(i, &cpuset);
assert(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0);
printf("%lu\n", read_msr(c));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我将尝试回答的问题是:为什么上面的代码SIGSEGV虽然SIGILL没有内存错误,但导致的不是非法指令(从非特权用户调用的特权指令)?
我也希望得到一个SIGILLwithsi_code ILL_PRVOPC而不是段错误。你的问题已经有 3 年历史了,今天,我偶然发现了同样的行为。我也很失望:-(
段错误的原因是什么
原因似乎是 Linux 内核代码决定发送SIGSEGV. 这是负责的函数:
http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/kernel/traps.c#L487
看一下该函数的最后一行。
在您的后续问题中,您获得了其他汇编指令的列表,这些指令会传播到SIGSEGV用户空间,尽管它们实际上是一般保护错误。我发现你的问题是因为我触发了该行为cli。
我该如何修改下面的代码来修复它?
从 Linux 内核 4.9 开始,我不知道有任何可靠的方法来区分内存错误(我期望的是SIGSEGV)和来自用户空间的特权指令错误。
可能有一种非常老套且不可移植的方法来区分这些情况。当特权指令导致 a 时SIGSEGV, 会被设置为部分siginfo_t si_code中未直接列出的值。记录的值为, , ,但我在系统上得到 (0x80)。根据手册页,这是一个代码“可以放置在任何信号的 si_code 中”。在 strace 中,你会看到. 负责的内核代码在这里。SIGSEGVman 2 sigactionSEGV_MAPERRSEGV_ACCERRSEGV_PKUERRSI_KERNELSI_KERNELSIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0}
也可以通过 grep 查找dmesg该字符串。
请永远不要使用这两种方法来区分生产系统上的 GPF 和内存错误。
针对您的代码的具体解决方案:只是不要rdmsr从用户空间运行。但如果您正在寻找一种通用方法来找出程序收到SIGSEGV.