|
@@ -38,7 +38,7 @@
|
|
|
#include <linux/string.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/preempt.h>
|
|
|
-#include <linux/moduleloader.h>
|
|
|
+
|
|
|
#include <asm/cacheflush.h>
|
|
|
#include <asm/pgtable.h>
|
|
|
#include <asm/kdebug.h>
|
|
@@ -51,8 +51,6 @@ static struct kprobe *kprobe_prev;
|
|
|
static unsigned long kprobe_status_prev, kprobe_old_rflags_prev, kprobe_saved_rflags_prev;
|
|
|
static struct pt_regs jprobe_saved_regs;
|
|
|
static long *jprobe_saved_rsp;
|
|
|
-static kprobe_opcode_t *get_insn_slot(void);
|
|
|
-static void free_insn_slot(kprobe_opcode_t *slot);
|
|
|
void jprobe_return_end(void);
|
|
|
|
|
|
/* copy of the kernel stack at the probe fire time */
|
|
@@ -681,112 +679,3 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
-
|
|
|
-/*
|
|
|
- * kprobe->ainsn.insn points to the copy of the instruction to be single-stepped.
|
|
|
- * By default on x86_64, pages we get from kmalloc or vmalloc are not
|
|
|
- * executable. Single-stepping an instruction on such a page yields an
|
|
|
- * oops. So instead of storing the instruction copies in their respective
|
|
|
- * kprobe objects, we allocate a page, map it executable, and store all the
|
|
|
- * instruction copies there. (We can allocate additional pages if somebody
|
|
|
- * inserts a huge number of probes.) Each page can hold up to INSNS_PER_PAGE
|
|
|
- * instruction slots, each of which is MAX_INSN_SIZE*sizeof(kprobe_opcode_t)
|
|
|
- * bytes.
|
|
|
- */
|
|
|
-#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE*sizeof(kprobe_opcode_t)))
|
|
|
-struct kprobe_insn_page {
|
|
|
- struct hlist_node hlist;
|
|
|
- kprobe_opcode_t *insns; /* page of instruction slots */
|
|
|
- char slot_used[INSNS_PER_PAGE];
|
|
|
- int nused;
|
|
|
-};
|
|
|
-
|
|
|
-static struct hlist_head kprobe_insn_pages;
|
|
|
-
|
|
|
-/**
|
|
|
- * get_insn_slot() - Find a slot on an executable page for an instruction.
|
|
|
- * We allocate an executable page if there's no room on existing ones.
|
|
|
- */
|
|
|
-static kprobe_opcode_t *get_insn_slot(void)
|
|
|
-{
|
|
|
- struct kprobe_insn_page *kip;
|
|
|
- struct hlist_node *pos;
|
|
|
-
|
|
|
- hlist_for_each(pos, &kprobe_insn_pages) {
|
|
|
- kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
|
|
|
- if (kip->nused < INSNS_PER_PAGE) {
|
|
|
- int i;
|
|
|
- for (i = 0; i < INSNS_PER_PAGE; i++) {
|
|
|
- if (!kip->slot_used[i]) {
|
|
|
- kip->slot_used[i] = 1;
|
|
|
- kip->nused++;
|
|
|
- return kip->insns + (i*MAX_INSN_SIZE);
|
|
|
- }
|
|
|
- }
|
|
|
- /* Surprise! No unused slots. Fix kip->nused. */
|
|
|
- kip->nused = INSNS_PER_PAGE;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* All out of space. Need to allocate a new page. Use slot 0.*/
|
|
|
- kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
|
|
|
- if (!kip) {
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * For the %rip-relative displacement fixups to be doable, we
|
|
|
- * need our instruction copy to be within +/- 2GB of any data it
|
|
|
- * might access via %rip. That is, within 2GB of where the
|
|
|
- * kernel image and loaded module images reside. So we allocate
|
|
|
- * a page in the module loading area.
|
|
|
- */
|
|
|
- kip->insns = module_alloc(PAGE_SIZE);
|
|
|
- if (!kip->insns) {
|
|
|
- kfree(kip);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
- INIT_HLIST_NODE(&kip->hlist);
|
|
|
- hlist_add_head(&kip->hlist, &kprobe_insn_pages);
|
|
|
- memset(kip->slot_used, 0, INSNS_PER_PAGE);
|
|
|
- kip->slot_used[0] = 1;
|
|
|
- kip->nused = 1;
|
|
|
- return kip->insns;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * free_insn_slot() - Free instruction slot obtained from get_insn_slot().
|
|
|
- */
|
|
|
-static void free_insn_slot(kprobe_opcode_t *slot)
|
|
|
-{
|
|
|
- struct kprobe_insn_page *kip;
|
|
|
- struct hlist_node *pos;
|
|
|
-
|
|
|
- hlist_for_each(pos, &kprobe_insn_pages) {
|
|
|
- kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
|
|
|
- if (kip->insns <= slot
|
|
|
- && slot < kip->insns+(INSNS_PER_PAGE*MAX_INSN_SIZE)) {
|
|
|
- int i = (slot - kip->insns) / MAX_INSN_SIZE;
|
|
|
- kip->slot_used[i] = 0;
|
|
|
- kip->nused--;
|
|
|
- if (kip->nused == 0) {
|
|
|
- /*
|
|
|
- * Page is no longer in use. Free it unless
|
|
|
- * it's the last one. We keep the last one
|
|
|
- * so as not to have to set it up again the
|
|
|
- * next time somebody inserts a probe.
|
|
|
- */
|
|
|
- hlist_del(&kip->hlist);
|
|
|
- if (hlist_empty(&kprobe_insn_pages)) {
|
|
|
- INIT_HLIST_NODE(&kip->hlist);
|
|
|
- hlist_add_head(&kip->hlist,
|
|
|
- &kprobe_insn_pages);
|
|
|
- } else {
|
|
|
- module_free(NULL, kip->insns);
|
|
|
- kfree(kip);
|
|
|
- }
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|