Esempio n. 1
0
static int
fbt_prov_entry(pf_info_t *infp, instr_t *instr, int size, int modrm)
{
	fbt_probe_t *fbt;

	/***********************************************/
	/*   Avoid patching a patched probe point.     */
	/***********************************************/
	if (*instr == 0xcc)
		return 1;

	/***********************************************/
	/*   This  is temporary. Because of the issue  */
	/*   of   %RIP   relative   addressing  modes  */
	/*   potentially not being addressable in our  */
	/*   single-step  jump  buffer, disable those  */
	/*   probes which look like one of these.      */
	/***********************************************/
	if (modrm >= 0 && (instr[modrm] & 0xc7) == 0x05) 
		return 1;

	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
	fbt->fbtp_name = infp->name;

	fbt->fbtp_id = dtrace_probe_create(fbt_id, infp->modname,
	    infp->name, FBT_ENTRY, 3, fbt);
	num_probes++;
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = infp->mp; // ctl;
	fbt->fbtp_loadcnt = get_refcount(infp->mp);
	fbt->fbtp_rval = DTRACE_INVOP_ANY;
	/***********************************************/
	/*   Save  potential overwrite of instruction  */
	/*   and  length,  because  we  will need the  */
	/*   entire  instruction  when we single step  */
	/*   over it.				       */
	/***********************************************/
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_inslen = size;
	fbt->fbtp_type = 0; /* entry */
//if (modrm >= 0 && (instr[modrm] & 0xc7) == 0x05) printk("modrm %s %p rm=%d\n", name, instr, modrm);
	fbt->fbtp_modrm = modrm;
	fbt->fbtp_patchval = FBT_PATCHVAL;

	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt->fbtp_symndx = infp->symndx;
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	infp->pmp->fbt_nentries++;
	infp->retptr = NULL;

	return 1;
}
Esempio n. 2
0
/*ARGSUSED*/
static void
fbt_destroy(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
	fbt_probe_t *fbt = parg, *next, *hash, *last;
	int ndx;

	do {
		/*
		 * Now we need to remove this probe from the fbt_probetab.
		 */
		ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
		last = NULL;
		hash = fbt_probetab[ndx];

		while (hash != fbt) {
			ASSERT(hash != NULL);
			last = hash;
			hash = hash->fbtp_hashnext;
		}

		if (last != NULL) {
			last->fbtp_hashnext = fbt->fbtp_hashnext;
		} else {
			fbt_probetab[ndx] = fbt->fbtp_hashnext;
		}

		next = fbt->fbtp_next;
		kmem_free(fbt, sizeof (fbt_probe_t));

		fbt = next;
	} while (fbt != NULL);
}
Esempio n. 3
0
static void
fbt_disable(void *arg, dtrace_id_t id, void *parg)
{
	fbt_probe_t *fbt = parg, *hash;
	modctl_t *ctl = fbt->fbtp_ctl;

	ASSERT(ctl->nenabled > 0);
	ctl->nenabled--;

	if ((ctl->loadcnt != fbt->fbtp_loadcnt))
		return;

	for (; fbt != NULL; fbt = fbt->fbtp_probenext) {
		fbt->fbtp_enabled--;

		for (hash = fbt_probetab[FBT_ADDR2NDX(fbt->fbtp_patchpoint)];
		    hash != NULL; hash = hash->fbtp_hashnext) {
			if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
				for (; hash != NULL; hash = hash->fbtp_tracenext)
					if (hash->fbtp_enabled > 0)
						break;
				break;
			}
		}
		if (hash == NULL)
			fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
	}
}
Esempio n. 4
0
static void
fbt_destroy_one(fbt_probe_t *fbt)
{
	fbt_probe_t *hash, *hashprev, *next;
	int ndx;

	ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
	for (hash = fbt_probetab[ndx], hashprev = NULL; hash != NULL;
	    hashprev = hash, hash = hash->fbtp_hashnext) {
		if (hash == fbt) {
			if ((next = fbt->fbtp_tracenext) != NULL)
				next->fbtp_hashnext = hash->fbtp_hashnext;
			else
				next = hash->fbtp_hashnext;
			if (hashprev != NULL)
				hashprev->fbtp_hashnext = next;
			else
				fbt_probetab[ndx] = next;
			goto free;
		} else if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
			for (next = hash; next->fbtp_tracenext != NULL;
			    next = next->fbtp_tracenext) {
				if (fbt == next->fbtp_tracenext) {
					next->fbtp_tracenext =
					    fbt->fbtp_tracenext;
					goto free;
				}
			}
		}
	}
	panic("probe %p not found in hash table", fbt);
free:
	free(fbt, M_FBT);
}
Esempio n. 5
0
int
fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
{
	struct trapframe *frame = (struct trapframe *)stack;
	solaris_cpu_t *cpu = &solaris_cpu[curcpu];
	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
	register_t fifthparam;

	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
			cpu->cpu_dtrace_caller = addr;

			/* Get 5th parameter from stack */
			DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
			fifthparam = *(register_t *)frame->tf_usr_sp;
			DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR);

			dtrace_probe(fbt->fbtp_id, frame->tf_r0,
			    frame->tf_r1, frame->tf_r2,
			    frame->tf_r3, fifthparam);

			cpu->cpu_dtrace_caller = 0;

			return (fbt->fbtp_rval | (fbt->fbtp_savedval << DTRACE_INVOP_SHIFT));
		}
	}

	return (0);
}
Esempio n. 6
0
static int
fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
{
	solaris_cpu_t *cpu = &solaris_cpu[curcpu];
	uintptr_t stack0, stack1, stack2, stack3, stack4;
	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];

	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
			fbt->fbtp_invop_cnt++;
			if (fbt->fbtp_roffset == 0) {
				int i = 0;
				/*
				 * When accessing the arguments on the stack,
				 * we must protect against accessing beyond
				 * the stack.  We can safely set NOFAULT here
				 * -- we know that interrupts are already
				 * disabled.
				 */
				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
				cpu->cpu_dtrace_caller = stack[i++];
				stack0 = stack[i++];
				stack1 = stack[i++];
				stack2 = stack[i++];
				stack3 = stack[i++];
				stack4 = stack[i++];
				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
				    CPU_DTRACE_BADADDR);

				dtrace_probe(fbt->fbtp_id, stack0, stack1,
				    stack2, stack3, stack4);

				cpu->cpu_dtrace_caller = 0;
			} else {
#ifdef __amd64__
				/*
				 * On amd64, we instrument the ret, not the
				 * leave.  We therefore need to set the caller
				 * to assure that the top frame of a stack()
				 * action is correct.
				 */
				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
				cpu->cpu_dtrace_caller = stack[0];
				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
				    CPU_DTRACE_BADADDR);
#endif

				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
				    rval, 0, 0, 0);
				cpu->cpu_dtrace_caller = 0;
			}

			return (fbt->fbtp_rval);
		}
	}

	return (0);
}
Esempio n. 7
0
int *
fbt_get_instr_buf(instr_t *addr)
{
	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
		if (fbt->fbtp_patchpoint == addr)
			return fbt->fbtp_instr_buf;
	}
	return NULL;
}
Esempio n. 8
0
static int
fbt_is_patched(char *name, instr_t *addr)
{
	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];

	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
		if (fbt->fbtp_patchpoint == addr) {
			dtrace_printf("fbt:dup patch: %p %s\n", addr, name);
			return 1;
		}
	}
	return 0;
}
Esempio n. 9
0
int
fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
{
	struct trapframe *frame = (struct trapframe *)stack;
	solaris_cpu_t *cpu = &solaris_cpu[curcpu];
	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
	uintptr_t tmp;

	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
			fbt->fbtp_invop_cnt++;
			if (fbt->fbtp_roffset == 0) {
				cpu->cpu_dtrace_caller = addr;

				dtrace_probe(fbt->fbtp_id, frame->fixreg[3],
				    frame->fixreg[4], frame->fixreg[5],
				    frame->fixreg[6], frame->fixreg[7]);

				cpu->cpu_dtrace_caller = 0;
			} else {

				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
				    rval, 0, 0, 0);
				/*
				 * The caller doesn't have the fbt item, so
				 * fixup tail calls here.
				 */
				if (fbt->fbtp_rval == DTRACE_INVOP_JUMP) {
					frame->srr0 = (uintptr_t)fbt->fbtp_patchpoint;
					tmp = fbt->fbtp_savedval & FBT_BR_MASK;
					/* Sign extend. */
					if (tmp & 0x02000000)
#ifdef __powerpc64__
						tmp |= 0xfffffffffc000000ULL;
#else
						tmp |= 0xfc000000UL;
#endif
					frame->srr0 += tmp;
				}
				cpu->cpu_dtrace_caller = 0;
			}

			return (fbt->fbtp_rval);
		}
	}

	return (0);
}
Esempio n. 10
0
/*ARGSUSED*/
static void
fbt_destroy(void *arg, dtrace_id_t id, void *parg)
{
	fbt_probe_t *fbt = parg, *next, *hash, *last;
	struct modctl *ctl = fbt->fbtp_ctl;
	struct module *mp = ctl;
	int ndx;

	do {
//printk("refc=%d load=%d\n", get_refcount(mp), fbt->fbtp_loadcnt);
		if (mp != NULL && get_refcount(mp) == fbt->fbtp_loadcnt) {
			if ((get_refcount(mp) == fbt->fbtp_loadcnt &&
			    mp->state == MODULE_STATE_LIVE)) {
			    	par_module_t *pmp = par_alloc(PARD_FBT, mp, sizeof *pmp, NULL);
				if (pmp && --pmp->fbt_nentries == 0)
					par_free(PARD_FBT, pmp);
			}
		}

		/*
		 * Now we need to remove this probe from the fbt_probetab.
		 */
		ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
		last = NULL;
		hash = fbt_probetab[ndx];

		while (hash != fbt) {
			ASSERT(hash != NULL);
			last = hash;
			hash = hash->fbtp_hashnext;
		}

		if (last != NULL) {
			last->fbtp_hashnext = fbt->fbtp_hashnext;
		} else {
			fbt_probetab[ndx] = fbt->fbtp_hashnext;
		}

		next = fbt->fbtp_next;
		kmem_free(fbt, sizeof (fbt_probe_t));

		fbt = next;
	} while (fbt != NULL);
}
Esempio n. 11
0
/*ARGSUSED*/
static void
fbt_destroy(void *arg, dtrace_id_t id, void *parg)
{
	fbt_probe_t *fbt = parg, *next, *hash, *last;
	struct modctl *ctl = fbt->fbtp_ctl;
	int ndx;

	do {
		if (ctl != NULL && ctl->mod_loadcnt == fbt->fbtp_loadcnt) {
			if ((ctl->mod_loadcnt == fbt->fbtp_loadcnt &&
			    ctl->mod_loaded)) {
				((struct module *)
				    (ctl->mod_mp))->fbt_nentries--;
			}
		}

		/*
		 * Now we need to remove this probe from the fbt_probetab.
		 */
		ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
		last = NULL;
		hash = fbt_probetab[ndx];

		while (hash != fbt) {
			ASSERT(hash != NULL);
			last = hash;
			hash = hash->fbtp_hashnext;
		}

		if (last != NULL) {
			last->fbtp_hashnext = fbt->fbtp_hashnext;
		} else {
			fbt_probetab[ndx] = fbt->fbtp_hashnext;
		}

		next = fbt->fbtp_next;
		kmem_free(fbt, sizeof (fbt_probe_t));

		fbt = next;
	} while (fbt != NULL);
}
Esempio n. 12
0
static void
fbt_destroy(void *arg, dtrace_id_t id, void *parg)
{
	fbt_probe_t *fbt = parg, *next, *hash, *last;
	modctl_t *ctl;
	int ndx;

	do {
		ctl = fbt->fbtp_ctl;

		ctl->fbt_nentries--;

		/*
		 * Now we need to remove this probe from the fbt_probetab.
		 */
		ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
		last = NULL;
		hash = fbt_probetab[ndx];

		while (hash != fbt) {
			ASSERT(hash != NULL);
			last = hash;
			hash = hash->fbtp_hashnext;
		}

		if (last != NULL) {
			last->fbtp_hashnext = fbt->fbtp_hashnext;
		} else {
			fbt_probetab[ndx] = fbt->fbtp_hashnext;
		}

		next = fbt->fbtp_next;
		free(fbt, M_FBT);

		fbt = next;
	} while (fbt != NULL);
}
Esempio n. 13
0
int
fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
{
	solaris_cpu_t *cpu;
	fbt_probe_t *fbt;

	cpu = &solaris_cpu[curcpu];
	fbt = fbt_probetab[FBT_ADDR2NDX(addr)];

	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
			cpu->cpu_dtrace_caller = addr;

			dtrace_probe(fbt->fbtp_id, frame->a0,
			    frame->a1, frame->a2,
			    frame->a3, frame->a4);

			cpu->cpu_dtrace_caller = 0;
			return (fbt->fbtp_savedval);
		}
	}

	return (0);
}
Esempio n. 14
0
int
fbt_provide_module_function(linker_file_t lf, int symindx,
    linker_symval_t *symval, void *opaque)
{
	char *modname = opaque;
	const char *name = symval->name;
	fbt_probe_t *fbt, *retfbt;
	uint32_t *instr, *limit;
	int popm;

	if (strncmp(name, "dtrace_", 7) == 0 &&
	    strncmp(name, "dtrace_safe_", 12) != 0) {
		/*
		 * Anything beginning with "dtrace_" may be called
		 * from probe context unless it explicitly indicates
		 * that it won't be called from probe context by
		 * using the prefix "dtrace_safe_".
		 */
		return (0);
	}

	if (name[0] == '_' && name[1] == '_')
		return (0);

	instr = (uint32_t *)symval->value;
	limit = (uint32_t *)(symval->value + symval->size);

	/*
	 * va_arg functions has first instruction of
	 * sub sp, sp, #?
	 */
	if ((*instr & 0xfffff000) == FBT_SUBSP)
		instr++;

	/*
	 * check if insn is a pushm with LR
	 */
	if ((*instr & 0xffff0000) != FBT_PUSHM ||
	    (*instr & (1 << LR)) == 0)
		return (0);

	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;
	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
	    name, FBT_ENTRY, 2, fbt);
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = FBT_BREAKPOINT;
	fbt->fbtp_rval = DTRACE_INVOP_PUSHM;
	fbt->fbtp_symindx = symindx;

	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	popm = FBT_POPM | ((*instr) & 0x3FFF) | 0x8000;

	retfbt = NULL;
again:
	for (; instr < limit; instr++) {
		if (*instr == popm)
			break;
		else if ((*instr & 0xff000000) == FBT_JUMP) {
			uint32_t *target, *start;
			int offset;

			offset = (*instr & 0xffffff);
			offset <<= 8;
			offset /= 64;
			target = instr + (2 + offset);
			start = (uint32_t *)symval->value;
			if (target >= limit || target < start)
				break;
		}
	}

	if (instr >= limit)
		return (0);

	/*
	 * We have a winner!
	 */
	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;
	if (retfbt == NULL) {
		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
		    name, FBT_RETURN, 2, fbt);
	} else {
		retfbt->fbtp_next = fbt;
		fbt->fbtp_id = retfbt->fbtp_id;
	}
	retfbt = fbt;

	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_symindx = symindx;
	if ((*instr & 0xff000000) == FBT_JUMP)
		fbt->fbtp_rval = DTRACE_INVOP_B;
	else
		fbt->fbtp_rval = DTRACE_INVOP_POPM;
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = FBT_BREAKPOINT;
	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	instr++;
	goto again;
}
Esempio n. 15
0
static int
fbt_provide_module_function(linker_file_t lf, int symindx,
    linker_symval_t *symval, void *opaque)
{
	char *modname = opaque;
	const char *name = symval->name;
	fbt_probe_t *fbt, *retfbt;
	int j;
	int size;
	u_int8_t *instr, *limit;

	if (strncmp(name, "dtrace_", 7) == 0 &&
	    strncmp(name, "dtrace_safe_", 12) != 0) {
		/*
		 * Anything beginning with "dtrace_" may be called
		 * from probe context unless it explicitly indicates
		 * that it won't be called from probe context by
		 * using the prefix "dtrace_safe_".
		 */
		return (0);
	}

	if (name[0] == '_' && name[1] == '_')
		return (0);

	size = symval->size;

	instr = (u_int8_t *) symval->value;
	limit = (u_int8_t *) symval->value + symval->size;

#ifdef __amd64__
	while (instr < limit) {
		if (*instr == FBT_PUSHL_EBP)
			break;

		if ((size = dtrace_instr_size(instr)) <= 0)
			break;

		instr += size;
	}

	if (instr >= limit || *instr != FBT_PUSHL_EBP) {
		/*
		 * We either don't save the frame pointer in this
		 * function, or we ran into some disassembly
		 * screw-up.  Either way, we bail.
		 */
		return (0);
	}
#else
	if (instr[0] != FBT_PUSHL_EBP)
		return (0);

	if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
	    instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
	    !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
	    instr[2] == FBT_MOVL_ESP_EBP1_V1))
		return (0);
#endif

	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;
	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
	    name, FBT_ENTRY, 3, fbt);
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = FBT_PATCHVAL;
	fbt->fbtp_symindx = symindx;

	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	retfbt = NULL;
again:
	if (instr >= limit)
		return (0);

	/*
	 * If this disassembly fails, then we've likely walked off into
	 * a jump table or some other unsuitable area.  Bail out of the
	 * disassembly now.
	 */
	if ((size = dtrace_instr_size(instr)) <= 0)
		return (0);

#ifdef __amd64__
	/*
	 * We only instrument "ret" on amd64 -- we don't yet instrument
	 * ret imm16, largely because the compiler doesn't seem to
	 * (yet) emit them in the kernel...
	 */
	if (*instr != FBT_RET) {
		instr += size;
		goto again;
	}
#else
	if (!(size == 1 &&
	    (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
	    (*(instr + 1) == FBT_RET ||
	    *(instr + 1) == FBT_RET_IMM16))) {
		instr += size;
		goto again;
	}
#endif

	/*
	 * We (desperately) want to avoid erroneously instrumenting a
	 * jump table, especially given that our markers are pretty
	 * short:  two bytes on x86, and just one byte on amd64.  To
	 * determine if we're looking at a true instruction sequence
	 * or an inline jump table that happens to contain the same
	 * byte sequences, we resort to some heuristic sleeze:  we
	 * treat this instruction as being contained within a pointer,
	 * and see if that pointer points to within the body of the
	 * function.  If it does, we refuse to instrument it.
	 */
	for (j = 0; j < sizeof (uintptr_t); j++) {
		caddr_t check = (caddr_t) instr - j;
		uint8_t *ptr;

		if (check < symval->value)
			break;

		if (check + sizeof (caddr_t) > (caddr_t)limit)
			continue;

		ptr = *(uint8_t **)check;

		if (ptr >= (uint8_t *) symval->value && ptr < limit) {
			instr += size;
			goto again;
		}
	}

	/*
	 * We have a winner!
	 */
	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;

	if (retfbt == NULL) {
		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
		    name, FBT_RETURN, 3, fbt);
	} else {
		retfbt->fbtp_next = fbt;
		fbt->fbtp_id = retfbt->fbtp_id;
	}

	retfbt = fbt;
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_symindx = symindx;

#ifndef __amd64__
	if (*instr == FBT_POPL_EBP) {
		fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
	} else {
		ASSERT(*instr == FBT_LEAVE);
		fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
	}
	fbt->fbtp_roffset =
	    (uintptr_t)(instr - (uint8_t *) symval->value) + 1;

#else
	ASSERT(*instr == FBT_RET);
	fbt->fbtp_rval = DTRACE_INVOP_RET;
	fbt->fbtp_roffset =
	    (uintptr_t)(instr - (uint8_t *) symval->value);
#endif

	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = FBT_PATCHVAL;
	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	instr += size;
	goto again;
}
Esempio n. 16
0
static int
fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval, trap_instr_t *tinfo)
{
	uintptr_t stack0, stack1, stack2, stack3, stack4;
	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];

//HERE();
//int dtrace_here = 1;
//if (dtrace_here) printk("fbt_invop:addr=%lx stack=%p eax=%lx\n", addr, stack, (long) rval);
	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
//if (dtrace_here) printk("patchpoint: %p rval=%x\n", fbt->fbtp_patchpoint, fbt->fbtp_rval);
		if ((uintptr_t)fbt->fbtp_patchpoint != addr)
			continue;

		/***********************************************/
		/*   If  probe  is  not  enabled,  but  still  */
		/*   fired,  then it *might have* overran. Likely cause is  */
		/*   cpu cache consistency issue - some other  */
		/*   CPU  hasnt  seen  the  breakpoint  being  */
		/*   removed.  Flag  it  so  we  can  show in  */
		/*   /proc/dtrace/fbt.			       */
		/*   Additionally, another provider (eg prov)  */
		/*   is  sitting  on the same function, so we  */
		/*   have to broadcast to both/all providers.  */
		/***********************************************/
		if (!fbt->fbtp_enabled) {
			fbt->fbtp_overrun = TRUE;
		}

		/***********************************************/
		/*   Always  handle  the  probe - if we dont,  */
		/*   nobody  else  will  know what to do with  */
		/*   it. Its possible somebody else does want  */
		/*   it,  e.g.  INSTR,  but we cant know who.  */
		/*   Probably  need  to  call  all providers,  */
		/*   rather than just the first one.	       */
		/***********************************************/
		if (1) {
			tinfo->t_opcode = fbt->fbtp_savedval;
//printk("fbt: opc=%p %p\n", tinfo->t_opcode, fbt->fbtp_savedval);
			tinfo->t_inslen = fbt->fbtp_inslen;
			tinfo->t_modrm = fbt->fbtp_modrm;
			if (!tinfo->t_doprobe)
				return fbt->fbtp_rval;
			fbt->fbtp_fired++;
			if (fbt->fbtp_roffset == 0) {
				struct pt_regs *ptregs = (struct pt_regs *) stack;
				/*
				 * When accessing the arguments on the stack,
				 * we must protect against accessing beyond
				 * the stack.  We can safely set NOFAULT here
				 * -- we know that interrupts are already
				 * disabled.
				 */
				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
				CPU->cpu_dtrace_caller = ptregs->r_pc;
				stack0 = ptregs->c_arg0;
				stack1 = ptregs->c_arg1;
				stack2 = ptregs->c_arg2;
				stack3 = ptregs->c_arg3;
				stack4 = ptregs->c_arg4;
				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
				    CPU_DTRACE_BADADDR);

				dtrace_probe(fbt->fbtp_id, stack0, stack1,
				    stack2, stack3, stack4);

				CPU->cpu_dtrace_caller = NULL;
			} else {
#ifdef __amd64
				/*
				 * On amd64, we instrument the ret, not the
				 * leave.  We therefore need to set the caller
				 * to assure that the top frame of a stack()
				 * action is correct.
				 */
				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
				CPU->cpu_dtrace_caller = stack[0];
				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
				    CPU_DTRACE_BADADDR);
#endif

				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
				    rval, 0, 0, 0);
				CPU->cpu_dtrace_caller = NULL;
			}

			return (fbt->fbtp_rval);
		}
	}
//HERE();

	return (0);
}
Esempio n. 17
0
static int
fbt_prov_return(pf_info_t *infp, instr_t *instr, int size)
{
	fbt_probe_t *fbt;
	fbt_probe_t *retfbt = infp->retptr;

# if defined(__i386) || defined(__amd64)
	if (*instr == 0xcc)
		return 1;
# endif
	/***********************************************/
	/*   Sanity check for bad things happening.    */
	/***********************************************/
	if (fbt_is_patched(infp->name, instr)) {
		return 0;
	}

	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
	fbt->fbtp_name = infp->name;

	if (retfbt == NULL) {
		fbt->fbtp_id = dtrace_probe_create(fbt_id, infp->modname,
		    infp->name, FBT_RETURN, 3, fbt);
		num_probes++;
	} else {
		retfbt->fbtp_next = fbt;
		fbt->fbtp_id = retfbt->fbtp_id;
	}
	infp->retptr = fbt;
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = infp->mp; //ctl;
	fbt->fbtp_loadcnt = get_refcount(infp->mp);

	/***********************************************/
	/*   Swapped  sense  of  the  following ifdef  */
	/*   around so we are consistent.	       */
	/***********************************************/
	fbt->fbtp_rval = DTRACE_INVOP_ANY;
#if defined(__amd64)
	ASSERT(*instr == FBT_RET);
	fbt->fbtp_roffset =
	    (uintptr_t)(instr - (uint8_t *)infp->st_value);
#elif defined(__i386)
	fbt->fbtp_roffset =
	    (uintptr_t)(instr - (uint8_t *)infp->st_value) + 1;
#elif defined(__arm__)
	fbt->fbtp_roffset = 0;
#endif

	/***********************************************/
	/*   Save  potential overwrite of instruction  */
	/*   and  length,  because  we  will need the  */
	/*   entire  instruction  when we single step  */
	/*   over it.				       */
	/***********************************************/
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_inslen = size;
	fbt->fbtp_type = 1; /* return */
	fbt->fbtp_modrm = -1;
	fbt->fbtp_patchval = FBT_PATCHVAL;
	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt->fbtp_symndx = infp->symndx;

	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	infp->pmp->fbt_nentries++;
	return 1;
}
Esempio n. 18
0
int
fbt_provide_module_function(linker_file_t lf, int symindx,
    linker_symval_t *symval, void *opaque)
{
	char *modname = opaque;
	const char *name = symval->name;
	fbt_probe_t *fbt, *retfbt;
	int j;
	uint32_t *instr, *limit;

#ifdef __powerpc64__
	/*
	 * PowerPC64 uses '.' prefixes on symbol names, ignore it, but only
	 * allow symbols with the '.' prefix, so that we don't get the function
	 * descriptor instead.
	 */
	if (name[0] == '.')
		name++;
	else
		return (0);
#endif

	if (strncmp(name, "dtrace_", 7) == 0 &&
	    strncmp(name, "dtrace_safe_", 12) != 0) {
		/*
		 * Anything beginning with "dtrace_" may be called
		 * from probe context unless it explicitly indicates
		 * that it won't be called from probe context by
		 * using the prefix "dtrace_safe_".
		 */
		return (0);
	}

	if (name[0] == '_' && name[1] == '_')
		return (0);

	instr = (uint32_t *) symval->value;
	limit = (uint32_t *) (symval->value + symval->size);

	for (; instr < limit; instr++)
		if (*instr == FBT_MFLR_R0)
			break;

	if (*instr != FBT_MFLR_R0)
		return (0);

	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;
	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
	    name, FBT_ENTRY, FBT_AFRAMES, fbt);
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = FBT_PATCHVAL;
	fbt->fbtp_rval = DTRACE_INVOP_MFLR_R0;
	fbt->fbtp_symindx = symindx;

	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	retfbt = NULL;
again:
	if (instr >= limit)
		return (0);

	/*
	 * We (desperately) want to avoid erroneously instrumenting a
	 * jump table. To determine if we're looking at a true instruction
	 * sequence or an inline jump table that happens to contain the same
	 * byte sequences, we resort to some heuristic sleeze:  we treat this
	 * instruction as being contained within a pointer, and see if that
	 * pointer points to within the body of the function.  If it does, we
	 * refuse to instrument it.
	 */
	{
		uint32_t *ptr;

		ptr = *(uint32_t **)instr;

		if (ptr >= (uint32_t *) symval->value && ptr < limit) {
			instr++;
			goto again;
		}
	}

	if (*instr != FBT_MTLR_R0) {
		instr++;
		goto again;
	}

	instr++;

	for (j = 0; j < 12 && instr < limit; j++, instr++) {
		if ((*instr == FBT_BCTR) || (*instr == FBT_BLR) ||
		    FBT_IS_JUMP(*instr))
			break;
	}

	if (!(*instr == FBT_BCTR || *instr == FBT_BLR || FBT_IS_JUMP(*instr)))
		goto again;

	/*
	 * We have a winner!
	 */
	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;

	if (retfbt == NULL) {
		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
		    name, FBT_RETURN, FBT_AFRAMES, fbt);
	} else {
		retfbt->fbtp_next = fbt;
		fbt->fbtp_id = retfbt->fbtp_id;
	}

	retfbt = fbt;
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_symindx = symindx;

	if (*instr == FBT_BCTR)
		fbt->fbtp_rval = DTRACE_INVOP_BCTR;
	else if (*instr == FBT_BLR)
		fbt->fbtp_rval = DTRACE_INVOP_RET;
	else
		fbt->fbtp_rval = DTRACE_INVOP_JUMP;

	fbt->fbtp_roffset =
	    (uintptr_t)((uint8_t *)instr - (uint8_t *)symval->value);

	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = FBT_PATCHVAL;
	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	instr += 4;
	goto again;
}
Esempio n. 19
0
static void
fbt_provide_function(struct modctl *mp, par_module_t *pmp,
	char *modname, char *name, uint8_t *st_value,
	uint8_t *instr, uint8_t *limit, int symndx)
{
	int	do_print = FALSE;
	int	invop = 0;
	fbt_probe_t *fbt, *retfbt;
	int	size;
	int	modrm;

	/***********************************************/
	/*   Dont  let  us  register  anything on the  */
	/*   notifier list(s) we are on, else we will  */
	/*   have recursion trouble.		       */
	/***********************************************/
	if (on_notifier_list(instr)) {
		printk("fbt_provide_function: Skip %s:%s - on notifier_chain\n",
			modname, name);
		return;
	}

# define UNHANDLED_FBT() if (do_print || dtrace_unhandled) { \
		printk("fbt:unhandled instr %s:%p %02x %02x %02x %02x\n", \
			name, instr, instr[0], instr[1], instr[2], instr[3]); \
			}

	/***********************************************/
	/*   Make sure we dont try and handle data or  */
	/*   bad instructions.			       */
	/***********************************************/
	if ((size = dtrace_instr_size_modrm(instr, &modrm)) <= 0)
		return;

	/***********************************************/
	/*   Allow  us  to  work on a single function  */
	/*   for  debugging/tracing  else we generate  */
	/*   too  much  printk() output and swamp the  */
	/*   log daemon.			       */
	/***********************************************/
//	do_print = strncmp(name, "update_process", 9) == NULL;

#ifdef __amd64
// am not happy with 0xe8 CALLR instructions, so disable them for now.
if (*instr == 0xe8) return;
	invop = DTRACE_INVOP_ANY;
	switch (instr[0] & 0xf0) {
	  case 0x00:
	  case 0x10:
	  case 0x20:
	  case 0x30:
	  	break;

	  case 0x40:
	  	if (instr[0] == 0x48 && instr[1] == 0xcf)
			return;
	  	break;

	  case 0x50:
	  case 0x60:
	  case 0x70:
	  case 0x80:
	  case 0x90:
	  case 0xa0:
	  case 0xb0:
		break;
	  case 0xc0:
	  	/***********************************************/
	  	/*   We  cannot  single step an IRET for now,  */
	  	/*   so just ditch them as potential probes.   */
	  	/***********************************************/
	  	if (instr[0] == 0xcf)
			return;
		break;
	  case 0xd0:
	  case 0xe0:
		break;
	  case 0xf0:
	  	/***********************************************/
	  	/*   This  doesnt  work - if we single step a  */
	  	/*   HLT, well, the kernel doesnt really like  */
	  	/*   it.				       */
	  	/***********************************************/
	  	if (instr[0] == 0xf4)
			return;

//		printk("fbt:F instr %s:%p size=%d %02x %02x %02x %02x %02x %02x\n", name, instr, size, instr[0], instr[1], instr[2], instr[3], instr[4], instr[5]);
		break;
	  }
#else
	/***********************************************/
	/*   GCC    generates   lots   of   different  */
	/*   assembler  functions plus we have inline  */
	/*   assembler  to  deal  with.  We break the  */
	/*   opcodes  down  into groups, which helped  */
	/*   during  debugging,  or  if  we  need  to  */
	/*   single  out  specific  opcodes,  but for  */
	/*   now,   we  can  pretty  much  allow  all  */
	/*   opcodes.  (Apart  from HLT - which I may  */
	/*   get around to fixing).		       */
	/***********************************************/
	invop = DTRACE_INVOP_ANY;
	switch (instr[0] & 0xf0) {
	  case 0x00:
	  case 0x10:
	  case 0x20:
	  case 0x30:
	  case 0x40:
	  case 0x50:
	  case 0x60:
	  case 0x70:
	  case 0x80:
	  case 0x90:
	  case 0xa0:
	  case 0xb0:
		break;
	  case 0xc0:
	  	/***********************************************/
	  	/*   We  cannot  single step an IRET for now,  */
	  	/*   so just ditch them as potential probes.   */
	  	/***********************************************/
	  	if (instr[0] == 0xcf)
			return;
		break;
	  case 0xd0:
	  case 0xe0:
		break;
	  case 0xf0:
	  	/***********************************************/
	  	/*   This  doesnt  work - if we single step a  */
	  	/*   HLT, well, the kernel doesnt really like  */
	  	/*   it.				       */
	  	/***********************************************/
	  	if (instr[0] == 0xf4)
			return;

//		printk("fbt:F instr %s:%p size=%d %02x %02x %02x %02x %02x %02x\n", name, instr, size, instr[0], instr[1], instr[2], instr[3], instr[4], instr[5]);
		break;
	  }
#endif
	/***********************************************/
	/*   Handle  fact we may not be ready to cope  */
	/*   with this instruction yet.		       */
	/***********************************************/
	if (invop == 0) {
		UNHANDLED_FBT();
		return;
	}

	/***********************************************/
	/*   Make  sure  this  doesnt overlap another  */
	/*   sym. We are in trouble when this happens  */
	/*   - eg we will mistaken what the emulation  */
	/*   is  for,  but  also,  it means something  */
	/*   strange  happens, like kernel is reusing  */
	/*   a  page  (eg  for init/exit section of a  */
	/*   module).				       */
	/***********************************************/
	if (fbt_is_patched(name, instr))
		return;
/*if (modrm >= 0 && (instr[modrm] & 0xc7) == 0x05) {
static char buf[128];
sprintf(buf, "MODRM_%s", name);
name = buf;
}
*/
	/***********************************************/
	/*   Special code here for debugging - we can  */
	/*   make  the  name of a probe a function of  */
	/*   the  instruction, so we can validate and  */
	/*   binary search to find faulty instruction  */
	/*   stepping code in cpu_x86.c		       */
	/***********************************************/
	if (fbt_name_opcodes) {
		static char buf[128];
		if (fbt_name_opcodes == 2)
			snprintf(buf, sizeof buf, "x%02x%02x_%s", *instr, instr[1], name);
		else
			snprintf(buf, sizeof buf, "x%02x_%s", *instr, name);
		name = buf;
	}

	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
	fbt->fbtp_name = name;

	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
	    name, FBT_ENTRY, 3, fbt);
	num_probes++;
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = mp; // ctl;
	fbt->fbtp_loadcnt = get_refcount(mp);
	fbt->fbtp_rval = invop;
	/***********************************************/
	/*   Save  potential overwrite of instruction  */
	/*   and  length,  because  we  will need the  */
	/*   entire  instruction  when we single step  */
	/*   over it.				       */
	/***********************************************/
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_inslen = size;
//if (modrm >= 0 && (instr[modrm] & 0xc7) == 0x05) printk("modrm %s %p rm=%d\n", name, instr, modrm);
	fbt->fbtp_modrm = modrm;
	fbt->fbtp_patchval = FBT_PATCHVAL;

	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt->fbtp_symndx = symndx;
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	if (do_print)
		printk("%d:alloc entry-patchpoint: %s %p invop=%x sz=%d %02x %02x %02x\n", 
			__LINE__, 
			name, 
			fbt->fbtp_patchpoint, 
			fbt->fbtp_rval, 
			fbt->fbtp_inslen,
			instr[0], instr[1], instr[2]);

	pmp->fbt_nentries++;

	retfbt = NULL;

	/***********************************************/
	/*   This   point   is   part   of   a   loop  */
	/*   (implemented  with goto) to find the end  */
	/*   part  of  a  function.  Original Solaris  */
	/*   code assumes a single exit, via RET, for  */
	/*   amd64,  but  is more accepting for i386.  */
	/*   However,  with  GCC  as a compiler a lot  */
	/*   more    things   can   happen   -   very  */
	/*   specifically,  we can have multiple exit  */
	/*   points in a function. So we need to find  */
	/*   each of those.			       */
	/***********************************************/
again:
	if (instr >= limit) {
		return;
	}

	/*
	 * If this disassembly fails, then we've likely walked off into
	 * a jump table or some other unsuitable area.  Bail out of the
	 * disassembly now.
	 */
	if ((size = dtrace_instr_size(instr)) <= 0)
		return;

//HERE();
#ifdef __amd64
	/*
	 * We only instrument "ret" on amd64 -- we don't yet instrument
	 * ret imm16, largely because the compiler doesn't seem to
	 * (yet) emit them in the kernel...
	 */
	switch (*instr) {
	  case FBT_RET:
		invop = DTRACE_INVOP_RET;
		break;
	  default:
		instr += size;
		goto again;
	  }
#else
	switch (*instr) {
/*		  case FBT_POPL_EBP:
		invop = DTRACE_INVOP_POPL_EBP;
		break;
	  case FBT_LEAVE:
		invop = DTRACE_INVOP_LEAVE;
		break;
*/
	  case FBT_RET:
		invop = DTRACE_INVOP_RET;
		break;
	  case FBT_RET_IMM16:
		invop = DTRACE_INVOP_RET_IMM16;
		break;
	  /***********************************************/
	  /*   Some  functions  can end in a jump, e.g.	 */
	  /*   security_file_permission has an indirect	 */
	  /*   jmp.  Dont  think we care too much about	 */
	  /*   this,  but  we  could  handle direct and	 */
	  /*   indirect jumps out of the function body.	 */
	  /***********************************************/
	  default:
		instr += size;
		goto again;
	  }
#endif

	/***********************************************/
	/*   Sanity check for bad things happening.    */
	/***********************************************/
	if (fbt_is_patched(name, instr)) {
		instr += size;
		goto again;
	}
	/*
	 * We have a winner!
	 */
	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
	fbt->fbtp_name = name;

	if (retfbt == NULL) {
		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
		    name, FBT_RETURN, 3, fbt);
		num_probes++;
	} else {
		retfbt->fbtp_next = fbt;
		fbt->fbtp_id = retfbt->fbtp_id;
	}
	retfbt = fbt;
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = mp; //ctl;
	fbt->fbtp_loadcnt = get_refcount(mp);

	/***********************************************/
	/*   Swapped  sense  of  the  following ifdef  */
	/*   around so we are consistent.	       */
	/***********************************************/
	fbt->fbtp_rval = invop;
#ifdef __amd64
	ASSERT(*instr == FBT_RET);
	fbt->fbtp_roffset =
	    (uintptr_t)(instr - (uint8_t *)st_value);
#else
	fbt->fbtp_roffset =
	    (uintptr_t)(instr - (uint8_t *)st_value) + 1;

#endif

	/***********************************************/
	/*   Save  potential overwrite of instruction  */
	/*   and  length,  because  we  will need the  */
	/*   entire  instruction  when we single step  */
	/*   over it.				       */
	/***********************************************/
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_inslen = size;
	fbt->fbtp_modrm = -1;
	fbt->fbtp_patchval = FBT_PATCHVAL;
	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt->fbtp_symndx = symndx;

	if (do_print) {
		printk("%d:alloc return-patchpoint: %s %p: %02x %02x invop=%d\n", __LINE__, name, instr, instr[0], instr[1], invop);
	}

	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	pmp->fbt_nentries++;

	instr += size;
	goto again;
}
Esempio n. 20
0
/*ARGSUSED*/
static void
fbt_provide_module(void *arg, struct modctl *ctl)
{
	struct module *mp = ctl->mod_mp;
	char *str = mp->strings;
	int nsyms = mp->nsyms;
	Shdr *symhdr = mp->symhdr;
	char *modname = ctl->mod_modname;
	char *name;
	fbt_probe_t *fbt, *retfbt;
	size_t symsize;
	int i, size;

	/*
	 * Employees of dtrace and their families are ineligible.  Void
	 * where prohibited.
	 */
	if (strcmp(modname, "dtrace") == 0)
		return;

	if (ctl->mod_requisites != NULL) {
		struct modctl_list *list;

		list = (struct modctl_list *)ctl->mod_requisites;

		for (; list != NULL; list = list->modl_next) {
			if (strcmp(list->modl_modp->mod_modname, "dtrace") == 0)
				return;
		}
	}

	/*
	 * KMDB is ineligible for instrumentation -- it may execute in
	 * any context, including probe context.
	 */
	if (strcmp(modname, "kmdbmod") == 0)
		return;

	if (str == NULL || symhdr == NULL || symhdr->sh_addr == NULL) {
		/*
		 * If this module doesn't (yet) have its string or symbol
		 * table allocated, clear out.
		 */
		return;
	}

	symsize = symhdr->sh_entsize;

	if (mp->fbt_nentries) {
		/*
		 * This module has some FBT entries allocated; we're afraid
		 * to screw with it.
		 */
		return;
	}

	for (i = 1; i < nsyms; i++) {
		uint8_t *instr, *limit;
		Sym *sym = (Sym *)(symhdr->sh_addr + i * symsize);
		int j;

		if (ELF_ST_TYPE(sym->st_info) != STT_FUNC)
			continue;

		/*
		 * Weak symbols are not candidates.  This could be made to
		 * work (where weak functions and their underlying function
		 * appear as two disjoint probes), but it's not simple.
		 */
		if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
			continue;

		name = str + sym->st_name;

		if (strstr(name, "dtrace_") == name &&
		    strstr(name, "dtrace_safe_") != name) {
			/*
			 * Anything beginning with "dtrace_" may be called
			 * from probe context unless it explitly indicates
			 * that it won't be called from probe context by
			 * using the prefix "dtrace_safe_".
			 */
			continue;
		}

		if (strstr(name, "kdi_") == name ||
		    strstr(name, "_kdi_") != NULL) {
			/*
			 * Any function name beginning with "kdi_" or
			 * containing the string "_kdi_" is a part of the
			 * kernel debugger interface and may be called in
			 * arbitrary context -- including probe context.
			 */
			continue;
		}

		/*
		 * Due to 4524008, _init and _fini may have a bloated st_size.
		 * While this bug was fixed quite some time ago, old drivers
		 * may be lurking.  We need to develop a better solution to
		 * this problem, such that correct _init and _fini functions
		 * (the vast majority) may be correctly traced.  One solution
		 * may be to scan through the entire symbol table to see if
		 * any symbol overlaps with _init.  If none does, set a bit in
		 * the module structure that this module has correct _init and
		 * _fini sizes.  This will cause some pain the first time a
		 * module is scanned, but at least it would be O(N) instead of
		 * O(N log N)...
		 */
		if (strcmp(name, "_init") == 0)
			continue;

		if (strcmp(name, "_fini") == 0)
			continue;

		/*
		 * In order to be eligible, the function must begin with the
		 * following sequence:
		 *
		 * 	pushl	%esp
		 *	movl	%esp, %ebp
		 *
		 * Note that there are two variants of encodings that generate
		 * the movl; we must check for both.  For 64-bit, we would
		 * normally insist that a function begin with the following
		 * sequence:
		 *
		 *	pushq	%rbp
		 *	movq	%rsp, %rbp
		 *
		 * However, the compiler for 64-bit often splits these two
		 * instructions -- and the first instruction in the function
		 * is often not the pushq.  As a result, on 64-bit we look
		 * for any "pushq %rbp" in the function and we instrument
		 * this with a breakpoint instruction.
		 */
		instr = (uint8_t *)sym->st_value;
		limit = (uint8_t *)(sym->st_value + sym->st_size);

#ifdef __amd64
		while (instr < limit) {
			if (*instr == FBT_PUSHL_EBP)
				break;

			if ((size = dtrace_instr_size(instr)) <= 0)
				break;

			instr += size;
		}

		if (instr >= limit || *instr != FBT_PUSHL_EBP) {
			/*
			 * We either don't save the frame pointer in this
			 * function, or we ran into some disassembly
			 * screw-up.  Either way, we bail.
			 */
			continue;
		}
#else
		if (instr[0] != FBT_PUSHL_EBP)
			continue;

		if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
		    instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
		    !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
		    instr[2] == FBT_MOVL_ESP_EBP1_V1))
			continue;
#endif

		fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
		fbt->fbtp_name = name;
		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
		    name, FBT_ENTRY, 3, fbt);
		fbt->fbtp_patchpoint = instr;
		fbt->fbtp_ctl = ctl;
		fbt->fbtp_loadcnt = ctl->mod_loadcnt;
		fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
		fbt->fbtp_savedval = *instr;
		fbt->fbtp_patchval = FBT_PATCHVAL;

		fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
		fbt->fbtp_symndx = i;
		fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

		mp->fbt_nentries++;

		retfbt = NULL;
again:
		if (instr >= limit)
			continue;

		/*
		 * If this disassembly fails, then we've likely walked off into
		 * a jump table or some other unsuitable area.  Bail out of the
		 * disassembly now.
		 */
		if ((size = dtrace_instr_size(instr)) <= 0)
			continue;

#ifdef __amd64
		/*
		 * We only instrument "ret" on amd64 -- we don't yet instrument
		 * ret imm16, largely because the compiler doesn't seem to
		 * (yet) emit them in the kernel...
		 */
		if (*instr != FBT_RET) {
			instr += size;
			goto again;
		}
#else
		if (!(size == 1 &&
		    (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
		    (*(instr + 1) == FBT_RET ||
		    *(instr + 1) == FBT_RET_IMM16))) {
			instr += size;
			goto again;
		}
#endif

		/*
		 * We (desperately) want to avoid erroneously instrumenting a
		 * jump table, especially given that our markers are pretty
		 * short:  two bytes on x86, and just one byte on amd64.  To
		 * determine if we're looking at a true instruction sequence
		 * or an inline jump table that happens to contain the same
		 * byte sequences, we resort to some heuristic sleeze:  we
		 * treat this instruction as being contained within a pointer,
		 * and see if that pointer points to within the body of the
		 * function.  If it does, we refuse to instrument it.
		 */
		for (j = 0; j < sizeof (uintptr_t); j++) {
			uintptr_t check = (uintptr_t)instr - j;
			uint8_t *ptr;

			if (check < sym->st_value)
				break;

			if (check + sizeof (uintptr_t) > (uintptr_t)limit)
				continue;

			ptr = *(uint8_t **)check;

			if (ptr >= (uint8_t *)sym->st_value && ptr < limit) {
				instr += size;
				goto again;
			}
		}

		/*
		 * We have a winner!
		 */
		fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
		fbt->fbtp_name = name;

		if (retfbt == NULL) {
			fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
			    name, FBT_RETURN, 3, fbt);
		} else {
			retfbt->fbtp_next = fbt;
			fbt->fbtp_id = retfbt->fbtp_id;
		}

		retfbt = fbt;
		fbt->fbtp_patchpoint = instr;
		fbt->fbtp_ctl = ctl;
		fbt->fbtp_loadcnt = ctl->mod_loadcnt;

#ifndef __amd64
		if (*instr == FBT_POPL_EBP) {
			fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
		} else {
			ASSERT(*instr == FBT_LEAVE);
			fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
		}
		fbt->fbtp_roffset =
		    (uintptr_t)(instr - (uint8_t *)sym->st_value) + 1;

#else
		ASSERT(*instr == FBT_RET);
		fbt->fbtp_rval = DTRACE_INVOP_RET;
		fbt->fbtp_roffset =
		    (uintptr_t)(instr - (uint8_t *)sym->st_value);
#endif

		fbt->fbtp_savedval = *instr;
		fbt->fbtp_patchval = FBT_PATCHVAL;
		fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
		fbt->fbtp_symndx = i;
		fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

		mp->fbt_nentries++;

		instr += size;
		goto again;
	}
}
Esempio n. 21
0
int
fbt_provide_module_function(linker_file_t lf, int symindx,
    linker_symval_t *symval, void *opaque)
{
	fbt_probe_t *fbt, *retfbt;
	uint32_t *instr, *limit;
	const char *name;
	char *modname;
	int patchval;
	int rval;

	modname = opaque;
	name = symval->name;

	/* Check if function is excluded from instrumentation */
	if (fbt_excluded(name))
		return (0);

	instr = (uint32_t *)(symval->value);
	limit = (uint32_t *)(symval->value + symval->size);

	/* Look for sd operation */
	for (; instr < limit; instr++) {
		/* Look for a non-compressed store of ra to sp */
		if (match_opcode(*instr, (MATCH_SD | RS2_RA | RS1_SP),
		    (MASK_SD | RS2_MASK | RS1_MASK))) {
			rval = DTRACE_INVOP_SD;
			patchval = FBT_PATCHVAL;
			break;
		}

		/* Look for a 'C'-compressed store of ra to sp. */
		if (check_c_sdsp(&instr)) {
			rval = DTRACE_INVOP_C_SDSP;
			patchval = FBT_C_PATCHVAL;
			break;
		}
	}

	if (instr >= limit)
		return (0);

	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;
	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
	    name, FBT_ENTRY, 3, fbt);
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = patchval;
	fbt->fbtp_rval = rval;
	fbt->fbtp_symindx = symindx;

	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	retfbt = NULL;
again:
	for (; instr < limit; instr++) {
		/* Look for non-compressed return */
		if (match_opcode(*instr, (MATCH_JALR | (X_RA << RS1_SHIFT)),
		    (MASK_JALR | RD_MASK | RS1_MASK | IMM_MASK))) {
			rval = DTRACE_INVOP_RET;
			patchval = FBT_PATCHVAL;
			break;
		}

		/* Look for 'C'-compressed return */
		if (check_c_ret(&instr)) {
			rval = DTRACE_INVOP_C_RET;
			patchval = FBT_C_PATCHVAL;
			break;
		}
	}

	if (instr >= limit)
		return (0);

	/*
	 * We have a winner!
	 */
	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;
	if (retfbt == NULL) {
		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
		    name, FBT_RETURN, 3, fbt);
	} else {
		retfbt->fbtp_probenext = fbt;
		fbt->fbtp_id = retfbt->fbtp_id;
	}
	retfbt = fbt;

	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_symindx = symindx;
	fbt->fbtp_rval = rval;
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = patchval;
	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	instr++;
	goto again;
}
Esempio n. 22
0
int
fbt_provide_module_function(linker_file_t lf, int symindx,
    linker_symval_t *symval, void *opaque)
{
	fbt_probe_t *fbt, *retfbt;
	uint32_t *instr, *limit;
	const char *name;
	char *modname;

	modname = opaque;
	name = symval->name;

	/* Check if function is excluded from instrumentation */
	if (fbt_excluded(name))
		return (0);

	instr = (uint32_t *)(symval->value);
	limit = (uint32_t *)(symval->value + symval->size);

	/* Look for store double to ra register */
	for (; instr < limit; instr++) {
		if ((*instr & LDSD_RA_SP_MASK) == SD_RA_SP)
			break;
	}

	if (instr >= limit)
		return (0);

	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;
	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
	    name, FBT_ENTRY, 3, fbt);
	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = FBT_PATCHVAL;
	fbt->fbtp_rval = DTRACE_INVOP_SD;
	fbt->fbtp_symindx = symindx;

	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	retfbt = NULL;
again:
	for (; instr < limit; instr++) {
		if ((*instr & LDSD_RA_SP_MASK) == LD_RA_SP) {
			break;
		}
	}

	if (instr >= limit)
		return (0);

	/*
	 * We have a winner!
	 */
	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
	fbt->fbtp_name = name;
	if (retfbt == NULL) {
		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
		    name, FBT_RETURN, 3, fbt);
	} else {
		retfbt->fbtp_next = fbt;
		fbt->fbtp_id = retfbt->fbtp_id;
	}
	retfbt = fbt;

	fbt->fbtp_patchpoint = instr;
	fbt->fbtp_ctl = lf;
	fbt->fbtp_loadcnt = lf->loadcnt;
	fbt->fbtp_symindx = symindx;
	fbt->fbtp_rval = DTRACE_INVOP_LD;
	fbt->fbtp_savedval = *instr;
	fbt->fbtp_patchval = FBT_PATCHVAL;
	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;

	lf->fbt_nentries++;

	instr++;
	goto again;
}
Esempio n. 23
0
static int
fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval, trap_instr_t *tinfo)
{
	uintptr_t stack0, stack1, stack2, stack3, stack4;
	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];

//HERE();
if (dtrace_here) printk("fbt_invop:addr=%lx stack=%p eax=%lx\n", addr, stack, (long) rval);
	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
if (dtrace_here) printk("patchpoint: %p rval=%x\n", fbt->fbtp_patchpoint, fbt->fbtp_rval);
		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
			tinfo->t_opcode = fbt->fbtp_savedval;
			tinfo->t_inslen = fbt->fbtp_inslen;
			tinfo->t_modrm = fbt->fbtp_modrm;
			if (!tinfo->t_doprobe)
				return fbt->fbtp_rval;
			if (fbt->fbtp_roffset == 0) {
				/*
				 * When accessing the arguments on the stack,
				 * we must protect against accessing beyond
				 * the stack.  We can safely set NOFAULT here
				 * -- we know that interrupts are already
				 * disabled.
				 */
				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
				CPU->cpu_dtrace_caller = stack[0];
				stack0 = stack[1];
				stack1 = stack[2];
				stack2 = stack[3];
				stack3 = stack[4];
				stack4 = stack[5];
				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
				    CPU_DTRACE_BADADDR);

				dtrace_probe(fbt->fbtp_id, stack0, stack1,
				    stack2, stack3, stack4);

				CPU->cpu_dtrace_caller = NULL;
			} else {
#ifdef __amd64
				/*
				 * On amd64, we instrument the ret, not the
				 * leave.  We therefore need to set the caller
				 * to assure that the top frame of a stack()
				 * action is correct.
				 */
				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
				CPU->cpu_dtrace_caller = stack[0];
				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
				    CPU_DTRACE_BADADDR);
#endif

				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
				    rval, 0, 0, 0);
				CPU->cpu_dtrace_caller = NULL;
			}

			return (fbt->fbtp_rval);
		}
	}
//HERE();

	return (0);
}