bool
pink_util_putn_safe(pid_t pid, long addr, const char *src, size_t len)
{
	int n, m;
	union {
		long val;
		char x[sizeof(long)];
	} u;

	n = 0;
	m = len / sizeof(long);

	while (n < m) {
		memcpy(u.x, src, sizeof(long));
		if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr + n * ADDR_MUL, NULL)))
			return false;
		if (PINK_GCC_UNLIKELY(!pink_util_pokedata(pid, addr + n * ADDR_MUL, u.val)))
			return false;
		++n;
		src += sizeof(long);
	}

	m = len % sizeof(long);
	if (m) {
		memcpy(u.x, src, m);
		if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr + n * ADDR_MUL, NULL)))
			return false;
		if (PINK_GCC_UNLIKELY(!pink_util_pokedata(pid, addr + n * ADDR_MUL, u.val)))
			return false;
	}

	return true;
}
bool
pink_util_movestr(pid_t pid, long addr, char *dest, size_t len)
{
	int n, m;
	int started = 0;
	union {
		long val;
		char x[sizeof(long)];
	} u;

	if (addr & (sizeof(long) -1)) {
		/* addr not a multiple of sizeof(long) */
		n = addr - (addr & -sizeof(long)); /* residue */
		addr &= -sizeof(long); /* residue */

		if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr, &u.val))) {
			if (PINK_GCC_LIKELY(started && (errno == EPERM || errno == EIO || errno == EFAULT))) {
				/* Ran into end of memory */
				return true;
			}
			/* But if not started, we had a bogus address */
			return false;
		}
		started = 1;
		memcpy(dest, &u.x[n], m = MIN(sizeof(long) - n, len));
		while (n & (sizeof(long) - 1))
			if (u.x[n++] == '\0')
				return true;
		addr += sizeof(long), dest += m, len -= m;
	}
	while (len > 0) {
		if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr, &u.val))) {
			if (PINK_GCC_LIKELY(started && (errno == EPERM || errno == EIO || errno == EFAULT))) {
				/* Ran into end of memory */
				return true;
			}
			/* But if not started, we had a bogus address */
			return false;
		}
		started = 1;
		memcpy(dest, u.x, m = MIN(sizeof(long), len));
		for (unsigned int i = 0; i < sizeof(long); i++)
			if (u.x[i] == '\0')
				return true;
		addr += sizeof(long), dest += m, len -= m;
	}
	return true;
}
bool
pink_util_get_syscall(pid_t pid, pink_bitness_t bitness, long *res)
{
	long parm_offset;
	struct reg r;

	assert(bitness == PINK_BITNESS_32 || bitness == PINK_BITNESS_64);

	if (PINK_GCC_UNLIKELY(!pink_util_get_regs(pid, &r)))
		return false;

	/*
	 * FreeBSD has two special kinds of system call redirections --
	 * SYS_syscall, and SYS___syscall.  The former is the old syscall()
	 * routine, basicly; the latter is for quad-aligned arguments.
	 */
	*res = r.r_rax;
	switch (*res) {
	case SYS_syscall:
	case SYS___syscall:
		if (bitness == PINK_BITNESS_32) {
			parm_offset = r.r_rsp + sizeof(int);
			if (!pink_util_peekdata(pid, parm_offset, res))
				return false;
		}
		else
			*res = r.r_rdi;
		return true;
	default:
		return true;
	}
}
char *
pink_util_movestr_persistent(pid_t pid, long addr)
{
	int n, m, sum, save_errno;
	bool started;
	union {
		long val;
		char x[sizeof(long)];
	} u;
	char *res, *res_ptr;

#define XFREE(x)			\
	do {				\
		if ((x)) {		\
			free((x));	\
		}			\
	} while (0)

	save_errno = errno;
	started = false;
	res = res_ptr = NULL;
	sum = 0;

	if (addr & (sizeof(long) -1)) {
		/* addr not a multiple of sizeof(long) */
		n = addr - (addr & -sizeof(long)); /* residue */
		addr &= -sizeof(long); /* residue */

		if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr, &u.val))) {
			if (PINK_GCC_LIKELY(started && (errno == EPERM || errno == EIO || errno == EFAULT))) {
				/* Ran into end of memory */
				return res;
			}
			/* But if not started, we had a bogus address */
			XFREE(res);
			if (PINK_GCC_UNLIKELY(!started)) {
				/* NULL */
				errno = save_errno;
			}
			return NULL;
		}
		m = sizeof(long) - n;
		sum += m;
		if ((res = realloc(res, sum)) == NULL)
			return NULL;
		res_ptr = started ? res + (sum - m) : res;
		started = true;
		memcpy(res_ptr, &u.x[n], m);
		while (n & (sizeof(long) - 1))
			if (u.x[n++] == '\0')
				return res;
		addr += sizeof(long);
	}

	for (;;) {
		if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr, &u.val))) {
			if (PINK_GCC_LIKELY(started && (errno == EPERM || errno == EIO || errno == EFAULT))) {
				/* Ran into end of memory */
				return res;
			}
			/* But if not started, we had a bogus address */
			XFREE(res);
			if (PINK_GCC_UNLIKELY(!started)) {
				/* NULL */
				errno = save_errno;
			}
			return NULL;
		}
		sum += sizeof(long);
		if ((res = realloc(res, sum)) == NULL)
			return NULL;
		res_ptr = started ? res + (sum - sizeof(long)) : res;
		started = true;
		memcpy(res_ptr, u.x, sizeof(long));
		for (unsigned int i = 0; i < sizeof(long); i++)
			if (u.x[i] == '\0')
				return res;
		addr += sizeof(long);
	}
	/* never reached */
	assert(false);
#undef XFREE
}