#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include static volatile sig_atomic_t got_sigbus = 0; static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), int flags) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = handler; sa.sa_flags = SA_SIGINFO | flags; sigemptyset(&sa.sa_mask); if (sigaction(sig, &sa, 0)) err(1, "sigaction"); } #ifdef __x86_64__ # define REG_IP REG_RIP #else # define REG_IP REG_EIP #endif extern const char rdpmc_insn[]; static void sigsegv(int sig, siginfo_t *si, void *ctx_void) { ucontext_t *ctx = (ucontext_t*)ctx_void; if (ctx->uc_mcontext.gregs[REG_IP] == (unsigned long)rdpmc_insn) { got_sigbus = 1; ctx->uc_mcontext.gregs[REG_IP] += 2; } else { raise(SIGABRT); } } __attribute__((noinline, noclone)) static void do_rdpmc(unsigned int ecx) { unsigned int eax, edx; got_sigbus = 0; asm volatile ("rdpmc_insn: rdpmc" : "=a" (eax), "=d" (edx) : "c" (ecx)); if (got_sigbus) return; printf("PMC 0x%08X: 0x%08X%08X\n", ecx, edx, eax); } static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); } static void* map_one(void) { struct perf_event_attr pe; int fd; memset(&pe, 0, sizeof(struct perf_event_attr)); pe.type = PERF_TYPE_HARDWARE; pe.size = sizeof(struct perf_event_attr); pe.config = PERF_COUNT_HW_INSTRUCTIONS; pe.disabled = 0; pe.exclude_kernel = 1; pe.exclude_hv = 1; fd = perf_event_open(&pe, getpid(), -1, -1, 0); if (fd == -1) err(1, "perf_event_open"); return mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0); } int main(int argc, char **argv) { if (argc == 2 && !strcmp(argv[1], "try_harder")) { map_one(); munmap(map_one(), 4096); } sethandler(SIGSEGV, sigsegv, 0); for (unsigned int i = 0; i < 4096; i++) do_rdpmc(i); for (unsigned int i = 0; i < 4096; i++) do_rdpmc(i | (1<<30)); return 0; }