예제 #1
0
파일: fbt.c 프로젝트: coolgoose85/FreeBSD
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;
}
예제 #2
0
파일: fbt.c 프로젝트: andreiw/polaris
/*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;
	}
}
예제 #3
0
파일: fasttrap_isa.c 프로젝트: argp/xnu
int
fasttrap_pid_probe(arm_saved_state_t *regs)
{
	proc_t *p = current_proc();
	user_addr_t new_pc = 0;
	fasttrap_bucket_t *bucket;
	lck_mtx_t *pid_mtx;
	fasttrap_tracepoint_t *tp, tp_local;
	pid_t pid;
	dtrace_icookie_t cookie;
	uint_t is_enabled = 0;
	int instr_size;
	int was_simulated = 1, retire_tp = 1;

	user_addr_t pc = regs->pc;

	uthread_t uthread = (uthread_t) get_bsdthread_info(current_thread());

	/*
	 * It's possible that a user (in a veritable orgy of bad planning)
	 * could redirect this thread's flow of control before it reached the
	 * return probe fasttrap. In this case we need to kill the process
	 * since it's in a unrecoverable state.
	 */
	if (uthread->t_dtrace_step) {
		ASSERT(uthread->t_dtrace_on);
		fasttrap_sigtrap(p, uthread, pc);
		return (0);
	}

	/*
	 * Clear all user tracing flags.
	 */
	uthread->t_dtrace_ft = 0;
	uthread->t_dtrace_pc = 0;
	uthread->t_dtrace_npc = 0;
	uthread->t_dtrace_scrpc = 0;
	uthread->t_dtrace_astpc = 0;

	/*
	 * Treat a child created by a call to vfork(2) as if it were its
	 * parent. We know that there's only one thread of control in such a
	 * process: this one.
	 */
	if (p->p_lflag & P_LINVFORK) {
		proc_list_lock();
		while (p->p_lflag & P_LINVFORK)
			p = p->p_pptr;
		proc_list_unlock();
	}

	pid = p->p_pid;
	pid_mtx = &cpu_core[CPU->cpu_id].cpuc_pid_lock;
	lck_mtx_lock(pid_mtx);
	bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid,pc)];

	/*
	 * Lookup the tracepoint that the process just hit.
	 */
	for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
		if (pid == tp->ftt_pid && pc == tp->ftt_pc &&
		    tp->ftt_proc->ftpc_acount != 0)
			break;
	}

	/*
	 * If we couldn't find a matching tracepoint, either a tracepoint has
	 * been inserted without using the pid<pid> ioctl interface (see
	 * fasttrap_ioctl), or somehow we have mislaid this tracepoint.
	 */
	if (tp == NULL) {
		lck_mtx_unlock(pid_mtx);
		return (-1);
	}

	/* Default to always execute */
	int condition_code = 0xE;
	if (tp->ftt_thumb) {
		uint32_t itstate = GETITSTATE(regs->cpsr);
		if (itstate != 0) {
			/* In IT block, make sure it's the last statement in the block */
			if (ISLASTINIT(itstate)) {
				condition_code = itstate >> 4;
			} else {
				printf("dtrace: fasttrap: Tried to trace instruction %08x at %08x but not at end of IT block\n",
				    (tp->ftt_thumb && dtrace_instr_size(tp->ftt_instr,tp->ftt_thumb) == 2) ? tp->ftt_instr1 : tp->ftt_instr, pc);

				fasttrap_tracepoint_remove(p, tp);
				lck_mtx_unlock(pid_mtx);
				return (-1);
			}
		}
예제 #4
0
파일: fasttrap_isa.c 프로젝트: argp/xnu
int
fasttrap_tracepoint_init(proc_t *p, fasttrap_tracepoint_t *tp,
			 user_addr_t pc, fasttrap_probe_type_t type)
{
#pragma unused(type)
	uint32_t instr;

	/*
	 * Read the instruction at the given address out of the process's
	 * address space. We don't have to worry about a debugger
	 * changing this instruction before we overwrite it with our trap
	 * instruction since P_PR_LOCK is set. Since instructions can span
	 * pages, we potentially read the instruction in two parts. If the
	 * second part fails, we just zero out that part of the instruction.
	 */
	/*      
	 * APPLE NOTE: Of course, we do not have a P_PR_LOCK, so this is racey...
	 */             

	if (uread(p, &instr, 4, pc) != 0)
		return (-1);

	/* We want &instr to always point to the saved instruction, so just copy the
	 * whole thing When cast to a pointer to a uint16_t, that will give us a
	 * pointer to the first two bytes, which is the thumb instruction.
	 */
	tp->ftt_instr = instr;

	if (tp->ftt_fntype != FASTTRAP_FN_DONE_INIT) {
		switch(tp->ftt_fntype) {
			case FASTTRAP_FN_UNKNOWN:
				/* Can't instrument without any information. We can add some heuristics later if necessary. */
				return (-1);

			case FASTTRAP_FN_USDT:
				if (IS_ARM_NOP(instr) || IS_ARM_IS_ENABLED(instr)) {
					tp->ftt_thumb = 0;
				} else if (IS_THUMB_NOP(THUMB_INSTR(instr)) || IS_THUMB_IS_ENABLED(THUMB_INSTR(instr))) {
					tp->ftt_thumb = 1;
				} else {
					/* Shouldn't reach here - this means we don't recognize
					 * the instruction at one of the USDT probe locations
					 */
					return (-1);
				}
				tp->ftt_fntype = FASTTRAP_FN_DONE_INIT;
				break;

			case FASTTRAP_FN_ARM:
				tp->ftt_thumb = 0;
				tp->ftt_fntype = FASTTRAP_FN_DONE_INIT;
				break;

			case FASTTRAP_FN_THUMB:
				tp->ftt_thumb = 1;
				tp->ftt_fntype = FASTTRAP_FN_DONE_INIT;
				break;

			default:
				return (-1);
		}
	}

	if (tp->ftt_thumb) {
		tp->ftt_type = dtrace_decode_thumb(instr);
	} else {
		tp->ftt_type = dtrace_decode_arm(instr);
	}

	if (tp->ftt_type == FASTTRAP_T_INV) {
		/* This is an instruction we either don't recognize or can't instrument */
		printf("dtrace: fasttrap: Unrecognized instruction: %08x at %08x\n",
			(tp->ftt_thumb && dtrace_instr_size(tp->ftt_instr,tp->ftt_thumb) == 2) ? tp->ftt_instr1 : instr, pc);
		return (-1);
	}

	return (0);
}
예제 #5
0
파일: fbt_linux.c 프로젝트: HackLinux/linux
static int fbt_seq_show(struct seq_file *seq, void *v)
{
	int	i, s;
	int	n = (int) (long) v;
	int	target;
	fbt_probe_t *fbt = NULL;
	unsigned long size;
	unsigned long offset;
	char	*modname = NULL;
	char	name[KSYM_NAME_LEN];
	char	ibuf[64];
	char	*cp;

//printk("%s v=%p\n", __func__, v);
	if (n == 1) {
		seq_printf(seq, "# count patchpoint opcode inslen modrm name\n");
		return 0;
	}
	if (n > num_probes)
		return 0;

	/***********************************************/
	/*   Find  first  probe.  This  is incredibly  */
	/*   slow  and  inefficient, but we dont want  */
	/*   to waste memory keeping a list (an extra  */
	/*   ptr for each probe).		       */
	/***********************************************/
	target = n;
	for (i = 0; target > 0 && i < fbt_probetab_size; i++) {
		fbt = fbt_probetab[i];
		if (fbt == NULL)
			continue;
		target--;
		while (target > 0 && fbt) {
			if ((fbt = fbt->fbtp_hashnext) == NULL)
				break;
			target--;
		}
	}
	if (fbt == NULL)
		return 0;

	/***********************************************/
	/*   Dump the instruction so we can make sure  */
	/*   we  can  single  step them in the adjust  */
	/*   cpu code.				       */
	/*   When a module is loaded and we parse the  */
	/*   module,  this may include syms which may  */
	/*   get  unmapped  later  on.  So, trying to  */
	/*   access a patchpoint instruction can GPF.  */
	/*   We need to tread very carefully here not  */
	/*   to  GPF whilst catting /proc/dtrace/fbt.  */
	/*   We   actually   need  to  disable  these  */
	/*   probes.				       */
	/***********************************************/
//printk("fbtproc %p\n", fbt->fbtp_patchpoint);
	if (!validate_ptr(fbt->fbtp_patchpoint)) {
		s = 0xfff;
		strcpy(ibuf, "<unmapped>");
	} else {
		s = dtrace_instr_size((uchar_t *) fbt->fbtp_patchpoint);
		ibuf[0] = '\0';
		for (cp = ibuf, i = 0; i < s; i++) {
			snprintf(cp, sizeof ibuf - (cp - ibuf) - 3, "%02x ", 
				fbt->fbtp_patchpoint[i] & 0xff);
			cp += 3;
			}
	}
	cp = (char *) my_kallsyms_lookup((unsigned long) fbt->fbtp_patchpoint, 
		&size, &offset, &modname, name);
# if defined(__arm__)
	seq_printf(seq, "%d %04u%c %p %08x %d %2d %s:%s:%s %s\n", n-1, 
# else
	seq_printf(seq, "%d %04u%c %p %02x %d %2d %s:%s:%s %s\n", n-1, 
# endif
		fbt->fbtp_fired,
		fbt->fbtp_overrun ? '*' : ' ',
		fbt->fbtp_patchpoint,
		fbt->fbtp_savedval,
		fbt->fbtp_inslen,
		fbt->fbtp_modrm,
		modname ? modname : "kernel",
		cp ? cp : "NULL",
		fbt->fbtp_type ? "return" : "entry",
		ibuf);
	return 0;
}
예제 #6
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;
}