Beispiel #1
0
int
proc_bkptset(struct proc_handle *phdl, uintptr_t address,
             unsigned long *saved)
{
    struct ptrace_io_desc piod;
    unsigned long paddr, caddr;
    int ret = 0;

    *saved = 0;
    if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
            phdl->status == PS_IDLE) {
        errno = ENOENT;
        return (-1);
    }

    DPRINTFX("adding breakpoint at 0x%lx", address);

    if (phdl->status != PS_STOP)
        if (proc_stop(phdl) != 0)
            return (-1);

    /*
     * Read the original instruction.
     */
    caddr = address;
    paddr = 0;
    piod.piod_op = PIOD_READ_I;
    piod.piod_offs = (void *)caddr;
    piod.piod_addr = &paddr;
    piod.piod_len  = BREAKPOINT_INSTR_SZ;
    if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
        DPRINTF("ERROR: couldn't read instruction at address 0x%"
                PRIuPTR, address);
        ret = -1;
        goto done;
    }
    *saved = paddr;
    /*
     * Write a breakpoint instruction to that address.
     */
    caddr = address;
    paddr = BREAKPOINT_INSTR;
    piod.piod_op = PIOD_WRITE_I;
    piod.piod_offs = (void *)caddr;
    piod.piod_addr = &paddr;
    piod.piod_len  = BREAKPOINT_INSTR_SZ;
    if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
        DPRINTF("ERROR: couldn't write instruction at address 0x%"
                PRIuPTR, address);
        ret = -1;
        goto done;
    }

done:
    if (phdl->status != PS_STOP)
        /* Restart the process if we had to stop it. */
        proc_cont(phdl);

    return (ret);
}
Beispiel #2
0
int
proc_regset(struct proc_handle *phdl, proc_reg_t reg, unsigned long regvalue)
{
	struct reg regs;

	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
	    phdl->status == PS_IDLE) {
		errno = ENOENT;
		return (-1);
	}
	if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)&regs, 0) < 0)
		return (-1);
	switch (reg) {
	case REG_PC:
#if defined(__aarch64__)
		regs.elr = regvalue;
#elif defined(__amd64__)
		regs.r_rip = regvalue;
#elif defined(__arm__)
		regs.r_pc = regvalue;
#elif defined(__i386__)
		regs.r_eip = regvalue;
#elif defined(__mips__)
		regs.r_regs[PC] = regvalue;
#elif defined(__powerpc__)
		regs.pc = regvalue;
#elif defined(__riscv__)
		regs.sepc = regvalue;
#endif
		break;
	case REG_SP:
#if defined(__aarch64__)
		regs.sp = regvalue;
#elif defined(__amd64__)
		regs.r_rsp = regvalue;
#elif defined(__arm__)
		regs.r_sp = regvalue;
#elif defined(__i386__)
		regs.r_esp = regvalue;
#elif defined(__mips__)
		regs.r_regs[PC] = regvalue;
#elif defined(__powerpc__)
		regs.fixreg[1] = regvalue;
#elif defined(__riscv__)
		regs.sp = regvalue;
#endif
		break;
	default:
		DPRINTFX("ERROR: no support for reg number %d", reg);
		return (-1);
	}
	if (ptrace(PT_SETREGS, proc_getpid(phdl), (caddr_t)&regs, 0) < 0)
		return (-1);

	return (0);
}
Beispiel #3
0
int
proc_bkptdel(struct proc_handle *phdl, uintptr_t address,
    unsigned long saved)
{
	struct ptrace_io_desc piod;
	unsigned long paddr, caddr;

	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
	    phdl->status == PS_IDLE) {
		errno = ENOENT;
		return (-1);
	}
	DPRINTF("removing breakpoint at 0x%lx\n", address);
	/*
	 * Overwrite the breakpoint instruction that we setup previously.
	 */
	caddr = address;
	paddr = saved;
	piod.piod_op = PIOD_WRITE_I;
	piod.piod_offs = (void *)caddr;
	piod.piod_addr = &paddr;
	piod.piod_len  = BREAKPOINT_INSTR_SZ;
	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
		DPRINTF("ERROR: couldn't write instruction at address 0x%" PRIuPTR,
		    address);
		return (-1);
	}
 
	return (0);
}
Beispiel #4
0
int
proc_regget(struct proc_handle *phdl, proc_reg_t reg, unsigned long *regvalue)
{
	struct reg regs;

	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
	    phdl->status == PS_IDLE) {
		errno = ENOENT;
		return (-1);
	}
	memset(&regs, 0, sizeof(regs));
	if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)&regs, 0) < 0)
		return (-1);
	switch (reg) {
	case REG_PC:
#if defined(__amd64__)
		*regvalue = regs.r_rip;
#elif defined(__i386__)
		*regvalue = regs.r_eip;
#endif
		break;
	case REG_SP:
#if defined(__amd64__)
		*regvalue = regs.r_rsp;
#elif defined(__i386__)
		*regvalue = regs.r_esp;
#endif
		break;
	default:
		warn("ERROR: no support for reg number %d", reg);
		return (-1);
	}

	return (0);
}
Beispiel #5
0
dt_proc_t *
dt_proc_lookup(dtrace_hdl_t *dtp, struct ps_prochandle *P, int remove)
{
	dt_proc_hash_t *dph = dtp->dt_procs;
#if defined(sun)
	pid_t pid = Pstatus(P)->pr_pid;
#else
	pid_t pid = proc_getpid(P);
#endif
	dt_proc_t *dpr, **dpp = &dph->dph_hash[pid & (dph->dph_hashlen - 1)];

	for (dpr = *dpp; dpr != NULL; dpr = dpr->dpr_hash) {
		if (dpr->dpr_pid == pid)
			break;
		else
			dpp = &dpr->dpr_hash;
	}

	assert(dpr != NULL);
	assert(dpr->dpr_proc == P);

	if (remove)
		*dpp = dpr->dpr_hash; /* remove from pid hash chain */

	return (dpr);
}
Beispiel #6
0
int
proc_bkptset(struct proc_handle *phdl, uintptr_t address,
    unsigned long *saved)
{
	struct ptrace_io_desc piod;
	unsigned long paddr, caddr;

	*saved = 0;
	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
	    phdl->status == PS_IDLE) {
		errno = ENOENT;
		return (-1);
	}

	/*
	 * Read the original instruction.
	 */
	caddr = address;
	paddr = 0;
	piod.piod_op = PIOD_READ_I;
	piod.piod_offs = (void *)caddr;
	piod.piod_addr = &paddr;
	piod.piod_len  = BREAKPOINT_INSTR_SZ;
	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
		DPRINTF("ERROR: couldn't read instruction at address 0x%" PRIuPTR,
		    address);
		return (-1);
	}
	*saved = paddr;
	/*
	 * Write a breakpoint instruction to that address.
	 */
	caddr = address;
	paddr = BREAKPOINT_INSTR;
	piod.piod_op = PIOD_WRITE_I;
	piod.piod_offs = (void *)caddr;
	piod.piod_addr = &paddr;
	piod.piod_len  = BREAKPOINT_INSTR_SZ;
	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
		warn("ERROR: couldn't write instruction at address 0x%" PRIuPTR,
		    address);
		return (-1);
	}

	return (0);
}
Beispiel #7
0
static int
proc_stop(struct proc_handle *phdl)
{
	int status;

	if (kill(proc_getpid(phdl), SIGSTOP) == -1) {
		DPRINTF("kill %d", proc_getpid(phdl));
		return (-1);
	} else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) {
		DPRINTF("waitpid %d", proc_getpid(phdl));
		return (-1);
	} else if (!WIFSTOPPED(status)) {
		DPRINTFX("waitpid: unexpected status 0x%x", status);
		return (-1);
	}

	return (0);
}
Beispiel #8
0
static int
dt_pid_has_jump_table(struct ps_prochandle *P, dtrace_hdl_t *dtp,
    uint8_t *text, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp)
{
	ulong_t i;
	int size;
#if defined(sun)
	pid_t pid = Pstatus(P)->pr_pid;
	char dmodel = Pstatus(P)->pr_dmodel;
#else
	pid_t pid = proc_getpid(P);
#if __i386__
	char dmodel = PR_MODEL_ILP32;
#elif __amd64__
	char dmodel = PR_MODEL_LP64;
#endif
#endif

	/*
	 * Take a pass through the function looking for a register-dependant
	 * jmp instruction. This could be a jump table so we have to be
	 * ultra conservative.
	 */
	for (i = 0; i < ftp->ftps_size; i += size) {
		size = dt_instr_size(&text[i], dtp, pid, symp->st_value + i,
		    dmodel);

		/*
		 * Assume the worst if we hit an illegal instruction.
		 */
		if (size <= 0) {
			dt_dprintf("error at %#lx (assuming jump table)\n", i);
			return (1);
		}

#ifdef notyet
		/*
		 * Register-dependant jmp instructions start with a 0xff byte
		 * and have the modrm.reg field set to 4. They can have an
		 * optional REX prefix on the 64-bit ISA.
		 */
		if ((text[i] == 0xff && DT_MODRM_REG(text[i + 1]) == 4) ||
		    (dmodel == PR_MODEL_LP64 && (text[i] & 0xf0) == 0x40 &&
		    text[i + 1] == 0xff && DT_MODRM_REG(text[i + 2]) == 4)) {
			dt_dprintf("found a suspected jump table at %s:%lx\n",
			    ftp->ftps_func, i);
			return (1);
		}
#endif
	}

	return (0);
}
Beispiel #9
0
struct ps_prochandle *
dtrace_proc_create(dtrace_hdl_t *dtp, const char *file, char *const *argv)
{
	dt_ident_t *idp = dt_idhash_lookup(dtp->dt_macros, "target");
	struct ps_prochandle *P = dt_proc_create(dtp, file, argv);

	if (P != NULL && idp != NULL && idp->di_id == 0)
#if defined(sun)
		idp->di_id = Pstatus(P)->pr_pid; /* $target = created pid */
#else
		idp->di_id = proc_getpid(P); /* $target = created pid */
#endif

	return (P);
}
Beispiel #10
0
prmap_t *
proc_name2map(struct proc_handle *p, const char *name)
{
	size_t i;
	int cnt;
	prmap_t *map;
	char tmppath[MAXPATHLEN];
	struct kinfo_vmentry *kves, *kve;
	rd_loadobj_t *rdl;

	/*
	 * If we haven't iterated over the list of loaded objects,
	 * librtld_db isn't yet initialized and it's very likely
	 * that librtld_db called us. We need to do the heavy
	 * lifting here to find the symbol librtld_db is looking for.
	 */
	if (p->nobjs == 0) {
		if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL)
			return (NULL);
		for (i = 0; i < (size_t)cnt; i++) {
			kve = kves + i;
			basename_r(kve->kve_path, tmppath);
			if (strcmp(tmppath, name) == 0) {
				map = proc_addr2map(p, kve->kve_start);
				free(kves);
				return (map);
			}
		}
		free(kves);
		return (NULL);
	}
	if (name == NULL || strcmp(name, "a.out") == 0) {
		map = proc_addr2map(p, p->rdobjs[0].rdl_saddr);
		return (map);
	}
	for (i = 0; i < p->nobjs; i++) {
		rdl = &p->rdobjs[i];
		basename_r(rdl->rdl_path, tmppath);
		if (strcmp(tmppath, name) == 0) {
			if ((map = malloc(sizeof(*map))) == NULL)
				return (NULL);
			proc_rdl2prmap(rdl, map);
			return (map);
		}
	}

	return (NULL);
}
Beispiel #11
0
int
proc_bkptdel(struct proc_handle *phdl, uintptr_t address,
    unsigned long saved)
{
	struct ptrace_io_desc piod;
	uintptr_t caddr;
	int ret = 0, stopped;
	instr_t instr;

	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
	    phdl->status == PS_IDLE) {
		errno = ENOENT;
		return (-1);
	}

	DPRINTFX("removing breakpoint at 0x%lx", address);

	stopped = 0;
	if (phdl->status != PS_STOP) {
		if (proc_stop(phdl) != 0)
			return (-1);
		stopped = 1;
	}

	/*
	 * Overwrite the breakpoint instruction that we setup previously.
	 */
	caddr = address;
	instr = saved;
	piod.piod_op = PIOD_WRITE_I;
	piod.piod_offs = (void *)caddr;
	piod.piod_addr = &instr;
	piod.piod_len  = BREAKPOINT_INSTR_SZ;
	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
		DPRINTF("ERROR: couldn't write instruction at address 0x%"
		    PRIuPTR, address);
		ret = -1;
	}

	if (stopped)
		/* Restart the process if we had to stop it. */
		proc_continue(phdl);
 
	return (ret);
}
Beispiel #12
0
/*
 * Step over the breakpoint.
 */
int
proc_bkptexec(struct proc_handle *phdl, unsigned long saved)
{
	unsigned long pc;
	unsigned long samesaved;
	int status;

	if (proc_regget(phdl, REG_PC, &pc) < 0) {
		warn("ERROR: couldn't get PC register");
		return (-1);
	}
	proc_bkptregadj(&pc);
	if (proc_bkptdel(phdl, pc, saved) < 0) {
		warn("ERROR: couldn't delete breakpoint");
		return (-1);
	}
	/*
	 * Go back in time and step over the new instruction just
	 * set up by proc_bkptdel().
	 */
	proc_regset(phdl, REG_PC, pc);
	if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) {
		warn("ERROR: ptrace step failed");
		return (-1);
	}
	proc_wstatus(phdl);
	status = proc_getwstat(phdl);
	if (!WIFSTOPPED(status)) {
		warn("ERROR: don't know why process stopped");
		return (-1);
	}
	/*
	 * Restore the breakpoint. The saved instruction should be
	 * the same as the one that we were passed in.
	 */
	if (proc_bkptset(phdl, pc, &samesaved) < 0) {
		warn("ERROR: couldn't restore breakpoint");
		return (-1);
	}
	assert(samesaved == saved);

	return (0);
}
Beispiel #13
0
static int
dt_pid_create_usdt_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp,
    dt_pcb_t *pcb, dt_proc_t *dpr)
{
	struct ps_prochandle *P = dpr->dpr_proc;
	int ret = 0;

	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
#if defined(sun)
	(void) Pupdate_maps(P);
	if (Pobject_iter(P, dt_pid_usdt_mapping, P) != 0) {
		ret = -1;
		(void) dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_USDT,
		    "failed to instantiate probes for pid %d: %s",
#if defined(sun)
		    (int)Pstatus(P)->pr_pid, strerror(errno));
#else
		    (int)proc_getpid(P), strerror(errno));
#endif
	}
Beispiel #14
0
/*ARGSUSED*/
int
dt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
    fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret)
{
	uint8_t *text;
	ulong_t i, end;
	int size;
#if defined(sun)
	pid_t pid = Pstatus(P)->pr_pid;
	char dmodel = Pstatus(P)->pr_dmodel;
#else
	pid_t pid = proc_getpid(P);
#if __i386__
	char dmodel = PR_MODEL_ILP32;
#elif __amd64__
	char dmodel = PR_MODEL_LP64;
#endif
#endif

	/*
	 * We allocate a few extra bytes at the end so we don't have to check
	 * for overrunning the buffer.
	 */
	if ((text = calloc(1, symp->st_size + 4)) == NULL) {
		dt_dprintf("mr sparkle: malloc() failed\n");
		return (DT_PROC_ERR);
	}

	if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) {
		dt_dprintf("mr sparkle: Pread() failed\n");
		free(text);
		return (DT_PROC_ERR);
	}

	ftp->ftps_type = DTFTP_RETURN;
	ftp->ftps_pc = (uintptr_t)symp->st_value;
	ftp->ftps_size = (size_t)symp->st_size;
	ftp->ftps_noffs = 0;

	/*
	 * If there's a jump table in the function we're only willing to
	 * instrument these specific (and equivalent) instruction sequences:
	 *	leave
	 *	[rep] ret
	 * and
	 *	movl	%ebp,%esp
	 *	popl	%ebp
	 *	[rep] ret
	 *
	 * We do this to avoid accidentally interpreting jump table
	 * offsets as actual instructions.
	 */
	if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) {
		for (i = 0, end = ftp->ftps_size; i < end; i += size) {
			size = dt_instr_size(&text[i], dtp, pid,
			    symp->st_value + i, dmodel);

			/* bail if we hit an invalid opcode */
			if (size <= 0)
				break;

			if (text[i] == DT_LEAVE && text[i + 1] == DT_RET) {
				dt_dprintf("leave/ret at %lx\n", i + 1);
				ftp->ftps_offs[ftp->ftps_noffs++] = i + 1;
				size = 2;
			} else if (text[i] == DT_LEAVE &&
			    text[i + 1] == DT_REP && text[i + 2] == DT_RET) {
				dt_dprintf("leave/rep ret at %lx\n", i + 1);
				ftp->ftps_offs[ftp->ftps_noffs++] = i + 1;
				size = 3;
			} else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP &&
			    text[i + 2] == DT_POPL_EBP &&
			    text[i + 3] == DT_RET) {
				dt_dprintf("movl/popl/ret at %lx\n", i + 3);
				ftp->ftps_offs[ftp->ftps_noffs++] = i + 3;
				size = 4;
			} else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP &&
			    text[i + 2] == DT_POPL_EBP &&
			    text[i + 3] == DT_REP &&
			    text[i + 4] == DT_RET) {
				dt_dprintf("movl/popl/rep ret at %lx\n", i + 3);
				ftp->ftps_offs[ftp->ftps_noffs++] = i + 3;
				size = 5;
			}
		}
	} else {
		for (i = 0, end = ftp->ftps_size; i < end; i += size) {
			size = dt_instr_size(&text[i], dtp, pid,
			    symp->st_value + i, dmodel);

			/* bail if we hit an invalid opcode */
			if (size <= 0)
				break;

			/* ordinary ret */
			if (size == 1 && text[i] == DT_RET)
				goto is_ret;

			/* two-byte ret */
			if (size == 2 && text[i] == DT_REP &&
			    text[i + 1] == DT_RET)
				goto is_ret;

			/* ret <imm16> */
			if (size == 3 && text[i] == DT_RET16)
				goto is_ret;

			/* two-byte ret <imm16> */
			if (size == 4 && text[i] == DT_REP &&
			    text[i + 1] == DT_RET16)
				goto is_ret;

			/* 32-bit displacement jmp outside of the function */
			if (size == 5 && text[i] == DT_JMP32 && symp->st_size <=
			    (uintptr_t)(i + size + *(int32_t *)&text[i + 1]))
				goto is_ret;

			/* 8-bit displacement jmp outside of the function */
			if (size == 2 && text[i] == DT_JMP8 && symp->st_size <=
			    (uintptr_t)(i + size + *(int8_t *)&text[i + 1]))
				goto is_ret;

			/* 32-bit disp. conditional jmp outside of the func. */
			if (size == 6 && DT_ISJ32(*(uint16_t *)&text[i]) &&
			    symp->st_size <=
			    (uintptr_t)(i + size + *(int32_t *)&text[i + 2]))
				goto is_ret;

			/* 8-bit disp. conditional jmp outside of the func. */
			if (size == 2 && DT_ISJ8(text[i]) && symp->st_size <=
			    (uintptr_t)(i + size + *(int8_t *)&text[i + 1]))
				goto is_ret;

			continue;
is_ret:
			dt_dprintf("return at offset %lx\n", i);
			ftp->ftps_offs[ftp->ftps_noffs++] = i;
		}
	}

	free(text);
	if (ftp->ftps_noffs > 0) {
		if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
			dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
			    strerror(errno));
			return (dt_set_errno(dtp, errno));
		}
	}

	return (ftp->ftps_noffs);
}
Beispiel #15
0
/*ARGSUSED*/
static void
prochandler(struct ps_prochandle *P, const char *msg, void *arg)
{
#if defined(sun)
	const psinfo_t *prp = Ppsinfo(P);
	int pid = Pstatus(P)->pr_pid;
	char name[SIG2STR_MAX];
#else
	int wstatus = proc_getwstat(P);
	int pid = proc_getpid(P);
#endif

	if (msg != NULL) {
		notice("pid %d: %s\n", pid, msg);
		return;
	}

#if defined(sun)
	switch (Pstate(P)) {
#else
	switch (proc_state(P)) {
#endif
	case PS_UNDEAD:
#if defined(sun)
		/*
		 * Ideally we would like to always report pr_wstat here, but it
		 * isn't possible given current /proc semantics.  If we grabbed
		 * the process, Ppsinfo() will either fail or return a zeroed
		 * psinfo_t depending on how far the parent is in reaping it.
		 * When /proc provides a stable pr_wstat in the status file,
		 * this code can be improved by examining this new pr_wstat.
		 */
		if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) {
			notice("pid %d terminated by %s\n", pid,
			    proc_signame(WTERMSIG(prp->pr_wstat),
			    name, sizeof (name)));
#else
		if (WIFSIGNALED(wstatus)) {
			notice("pid %d terminated by %d\n", pid,
			    WTERMSIG(wstatus));
#endif
#if defined(sun)
		} else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) {
			notice("pid %d exited with status %d\n",
			    pid, WEXITSTATUS(prp->pr_wstat));
#else
		} else if (WEXITSTATUS(wstatus) != 0) {
			notice("pid %d exited with status %d\n",
			    pid, WEXITSTATUS(wstatus));
#endif
		} else {
			notice("pid %d has exited\n", pid);
		}
		g_pslive--;
		break;

	case PS_LOST:
		notice("pid %d exec'd a set-id or unobservable program\n", pid);
		g_pslive--;
		break;
	}
}

/*ARGSUSED*/
static int
errhandler(const dtrace_errdata_t *data, void *arg)
{
	error(data->dteda_msg);
	return (DTRACE_HANDLE_OK);
}

/*ARGSUSED*/
static int
drophandler(const dtrace_dropdata_t *data, void *arg)
{
	error(data->dtdda_msg);
	return (DTRACE_HANDLE_OK);
}
Beispiel #16
0
/*ARGSUSED*/
int
dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp,
    fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern)
{
	uint8_t *text;
	int size;
	ulong_t i, end = symp->st_size;
#if defined(sun)
	pid_t pid = Pstatus(P)->pr_pid;
	char dmodel = Pstatus(P)->pr_dmodel;
#else
	pid_t pid = proc_getpid(P);
#if __i386__
	char dmodel = PR_MODEL_ILP32;
#elif __amd64__
	char dmodel = PR_MODEL_LP64;
#endif
#endif

	ftp->ftps_type = DTFTP_OFFSETS;
	ftp->ftps_pc = (uintptr_t)symp->st_value;
	ftp->ftps_size = (size_t)symp->st_size;
	ftp->ftps_noffs = 0;

	if ((text = malloc(symp->st_size)) == NULL) {
		dt_dprintf("mr sparkle: malloc() failed\n");
		return (DT_PROC_ERR);
	}

	if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) {
		dt_dprintf("mr sparkle: Pread() failed\n");
		free(text);
		return (DT_PROC_ERR);
	}

	/*
	 * We can't instrument offsets in functions with jump tables as
	 * we might interpret a jump table offset as an instruction.
	 */
	if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) {
		free(text);
		return (0);
	}

	if (strcmp("*", pattern) == 0) {
		for (i = 0; i < end; i += size) {
			ftp->ftps_offs[ftp->ftps_noffs++] = i;

			size = dt_instr_size(&text[i], dtp, pid,
			    symp->st_value + i, dmodel);

			/* bail if we hit an invalid opcode */
			if (size <= 0)
				break;
		}
	} else {
		char name[sizeof (i) * 2 + 1];

		for (i = 0; i < end; i += size) {
			(void) snprintf(name, sizeof (name), "%lx", i);
			if (gmatch(name, pattern))
				ftp->ftps_offs[ftp->ftps_noffs++] = i;

			size = dt_instr_size(&text[i], dtp, pid,
			    symp->st_value + i, dmodel);

			/* bail if we hit an invalid opcode */
			if (size <= 0)
				break;
		}
	}

	free(text);
	if (ftp->ftps_noffs > 0) {
		if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
			dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
			    strerror(errno));
			return (dt_set_errno(dtp, errno));
		}
	}

	return (ftp->ftps_noffs);
}
Beispiel #17
0
/*
 * Main loop for all victim process control threads.  We initialize all the
 * appropriate /proc control mechanisms, and then enter a loop waiting for
 * the process to stop on an event or die.  We process any events by calling
 * appropriate subroutines, and exit when the victim dies or we lose control.
 *
 * The control thread synchronizes the use of dpr_proc with other libdtrace
 * threads using dpr_lock.  We hold the lock for all of our operations except
 * waiting while the process is running: this is accomplished by writing a
 * PCWSTOP directive directly to the underlying /proc/<pid>/ctl file.  If the
 * libdtrace client wishes to exit or abort our wait, SIGCANCEL can be used.
 */
static void *
dt_proc_control(void *arg)
{
	dt_proc_control_data_t *datap = arg;
	dtrace_hdl_t *dtp = datap->dpcd_hdl;
	dt_proc_t *dpr = datap->dpcd_proc;
	dt_proc_hash_t *dph = dpr->dpr_hdl->dt_procs;
	struct ps_prochandle *P = dpr->dpr_proc;

#if defined(sun)
	int pfd = Pctlfd(P);

	const long wstop = PCWSTOP;
#endif
	int notify = B_FALSE;

	/*
	 * We disable the POSIX thread cancellation mechanism so that the
	 * client program using libdtrace can't accidentally cancel our thread.
	 * dt_proc_destroy() uses SIGCANCEL explicitly to simply poke us out
	 * of PCWSTOP with EINTR, at which point we will see dpr_quit and exit.
	 */
	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

	dpr->dpr_pid = proc_getpid(P);
	int pid = dpr->dpr_pid;

	/*
	 * Set up the corresponding process for tracing by libdtrace.  We want
	 * to be able to catch breakpoints and efficiently single-step over
	 * them, and we need to enable librtld_db to watch libdl activity.
	 */
	do_ptrace(__func__, PTRACE_ATTACH, dpr->dpr_pid, 0, 0);
	(void) pthread_mutex_lock(&dpr->dpr_lock);

	(void) Punsetflags(P, PR_ASYNC);	/* require synchronous mode */
	(void) Psetflags(P, PR_BPTADJ);		/* always adjust eip on x86 */
	(void) Punsetflags(P, PR_FORK);		/* do not inherit on fork */

	(void) Pfault(P, FLTBPT, B_TRUE);	/* always trace breakpoints */
	(void) Pfault(P, FLTTRACE, B_TRUE);	/* always trace single-step */

	/*
	 * We must trace exit from exec() system calls so that if the exec is
	 * successful, we can reset our breakpoints and re-initialize libproc.
	 */
	(void) Psysexit(P, SYS_exec, B_TRUE);
	(void) Psysexit(P, SYS_execve, B_TRUE);

	/*
	 * We must trace entry and exit for fork() system calls in order to
	 * disable our breakpoints temporarily during the fork.  We do not set
	 * the PR_FORK flag, so if fork succeeds the child begins executing and
	 * does not inherit any other tracing behaviors or a control thread.
	 */
	(void) Psysentry(P, SYS_vfork, B_TRUE);
	(void) Psysexit(P, SYS_vfork, B_TRUE);
	(void) Psysentry(P, SYS_fork1, B_TRUE);
	(void) Psysexit(P, SYS_fork1, B_TRUE);
	(void) Psysentry(P, SYS_forkall, B_TRUE);
	(void) Psysexit(P, SYS_forkall, B_TRUE);
	(void) Psysentry(P, SYS_forksys, B_TRUE);
	(void) Psysexit(P, SYS_forksys, B_TRUE);

	Psync(P);				/* enable all /proc changes */
	dt_proc_attach(dpr, B_FALSE);		/* enable rtld breakpoints */

	/*
	 * If PR_KLC is set, we created the process; otherwise we grabbed it.
	 * Check for an appropriate stop request and wait for dt_proc_continue.
	 */
	dpr->dpr_stop |= DT_PROC_STOP_CREATE;
	if (Pstatus(P)->pr_flags & PR_KLC)
		dt_proc_stop(dpr, DT_PROC_STOP_CREATE);
	else
		dt_proc_stop(dpr, DT_PROC_STOP_GRAB);

	if (Psetrun(P, 0, 0) == -1) {
		dt_dprintf("pid %d: failed to set running: %s\n",
		    (int)dpr->dpr_pid, strerror(errno));
	}

	(void) pthread_mutex_unlock(&dpr->dpr_lock);

	/*
	 * Wait for the process corresponding to this control thread to stop,
	 * process the event, and then set it running again.  We want to sleep
	 * with dpr_lock *unheld* so that other parts of libdtrace can use the
	 * ps_prochandle in the meantime (e.g. ustack()).  To do this, we write
	 * a PCWSTOP directive directly to the underlying /proc/<pid>/ctl file.
	 * Once the process stops, we wake up, grab dpr_lock, and then call
	 * Pwait() (which will return immediately) and do our processing.
	 */
//printf("%s: waiting to quit\n", __func__);
	while (!dpr->dpr_quit) {
		const lwpstatus_t *psp;
#if defined(sun)

		if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR)
			continue; /* check dpr_quit and continue waiting */
#else
		/* Wait for the process to report status. */
                proc_wait(P);
#endif
		(void) pthread_mutex_lock(&dpr->dpr_lock);
pwait_locked:
		if (Pstopstatus(P, PCNULL, 0) == -1 && errno == EINTR) {
//printf("%s stopstatus (loop) pr_pid pid=%d\n", __func__, Pstatus(dpr->dpr_proc)->pr_pid);
			(void) pthread_mutex_unlock(&dpr->dpr_lock);
			continue; /* check dpr_quit and continue waiting */
		}

		switch (Pstate(P)) {
		case PS_STOP:
			psp = &Pstatus(P)->pr_lwp;

			dt_dprintf("pid %d: proc stopped showing %d/%d\n",
			    pid, psp->pr_why, psp->pr_what);

#if defined(sun)
			/*
			 * If the process stops showing PR_REQUESTED, then the
			 * DTrace stop() action was applied to it or another
			 * debugging utility (e.g. pstop(1)) asked it to stop.
			 * In either case, the user's intention is for the
			 * process to remain stopped until another external
			 * mechanism (e.g. prun(1)) is applied.  So instead of
			 * setting the process running ourself, we wait for
			 * someone else to do so.  Once that happens, we return
			 * to our normal loop waiting for an event of interest.
			 */
			if (psp->pr_why == PR_REQUESTED) {
				dt_proc_waitrun(dpr);
				(void) pthread_mutex_unlock(&dpr->dpr_lock);
				continue;
			}

			/*
			 * If the process stops showing one of the events that
			 * we are tracing, perform the appropriate response.
			 * Note that we ignore PR_SUSPENDED, PR_CHECKPOINT, and
			 * PR_JOBCONTROL by design: if one of these conditions
			 * occurs, we will fall through to Psetrun() but the
			 * process will remain stopped in the kernel by the
			 * corresponding mechanism (e.g. job control stop).
			 */
			if (psp->pr_why == PR_FAULTED && psp->pr_what == FLTBPT)
				dt_proc_bpmatch(dtp, dpr);
			else if (psp->pr_why == PR_SYSENTRY &&
			    IS_SYS_FORK(psp->pr_what))
				dt_proc_bpdisable(dpr);
			else if (psp->pr_why == PR_SYSEXIT &&
			    IS_SYS_FORK(psp->pr_what))
				dt_proc_bpenable(dpr);
			else if (psp->pr_why == PR_SYSEXIT &&
			    IS_SYS_EXEC(psp->pr_what))
				dt_proc_attach(dpr, B_TRUE);
#endif
//printf("In PS_STOP dpr_stop=%x\n", dpr->dpr_stop);
			break;

		case PS_LOST:
//printf("in PS_LOST\n");
			if (Preopen(P) == 0)
				goto pwait_locked;

			dt_dprintf("pid %d: proc lost: %s\n",
			    pid, strerror(errno));

			dpr->dpr_quit = B_TRUE;
			notify = B_TRUE;
			break;

		case PS_UNDEAD:
		case PS_DEAD:
			dt_dprintf("pid %d: proc died\n", pid);
			dpr->dpr_quit = B_TRUE;
			notify = B_TRUE;
			break;

		}

		if (Pstate(P) != PS_UNDEAD && Psetrun(P, 0, 0) == -1) {
			dt_dprintf("pid %d: failed to set running: %s\n",
			    (int)dpr->dpr_pid, strerror(errno));
		}

		(void) pthread_mutex_unlock(&dpr->dpr_lock);
	}

	/*
	 * If the control thread detected PS_UNDEAD or PS_LOST, then enqueue
	 * the dt_proc_t structure on the dt_proc_hash_t notification list.
	 */
	if (notify)
		dt_proc_notify(dtp, dph, dpr, NULL);

	/*
	 * Destroy and remove any remaining breakpoints, set dpr_done and clear
	 * dpr_tid to indicate the control thread has exited, and notify any
	 * waiting thread in dt_proc_destroy() that we have succesfully exited.
	 */
	(void) pthread_mutex_lock(&dpr->dpr_lock);

	dt_proc_bpdestroy(dpr, B_TRUE);
	dpr->dpr_done = B_TRUE;
	dpr->dpr_tid = 0;

	(void) pthread_cond_broadcast(&dpr->dpr_cv);
	(void) pthread_mutex_unlock(&dpr->dpr_lock);

	return (NULL);
}
Beispiel #18
0
static void
proc_cont(struct proc_handle *phdl)
{

    ptrace(PT_CONTINUE, proc_getpid(phdl), (caddr_t)1, 0);
}
Beispiel #19
0
static int
dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
{
	dtrace_hdl_t *dtp = pp->dpp_dtp;
	dt_pcb_t *pcb = pp->dpp_pcb;
	dt_proc_t *dpr = pp->dpp_dpr;
	fasttrap_probe_spec_t *ftp;
	uint64_t off;
	char *end;
	uint_t nmatches = 0;
	ulong_t sz;
	int glob, err;
	int isdash = strcmp("-", func) == 0;
	pid_t pid;

#if defined(sun)
	pid = Pstatus(pp->dpp_pr)->pr_pid;
#else
	pid = proc_getpid(pp->dpp_pr);
#endif

	dt_dprintf("creating probe pid%d:%s:%s:%s\n", (int)pid, pp->dpp_obj,
	    func, pp->dpp_name);

	sz = sizeof (fasttrap_probe_spec_t) + (isdash ? 4 :
	    (symp->st_size - 1) * sizeof (ftp->ftps_offs[0]));

	if ((ftp = dt_alloc(dtp, sz)) == NULL) {
		dt_dprintf("proc_per_sym: dt_alloc(%lu) failed\n", sz);
		return (1); /* errno is set for us */
	}

	ftp->ftps_pid = pid;
	(void) strncpy(ftp->ftps_func, func, sizeof (ftp->ftps_func));

	dt_pid_objname(ftp->ftps_mod, sizeof (ftp->ftps_mod), pp->dpp_lmid,
	    pp->dpp_obj);

	if (!isdash && gmatch("return", pp->dpp_name)) {
		if (dt_pid_create_return_probe(pp->dpp_pr, dtp, ftp, symp,
		    pp->dpp_stret) < 0) {
			return (dt_pid_error(dtp, pcb, dpr, ftp,
			    D_PROC_CREATEFAIL, "failed to create return probe "
			    "for '%s': %s", func,
			    dtrace_errmsg(dtp, dtrace_errno(dtp))));
		}

		nmatches++;
	}

	if (!isdash && gmatch("entry", pp->dpp_name)) {
		if (dt_pid_create_entry_probe(pp->dpp_pr, dtp, ftp, symp) < 0) {
			return (dt_pid_error(dtp, pcb, dpr, ftp,
			    D_PROC_CREATEFAIL, "failed to create entry probe "
			    "for '%s': %s", func,
			    dtrace_errmsg(dtp, dtrace_errno(dtp))));
		}

		nmatches++;
	}

	glob = strisglob(pp->dpp_name);
	if (!glob && nmatches == 0) {
		off = strtoull(pp->dpp_name, &end, 16);
		if (*end != '\0') {
			return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_NAME,
			    "'%s' is an invalid probe name", pp->dpp_name));
		}

		if (off >= symp->st_size) {
			return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_OFF,
			    "offset 0x%llx outside of function '%s'",
			    (u_longlong_t)off, func));
		}

		err = dt_pid_create_offset_probe(pp->dpp_pr, pp->dpp_dtp, ftp,
		    symp, off);

		if (err == DT_PROC_ERR) {
			return (dt_pid_error(dtp, pcb, dpr, ftp,
			    D_PROC_CREATEFAIL, "failed to create probe at "
			    "'%s+0x%llx': %s", func, (u_longlong_t)off,
			    dtrace_errmsg(dtp, dtrace_errno(dtp))));
		}

		if (err == DT_PROC_ALIGN) {
			return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_ALIGN,
			    "offset 0x%llx is not aligned on an instruction",
			    (u_longlong_t)off));
		}

		nmatches++;

	} else if (glob && !isdash) {
		if (dt_pid_create_glob_offset_probes(pp->dpp_pr,
		    pp->dpp_dtp, ftp, symp, pp->dpp_name) < 0) {
			return (dt_pid_error(dtp, pcb, dpr, ftp,
			    D_PROC_CREATEFAIL,
			    "failed to create offset probes in '%s': %s", func,
			    dtrace_errmsg(dtp, dtrace_errno(dtp))));
		}

		nmatches++;
	}

	pp->dpp_nmatches += nmatches;

	dt_free(dtp, ftp);

	return (0);
}
Beispiel #20
0
/*ARGSUSED*/
int
dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
    fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off)
{
	ftp->ftps_type = DTFTP_OFFSETS;
	ftp->ftps_pc = (uintptr_t)symp->st_value;
	ftp->ftps_size = (size_t)symp->st_size;
	ftp->ftps_noffs = 1;

	if (strcmp("-", ftp->ftps_func) == 0) {
		ftp->ftps_offs[0] = off;
	} else {
		uint8_t *text;
		ulong_t i;
		int size;
#if defined(sun)
		pid_t pid = Pstatus(P)->pr_pid;
		char dmodel = Pstatus(P)->pr_dmodel;
#else
		pid_t pid = proc_getpid(P);
#if __i386__
		char dmodel = PR_MODEL_ILP32;
#elif __amd64__
		char dmodel = PR_MODEL_LP64;
#endif
#endif

		if ((text = malloc(symp->st_size)) == NULL) {
			dt_dprintf("mr sparkle: malloc() failed\n");
			return (DT_PROC_ERR);
		}

		if (Pread(P, text, symp->st_size, symp->st_value) !=
		    symp->st_size) {
			dt_dprintf("mr sparkle: Pread() failed\n");
			free(text);
			return (DT_PROC_ERR);
		}

		/*
		 * We can't instrument offsets in functions with jump tables
		 * as we might interpret a jump table offset as an
		 * instruction.
		 */
		if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) {
			free(text);
			return (0);
		}

		for (i = 0; i < symp->st_size; i += size) {
			if (i == off) {
				ftp->ftps_offs[0] = i;
				break;
			}

			/*
			 * If we've passed the desired offset without a
			 * match, then the given offset must not lie on a
			 * instruction boundary.
			 */
			if (i > off) {
				free(text);
				return (DT_PROC_ALIGN);
			}

			size = dt_instr_size(&text[i], dtp, pid,
			    symp->st_value + i, dmodel);

			/*
			 * If we hit an invalid instruction, bail as if we
			 * couldn't find the offset.
			 */
			if (size <= 0) {
				free(text);
				return (DT_PROC_ALIGN);
			}
		}

		free(text);
	}

	if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
		dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
		    strerror(errno));
		return (dt_set_errno(dtp, errno));
	}

	return (ftp->ftps_noffs);
}