diff options
Diffstat (limited to 'usr/src/common')
23 files changed, 3121 insertions, 119 deletions
| diff --git a/usr/src/common/brand/lx/lx_errno.c b/usr/src/common/brand/lx/lx_errno.c new file mode 100644 index 0000000000..269ed470dc --- /dev/null +++ b/usr/src/common/brand/lx/lx_errno.c @@ -0,0 +1,206 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source.  A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * This file contains a mapping table and lookup function for converting + * illumos native error numbers into error numbers appropriate for Linux + * emulation. + * + * The translation table is generated by the "gen_errno", built from and + * documented in "usr/src/common/brand/lx/tools". + */ + +#include <sys/debug.h> + +const int +lx_stol_errno[] = { +	0,	/*   0: No Error					*/ +	1,	/*   1: EPERM		-->   1: EPERM			*/ +	2,	/*   2: ENOENT		-->   2: ENOENT			*/ +	3,	/*   3: ESRCH		-->   3: ESRCH			*/ +	4,	/*   4: EINTR		-->   4: EINTR			*/ +	5,	/*   5: EIO		-->   5: EIO			*/ +	6,	/*   6: ENXIO		-->   6: ENXIO			*/ +	7,	/*   7: E2BIG		-->   7: E2BIG			*/ +	8,	/*   8: ENOEXEC		-->   8: ENOEXEC		*/ +	9,	/*   9: EBADF		-->   9: EBADF			*/ +	10,	/*  10: ECHILD		-->  10: ECHILD			*/ +	11,	/*  11: EAGAIN		-->  11: EAGAIN			*/ +	12,	/*  12: ENOMEM		-->  12: ENOMEM			*/ +	13,	/*  13: EACCES		-->  13: EACCES			*/ +	14,	/*  14: EFAULT		-->  14: EFAULT			*/ +	15,	/*  15: ENOTBLK		-->  15: ENOTBLK		*/ +	16,	/*  16: EBUSY		-->  16: EBUSY			*/ +	17,	/*  17: EEXIST		-->  17: EEXIST			*/ +	18,	/*  18: EXDEV		-->  18: EXDEV			*/ +	19,	/*  19: ENODEV		-->  19: ENODEV			*/ +	20,	/*  20: ENOTDIR		-->  20: ENOTDIR		*/ +	21,	/*  21: EISDIR		-->  21: EISDIR			*/ +	22,	/*  22: EINVAL		-->  22: EINVAL			*/ +	23,	/*  23: ENFILE		-->  23: ENFILE			*/ +	24,	/*  24: EMFILE		-->  24: EMFILE			*/ +	25,	/*  25: ENOTTY		-->  25: ENOTTY			*/ +	26,	/*  26: ETXTBSY		-->  26: ETXTBSY		*/ +	27,	/*  27: EFBIG		-->  27: EFBIG			*/ +	28,	/*  28: ENOSPC		-->  28: ENOSPC			*/ +	29,	/*  29: ESPIPE		-->  29: ESPIPE			*/ +	30,	/*  30: EROFS		-->  30: EROFS			*/ +	31,	/*  31: EMLINK		-->  31: EMLINK			*/ +	32,	/*  32: EPIPE		-->  32: EPIPE			*/ +	33,	/*  33: EDOM		-->  33: EDOM			*/ +	34,	/*  34: ERANGE		-->  34: ERANGE			*/ +	42,	/*  35: ENOMSG		-->  42: ENOMSG			*/ +	43,	/*  36: EIDRM		-->  43: EIDRM			*/ +	44,	/*  37: ECHRNG		-->  44: ECHRNG			*/ +	45,	/*  38: EL2NSYNC	-->  45: EL2NSYNC		*/ +	46,	/*  39: EL3HLT		-->  46: EL3HLT			*/ +	47,	/*  40: EL3RST		-->  47: EL3RST			*/ +	48,	/*  41: ELNRNG		-->  48: ELNRNG			*/ +	49,	/*  42: EUNATCH		-->  49: EUNATCH		*/ +	50,	/*  43: ENOCSI		-->  50: ENOCSI			*/ +	51,	/*  44: EL2HLT		-->  51: EL2HLT			*/ +	35,	/*  45: EDEADLK		-->  35: EDEADLK		*/ +	37,	/*  46: ENOLCK		-->  37: ENOLCK			*/ +	125,	/*  47: ECANCELED	--> 125: ECANCELED		*/ +	38,	/*  48: ENOTSUP		-->  38: ENOSYS			*/ +	122,	/*  49: EDQUOT		--> 122: EDQUOT			*/ +	52,	/*  50: EBADE		-->  52: EBADE			*/ +	53,	/*  51: EBADR		-->  53: EBADR			*/ +	54,	/*  52: EXFULL		-->  54: EXFULL			*/ +	55,	/*  53: ENOANO		-->  55: ENOANO			*/ +	56,	/*  54: EBADRQC		-->  56: EBADRQC		*/ +	57,	/*  55: EBADSLT		-->  57: EBADSLT		*/ +	35,	/*  56: EDEADLOCK	-->  35: EDEADLK		*/ +	59,	/*  57: EBFONT		-->  59: EBFONT			*/ +	130,	/*  58: EOWNERDEAD	--> 130: EOWNERDEAD		*/ +	131,	/*  59: ENOTRECOVERABLE	--> 131: ENOTRECOVERABLE	*/ +	60,	/*  60: ENOSTR		-->  60: ENOSTR			*/ +	61,	/*  61: ENODATA		-->  61: ENODATA		*/ +	62,	/*  62: ETIME		-->  62: ETIME			*/ +	63,	/*  63: ENOSR		-->  63: ENOSR			*/ +	64,	/*  64: ENONET		-->  64: ENONET			*/ +	65,	/*  65: ENOPKG		-->  65: ENOPKG			*/ +	66,	/*  66: EREMOTE		-->  66: EREMOTE		*/ +	67,	/*  67: ENOLINK		-->  67: ENOLINK		*/ +	68,	/*  68: EADV		-->  68: EADV			*/ +	69,	/*  69: ESRMNT		-->  69: ESRMNT			*/ +	70,	/*  70: ECOMM		-->  70: ECOMM			*/ +	71,	/*  71: EPROTO		-->  71: EPROTO			*/ +	-2,	/*  72: ELOCKUNMAPPED	-->  -2: No Analogue		*/ +	-2,	/*  73: ENOTACTIVE	-->  -2: No Analogue		*/ +	72,	/*  74: EMULTIHOP	-->  72: EMULTIHOP		*/ +	-1,	/*  75: Unused Number					*/ +	-1,	/*  76: Unused Number					*/ +	74,	/*  77: EBADMSG		-->  74: EBADMSG		*/ +	36,	/*  78: ENAMETOOLONG	-->  36: ENAMETOOLONG		*/ +	75,	/*  79: EOVERFLOW	-->  75: EOVERFLOW		*/ +	76,	/*  80: ENOTUNIQ	-->  76: ENOTUNIQ		*/ +	77,	/*  81: EBADFD		-->  77: EBADFD			*/ +	78,	/*  82: EREMCHG		-->  78: EREMCHG		*/ +	79,	/*  83: ELIBACC		-->  79: ELIBACC		*/ +	80,	/*  84: ELIBBAD		-->  80: ELIBBAD		*/ +	81,	/*  85: ELIBSCN		-->  81: ELIBSCN		*/ +	82,	/*  86: ELIBMAX		-->  82: ELIBMAX		*/ +	83,	/*  87: ELIBEXEC	-->  83: ELIBEXEC		*/ +	84,	/*  88: EILSEQ		-->  84: EILSEQ			*/ +	38,	/*  89: ENOSYS		-->  38: ENOSYS			*/ +	40,	/*  90: ELOOP		-->  40: ELOOP			*/ +	85,	/*  91: ERESTART	-->  85: ERESTART		*/ +	86,	/*  92: ESTRPIPE	-->  86: ESTRPIPE		*/ +	39,	/*  93: ENOTEMPTY	-->  39: ENOTEMPTY		*/ +	87,	/*  94: EUSERS		-->  87: EUSERS			*/ +	88,	/*  95: ENOTSOCK	-->  88: ENOTSOCK		*/ +	89,	/*  96: EDESTADDRREQ	-->  89: EDESTADDRREQ		*/ +	90,	/*  97: EMSGSIZE	-->  90: EMSGSIZE		*/ +	91,	/*  98: EPROTOTYPE	-->  91: EPROTOTYPE		*/ +	92,	/*  99: ENOPROTOOPT	-->  92: ENOPROTOOPT		*/ +	-1,	/* 100: Unused Number					*/ +	-1,	/* 101: Unused Number					*/ +	-1,	/* 102: Unused Number					*/ +	-1,	/* 103: Unused Number					*/ +	-1,	/* 104: Unused Number					*/ +	-1,	/* 105: Unused Number					*/ +	-1,	/* 106: Unused Number					*/ +	-1,	/* 107: Unused Number					*/ +	-1,	/* 108: Unused Number					*/ +	-1,	/* 109: Unused Number					*/ +	-1,	/* 110: Unused Number					*/ +	-1,	/* 111: Unused Number					*/ +	-1,	/* 112: Unused Number					*/ +	-1,	/* 113: Unused Number					*/ +	-1,	/* 114: Unused Number					*/ +	-1,	/* 115: Unused Number					*/ +	-1,	/* 116: Unused Number					*/ +	-1,	/* 117: Unused Number					*/ +	-1,	/* 118: Unused Number					*/ +	-1,	/* 119: Unused Number					*/ +	93,	/* 120: EPROTONOSUPPORT	-->  93: EPROTONOSUPPORT	*/ +	94,	/* 121: ESOCKTNOSUPPORT	-->  94: ESOCKTNOSUPPORT	*/ +	95,	/* 122: EOPNOTSUPP	-->  95: EOPNOTSUPP		*/ +	96,	/* 123: EPFNOSUPPORT	-->  96: EPFNOSUPPORT		*/ +	97,	/* 124: EAFNOSUPPORT	-->  97: EAFNOSUPPORT		*/ +	98,	/* 125: EADDRINUSE	-->  98: EADDRINUSE		*/ +	99,	/* 126: EADDRNOTAVAIL	-->  99: EADDRNOTAVAIL		*/ +	100,	/* 127: ENETDOWN	--> 100: ENETDOWN		*/ +	101,	/* 128: ENETUNREACH	--> 101: ENETUNREACH		*/ +	102,	/* 129: ENETRESET	--> 102: ENETRESET		*/ +	103,	/* 130: ECONNABORTED	--> 103: ECONNABORTED		*/ +	104,	/* 131: ECONNRESET	--> 104: ECONNRESET		*/ +	105,	/* 132: ENOBUFS		--> 105: ENOBUFS		*/ +	106,	/* 133: EISCONN		--> 106: EISCONN		*/ +	107,	/* 134: ENOTCONN	--> 107: ENOTCONN		*/ +	-1,	/* 135: Unused Number					*/ +	-1,	/* 136: Unused Number					*/ +	-1,	/* 137: Unused Number					*/ +	-1,	/* 138: Unused Number					*/ +	-1,	/* 139: Unused Number					*/ +	-1,	/* 140: Unused Number					*/ +	-1,	/* 141: Unused Number					*/ +	-1,	/* 142: Unused Number					*/ +	108,	/* 143: ESHUTDOWN	--> 108: ESHUTDOWN		*/ +	109,	/* 144: ETOOMANYREFS	--> 109: ETOOMANYREFS		*/ +	110,	/* 145: ETIMEDOUT	--> 110: ETIMEDOUT		*/ +	111,	/* 146: ECONNREFUSED	--> 111: ECONNREFUSED		*/ +	112,	/* 147: EHOSTDOWN	--> 112: EHOSTDOWN		*/ +	113,	/* 148: EHOSTUNREACH	--> 113: EHOSTUNREACH		*/ +	114,	/* 149: EALREADY	--> 114: EALREADY		*/ +	115,	/* 150: EINPROGRESS	--> 115: EINPROGRESS		*/ +	116	/* 151: ESTALE		--> 116: ESTALE			*/ +}; + +/* + * Convert an illumos native error number to a Linux error number and return + * it.  If no valid conversion is possible, the function fails back to the + * value of "defval".  In userland, passing a default error number of "-1" + * will abort the program if the error number could not be converted. + */ +int +lx_errno(int native_errno, int defval) +{ +#ifdef	_KERNEL +	VERIFY3S(defval, >=, 0); +#endif + +	if (native_errno < 0 || native_errno >= (sizeof (lx_stol_errno) / +	    sizeof (lx_stol_errno[0]))) { +#ifndef	_KERNEL +		VERIFY3S(defval, >=, 0); +#endif + +		return (defval); +	} + +	return (lx_stol_errno[native_errno]); +} diff --git a/usr/src/common/brand/lx/lx_errno.h b/usr/src/common/brand/lx/lx_errno.h new file mode 100644 index 0000000000..10b6b3066c --- /dev/null +++ b/usr/src/common/brand/lx/lx_errno.h @@ -0,0 +1,29 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source.  A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _LX_ERRNO_H +#define	_LX_ERRNO_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int lx_errno(int, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _LX_ERRNO_H */ diff --git a/usr/src/common/brand/lx/lx_signum.c b/usr/src/common/brand/lx/lx_signum.c new file mode 100644 index 0000000000..755600911a --- /dev/null +++ b/usr/src/common/brand/lx/lx_signum.c @@ -0,0 +1,317 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2010 Sun Microsystems, Inc.  All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. + */ + +#include <sys/signal.h> +#include <sys/lx_siginfo.h> +#include <lx_signum.h> +#include <sys/debug.h> + +/* + * Delivering signals to a Linux process is complicated by differences in + * signal numbering, stack structure and contents, and the action taken when a + * signal handler exits.  In addition, many signal-related structures, such as + * sigset_ts, vary between Solaris and Linux. + * + * The simplest transformation that must be done when sending signals is to + * translate between Linux and Solaris signal numbers. + * + * These are the major signal number differences between Linux and Solaris: + * + * 	==================================== + * 	| Number |   Linux    |  Solaris   | + * 	| ====== | =========  | ========== | + *	|    7   | SIGBUS     | SIGEMT     | + *	|   10   | SIGUSR1    | SIGBUS     | + *	|   12   | SIGUSR2    | SIGSYS     | + *	|   16   | SIGSTKFLT  | SIGUSR1    | + *	|   17   | SIGCHLD    | SIGUSR2    | + * 	|   18   | SIGCONT    | SIGCHLD    | + *	|   19   | SIGSTOP    | SIGPWR     | + * 	|   20   | SIGTSTP    | SIGWINCH   | + * 	|   21   | SIGTTIN    | SIGURG     | + * 	|   22   | SIGTTOU    | SIGPOLL    | + *	|   23   | SIGURG     | SIGSTOP    | + * 	|   24   | SIGXCPU    | SIGTSTP    | + *	|   25   | SIGXFSZ    | SIGCONT    | + *	|   26   | SIGVTALARM | SIGTTIN    | + *	|   27   | SIGPROF    | SIGTTOU    | + *	|   28   | SIGWINCH   | SIGVTALARM | + *	|   29   | SIGPOLL    | SIGPROF    | + *	|   30   | SIGPWR     | SIGXCPU    | + *	|   31   | SIGSYS     | SIGXFSZ    | + * 	==================================== + * + * Not every Linux signal maps to a Solaris signal, nor does every Solaris + * signal map to a Linux counterpart. However, when signals do map, the + * mapping is unique. + * + * One mapping issue is that Linux supports 33 real time signals, with SIGRTMIN + * typically starting at or near 32 (SIGRTMIN) and proceeding to 64 (SIGRTMAX) + * (SIGRTMIN is "at or near" 32 because glibc usually "steals" one ore more of + * these signals for its own internal use, adjusting SIGRTMIN and SIGRTMAX as + * needed.)  Conversely, Solaris actively uses signals 32-40 for other purposes + * and supports exactly 32 real time signals, in the range 41 (SIGRTMIN) + * to 72 (SIGRTMAX). + * + * At present, attempting to translate a Linux signal equal to 63 + * will generate an error (we allow SIGRTMAX because a program + * should be able to send SIGRTMAX without getting an EINVAL, though obviously + * anything that loops through the signals from SIGRTMIN to SIGRTMAX will + * fail.) + * + * Similarly, attempting to translate a native Solaris signal in the range + * 32-40 will also generate an error as we don't want to support the receipt of + * those signals from the Solaris global zone. + */ + +/* + * Linux to Solaris signal map + * + * Usage:  solaris_signal = ltos_signum[lx_signal]; + */ +const int +ltos_signo[LX_NSIG + 1] = { +	0, +	SIGHUP, +	SIGINT, +	SIGQUIT, +	SIGILL, +	SIGTRAP, +	SIGABRT, +	SIGBUS, +	SIGFPE, +	SIGKILL, +	SIGUSR1, +	SIGSEGV, +	SIGUSR2, +	SIGPIPE, +	SIGALRM, +	SIGTERM, +	SIGEMT,			/* 16:  Linux SIGSTKFLT; use Solaris SIGEMT */ +	SIGCHLD, +	SIGCONT, +	SIGSTOP, +	SIGTSTP, +	SIGTTIN, +	SIGTTOU, +	SIGURG, +	SIGXCPU, +	SIGXFSZ, +	SIGVTALRM, +	SIGPROF, +	SIGWINCH, +	SIGPOLL, +	SIGPWR, +	SIGSYS, +	_SIGRTMIN,		/* 32:  Linux SIGRTMIN */ +	_SIGRTMIN + 1, +	_SIGRTMIN + 2, +	_SIGRTMIN + 3, +	_SIGRTMIN + 4, +	_SIGRTMIN + 5, +	_SIGRTMIN + 6, +	_SIGRTMIN + 7, +	_SIGRTMIN + 8, +	_SIGRTMIN + 9, +	_SIGRTMIN + 10, +	_SIGRTMIN + 11, +	_SIGRTMIN + 12, +	_SIGRTMIN + 13, +	_SIGRTMIN + 14, +	_SIGRTMIN + 15, +	_SIGRTMIN + 16, +	_SIGRTMIN + 17, +	_SIGRTMIN + 18, +	_SIGRTMIN + 19, +	_SIGRTMIN + 20, +	_SIGRTMIN + 21, +	_SIGRTMIN + 22, +	_SIGRTMIN + 23, +	_SIGRTMIN + 24, +	_SIGRTMIN + 25, +	_SIGRTMIN + 26, +	_SIGRTMIN + 27, +	_SIGRTMIN + 28, +	_SIGRTMIN + 29, +	_SIGRTMIN + 30, +	_SIGRTMIN + 31, +	_SIGRTMAX,		/* 64:  Linux SIGRTMAX */ +}; + +/* + * Solaris to Linux signal map + * + * Usage:  lx_signal = stol_signo[solaris_signal]; + */ +const int +stol_signo[NSIG] = { +	0, +	LX_SIGHUP, +	LX_SIGINT, +	LX_SIGQUIT, +	LX_SIGILL, +	LX_SIGTRAP, +	LX_SIGABRT, +	LX_SIGSTKFLT,		/* 7:  Solaris SIGEMT; use for LX_SIGSTKFLT */ +	LX_SIGFPE, +	LX_SIGKILL, +	LX_SIGBUS, +	LX_SIGSEGV, +	LX_SIGSYS, +	LX_SIGPIPE, +	LX_SIGALRM, +	LX_SIGTERM, +	LX_SIGUSR1, +	LX_SIGUSR2, +	LX_SIGCHLD, +	LX_SIGPWR, +	LX_SIGWINCH, +	LX_SIGURG, +	LX_SIGPOLL, +	LX_SIGSTOP, +	LX_SIGTSTP, +	LX_SIGCONT, +	LX_SIGTTIN, +	LX_SIGTTOU, +	LX_SIGVTALRM, +	LX_SIGPROF, +	LX_SIGXCPU, +	LX_SIGXFSZ, +	-1,			/* 32:  Solaris SIGWAITING */ +	-1,			/* 33:  Solaris SIGLWP */ +	-1,			/* 34:  Solaris SIGFREEZE */ +	-1,			/* 35:  Solaris SIGTHAW */ +	-1,			/* 36:  Solaris SIGCANCEL */ +	-1,			/* 37:  Solaris SIGLOST */ +	-1,			/* 38:  Solaris SIGXRES */ +	-1,			/* 39:  Solaris SIGJVM1 */ +	-1,			/* 40:  Solaris SIGJVM2 */ +	-1,			/* 41:  Solaris SIGINFO */ +	LX_SIGRTMIN,		/* 42:  Solaris _SIGRTMIN */ +	LX_SIGRTMIN + 1, +	LX_SIGRTMIN + 2, +	LX_SIGRTMIN + 3, +	LX_SIGRTMIN + 4, +	LX_SIGRTMIN + 5, +	LX_SIGRTMIN + 6, +	LX_SIGRTMIN + 7, +	LX_SIGRTMIN + 8, +	LX_SIGRTMIN + 9, +	LX_SIGRTMIN + 10, +	LX_SIGRTMIN + 11, +	LX_SIGRTMIN + 12, +	LX_SIGRTMIN + 13, +	LX_SIGRTMIN + 14, +	LX_SIGRTMIN + 15, +	LX_SIGRTMIN + 16, +	LX_SIGRTMIN + 17, +	LX_SIGRTMIN + 18, +	LX_SIGRTMIN + 19, +	LX_SIGRTMIN + 20, +	LX_SIGRTMIN + 21, +	LX_SIGRTMIN + 22, +	LX_SIGRTMIN + 23, +	LX_SIGRTMIN + 24, +	LX_SIGRTMIN + 25, +	LX_SIGRTMIN + 26, +	LX_SIGRTMIN + 27, +	LX_SIGRTMIN + 28, +	LX_SIGRTMIN + 29, +	LX_SIGRTMIN + 30, +	LX_SIGRTMIN + 31, +	LX_SIGRTMAX,		/* 74: Solaris _SIGRTMAX */ +}; + +/* + * Convert an illumos native signal number to a Linux signal number and return + * it.  If no valid conversion is possible, the function fails back to the + * value of "defsig".  In userland, passing a default signal number of "-1" + * will abort the program if the signal number could not be converted. + */ +int +lx_stol_signo(int signo, int defsig) +{ +	int rval; + +#ifdef	_KERNEL +	VERIFY3S(defsig, >=, 0); +#endif + +	if (signo < 0 || signo >= NSIG || (rval = stol_signo[signo]) < 1) { +#ifndef	_KERNEL +		VERIFY3S(defsig, >=, 0); +#endif +		return (defsig); +	} + +	return (rval); +} + +/* + * Convert the "status" field of a SIGCLD siginfo_t.  We need to extract the + * illumos signal number and convert it to a Linux signal number while leaving + * the ptrace(2) event bits intact.  In userland, passing a default signal + * number of "-1" will abort the program if the signal number could not be + * converted, as for lx_stol_signo(). + */ +int +lx_stol_status(int s, int defsig) +{ +	/* +	 * We mask out the top bit here in case PTRACE_O_TRACESYSGOOD +	 * is in use and 0x80 has been ORed with the signal number. +	 */ +	int stat = lx_stol_signo(s & 0x7f, defsig); + +	/* +	 * We must mix in the ptrace(2) event which may be stored in +	 * the second byte of the status code.  We also re-include the +	 * PTRACE_O_TRACESYSGOOD bit. +	 */ +	return ((s & 0xff80) | stat); +} + +int +lx_stol_sigcode(int code) +{ +	switch (code) { +	case SI_USER: +		return (LX_SI_USER); +	case SI_LWP: +		return (LX_SI_TKILL); +	case SI_QUEUE: +		return (LX_SI_QUEUE); +	case SI_TIMER: +		return (LX_SI_TIMER); +	case SI_ASYNCIO: +		return (LX_SI_ASYNCIO); +	case SI_MESGQ: +		return (LX_SI_MESGQ); +	default: +		return (code); +	} +} diff --git a/usr/src/common/brand/lx/lx_signum.h b/usr/src/common/brand/lx/lx_signum.h new file mode 100644 index 0000000000..a7807c2b07 --- /dev/null +++ b/usr/src/common/brand/lx/lx_signum.h @@ -0,0 +1,85 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc.  All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _LX_SIGNUM_H +#define	_LX_SIGNUM_H + +#ifdef	__cplusplus +extern "C" { +#endif + +#define	LX_SIGHUP	1 +#define	LX_SIGINT	2 +#define	LX_SIGQUIT	3 +#define	LX_SIGILL	4 +#define	LX_SIGTRAP	5 +#define	LX_SIGABRT	6 +#define	LX_SIGIOT	6 +#define	LX_SIGBUS	7 +#define	LX_SIGFPE	8 +#define	LX_SIGKILL	9 +#define	LX_SIGUSR1	10 +#define	LX_SIGSEGV	11 +#define	LX_SIGUSR2	12 +#define	LX_SIGPIPE	13 +#define	LX_SIGALRM	14 +#define	LX_SIGTERM	15 +#define	LX_SIGSTKFLT	16 +#define	LX_SIGCHLD	17 +#define	LX_SIGCONT	18 +#define	LX_SIGSTOP	19 +#define	LX_SIGTSTP	20 +#define	LX_SIGTTIN	21 +#define	LX_SIGTTOU	22 +#define	LX_SIGURG	23 +#define	LX_SIGXCPU	24 +#define	LX_SIGXFSZ	25 +#define	LX_SIGVTALRM	26 +#define	LX_SIGPROF	27 +#define	LX_SIGWINCH	28 +#define	LX_SIGIO	29 +#define	LX_SIGPOLL	LX_SIGIO +#define	LX_SIGPWR	30 +#define	LX_SIGSYS	31 +#define	LX_SIGUNUSED	31 + +#define	LX_NSIG		64	/* Linux _NSIG */ + +#define	LX_SIGRTMIN	32 +#define	LX_SIGRTMAX	LX_NSIG + +extern const int ltos_signo[]; +extern const int stol_signo[]; + +extern int lx_stol_signo(int, int); +extern int lx_stol_status(int, int); +extern int lx_stol_sigcode(int); + +#ifdef	__cplusplus +} +#endif + +#endif	/* _LX_SIGNUM_H */ diff --git a/usr/src/common/brand/lx/lx_syscall.h b/usr/src/common/brand/lx/lx_syscall.h new file mode 100644 index 0000000000..e9d06fd9bc --- /dev/null +++ b/usr/src/common/brand/lx/lx_syscall.h @@ -0,0 +1,94 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source.  A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef	_LX_SYSCALL_H +#define	_LX_SYSCALL_H + +#include <sys/lx_brand.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The br_scall_args field of lx_lwp_data is going to be populated with + * pointers to structs. The types of these structs should be defined in this + * header file.  These are Linux specific arguments to system calls that don't + * exist in illumos. Each section should be labelled with which system call it + * belongs to. + */ + +/* arguments for waitpid(2) */ +/* see comments in usr/src/lib/brand/lx/lx_brand/common/wait.c */ +#define	LX_WNOTHREAD	0x20000000 /* Do not wait on siblings' children */ +#define	LX_WALL		0x40000000 /* Wait on all children */ +#define	LX_WCLONE	0x80000000 /* Wait only on clone children */ + +/* For arch_prctl(2) */ +#define	LX_ARCH_SET_GS	0x1001 +#define	LX_ARCH_SET_FS	0x1002 +#define	LX_ARCH_GET_FS	0x1003 +#define	LX_ARCH_GET_GS	0x1004 + +/* + * For ptrace(2): + */ +#define	LX_PTRACE_TRACEME	0 +#define	LX_PTRACE_PEEKTEXT	1 +#define	LX_PTRACE_PEEKDATA	2 +#define	LX_PTRACE_PEEKUSER	3 +#define	LX_PTRACE_POKETEXT	4 +#define	LX_PTRACE_POKEDATA	5 +#define	LX_PTRACE_POKEUSER	6 +#define	LX_PTRACE_CONT		7 +#define	LX_PTRACE_KILL		8 +#define	LX_PTRACE_SINGLESTEP	9 +#define	LX_PTRACE_GETREGS	12 +#define	LX_PTRACE_SETREGS	13 +#define	LX_PTRACE_GETFPREGS	14 +#define	LX_PTRACE_SETFPREGS	15 +#define	LX_PTRACE_ATTACH	16 +#define	LX_PTRACE_DETACH	17 +#define	LX_PTRACE_GETFPXREGS	18 +#define	LX_PTRACE_SETFPXREGS	19 +#define	LX_PTRACE_SYSCALL	24 +#define	LX_PTRACE_SETOPTIONS	0x4200 +#define	LX_PTRACE_GETEVENTMSG	0x4201 + +/* + * For clone(2): + */ +#define	LX_CSIGNAL		0x000000ff +#define	LX_CLONE_VM		0x00000100 +#define	LX_CLONE_FS		0x00000200 +#define	LX_CLONE_FILES		0x00000400 +#define	LX_CLONE_SIGHAND	0x00000800 +#define	LX_CLONE_PID		0x00001000 +#define	LX_CLONE_PTRACE		0x00002000 +#define	LX_CLONE_VFORK		0x00004000 +#define	LX_CLONE_PARENT		0x00008000 +#define	LX_CLONE_THREAD		0x00010000 +#define	LX_CLONE_SYSVSEM	0x00040000 +#define	LX_CLONE_SETTLS		0x00080000 +#define	LX_CLONE_PARENT_SETTID	0x00100000 +#define	LX_CLONE_CHILD_CLEARTID	0x00200000 +#define	LX_CLONE_DETACH		0x00400000 +#define	LX_CLONE_CHILD_SETTID	0x01000000 + +#ifdef	__cplusplus +} +#endif + +#endif	/* _LX_SYSCALL_H */ diff --git a/usr/src/common/brand/lx/tools/Makefile b/usr/src/common/brand/lx/tools/Makefile new file mode 100644 index 0000000000..5ad1240c55 --- /dev/null +++ b/usr/src/common/brand/lx/tools/Makefile @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source.  A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +PROG =			gen_errno + +include ../../../../cmd/Makefile.cmd + +OBJS =			gen_errno.o + +CLOBBERFILES +=		$(PROG) + +NATIVECC_CFLAGS +=	$(CFLAGS) $(CCVERBOSE) +NATIVECC_LDLIBS +=	-lcmdutils -lnvpair + +.KEEP_STATE: + +all: $(PROG) + +install: all + +lint:	lint_PROG + +clean: +	$(RM) $(OBJS) + +$(PROG): $(OBJS) +	$(NATIVECC) $(NATIVECC_CFLAGS) $(NATIVECC_LDLIBS) $(OBJS) -o $@ +	$(POST_PROCESS) + +include ../../../../cmd/Makefile.targ diff --git a/usr/src/common/brand/lx/tools/README.md b/usr/src/common/brand/lx/tools/README.md new file mode 100644 index 0000000000..5e4976f200 --- /dev/null +++ b/usr/src/common/brand/lx/tools/README.md @@ -0,0 +1,39 @@ +# Updating Error Number Translations + +To create an updated error number translation table, you can use the +`gen_errno` tool.  This tool requires, as input: + +* the illumos native `errno.h` file +* a set of foreign operating system `errno.h` files + +The output is a set of translation table entries suitable for inclusion in a +cstyled C array.  The index of the array is the native error number and the +value at each index is the translated error number for use with the foreign +operating system. + +## Example + +To generate a translation table for the LX Brand, you will require two files +from the current Linux source: + +* `include/uapi/asm-generic/errno-base.h` (low-valued, or base, error numbers) +* `include/uapi/asm-generic/errno.h` (extended error numbers) + +Assuming the files are in the current directory, you should run the tool as +follows: + +    $ dmake +    ... +    $ ./gen_errno -F errno-base.h -F errno.h \ +                  -N $SRC/uts/common/sys/errno.h +    0, /*  0: No Error                            */ +    1, /*  1: EPERM       -->   1: EPERM          */ +    2, /*  2: ENOENT      -->   2: ENOENT         */ +    3, /*  3: ESRCH       -->   3: ESRCH          */ +    4, /*  4: EINTR       -->   4: EINTR          */ +    5, /*  5: EIO         -->   5: EIO            */ +    6, /*  6: ENXIO       -->   6: ENXIO          */ +    7, /*  7: E2BIG       -->   7: E2BIG          */ +    ... + +The output may be used in the `$SRC/common/brand/lx/lx_errno.c` file. diff --git a/usr/src/common/brand/lx/tools/gen_errno.c b/usr/src/common/brand/lx/tools/gen_errno.c new file mode 100644 index 0000000000..1edd924edf --- /dev/null +++ b/usr/src/common/brand/lx/tools/gen_errno.c @@ -0,0 +1,450 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source.  A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Take the error number definitions from a foreign system and generate a + * translation table that converts illumos native error numbers to foreign + * system error numbers. + */ + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <err.h> +#include <sys/sysmacros.h> +#include <libcmdutils.h> +#include <libnvpair.h> + +nvlist_t *native_errors; +nvlist_t *foreign_errors; + +struct override { +	const char *ovr_from; +	const char *ovr_to; +} overrides[] = { +	{ "ENOTSUP", "ENOSYS" }, +	{ 0 } +}; + +static const char * +lookup_override(const char *from) +{ +	int i; + +	for (i = 0; overrides[i].ovr_from != NULL; i++) { +		if (strcmp(overrides[i].ovr_from, from) == 0) { +			return (overrides[i].ovr_to); +		} +	} + +	return (NULL); +} + +static int +parse_int(const char *number, int *rval) +{ +	long n; +	char *endpos; + +	errno = 0; +	if ((n = strtol(number, &endpos, 10)) == 0 && errno != 0) { +		return (-1); +	} + +	if (endpos != NULL && *endpos != '\0') { +		errno = EINVAL; +		return (-1); +	} + +	if (n > INT_MAX || n < INT_MIN) { +		errno = EOVERFLOW; +		return (-1); +	} + +	*rval = (int)n; +	return (0); +} + +static int +errnum_add(nvlist_t *nvl, const char *name, const char *number) +{ +	int val; + +	if (nvlist_exists(nvl, name)) { +		(void) fprintf(stderr, "ERROR: duplicate definition: %s -> " +		    "%s\n", name, number); +		errno = EEXIST; +		return (-1); +	} + +	/* +	 * Try and parse the error number: +	 */ +	if (parse_int(number, &val) == 0) { +		/* +		 * The name refers to a number. +		 */ +		if (nvlist_add_int32(nvl, name, val) != 0) { +			(void) fprintf(stderr, "ERROR: nvlist_add_int32: %s\n", +			    strerror(errno)); +			return (-1); +		} +	} else { +		/* +		 * The name refers to another definition. +		 */ +		if (nvlist_add_string(nvl, name, number) != 0) { +			(void) fprintf(stderr, "ERROR: nvlist_add_string: %s\n", +			    strerror(errno)); +			return (-1); +		} +	} + +	return (0); +} + +static int +errnum_max(nvlist_t *nvl) +{ +	int max = 0; +	nvpair_t *nvp = NULL; + +	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { +		if (nvpair_type(nvp) != DATA_TYPE_INT32) { +			continue; +		} + +		max = MAX(fnvpair_value_int32(nvp), max); +	} + +	return (max); +} + +static int +errname_by_num(nvlist_t *nvl, int num, const char **name) +{ +	nvpair_t *nvp = NULL; + +	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { +		if (nvpair_type(nvp) != DATA_TYPE_INT32) { +			continue; +		} + +		if (fnvpair_value_int32(nvp) == num) { +			*name = nvpair_name(nvp); +			return (0); +		} +	} + +	errno = ENOENT; +	return (-1); +} + +static int +errno_by_name(nvlist_t *nvl, const char *name, int *rval, const char **rname) +{ +	nvpair_t *nvp = NULL; + +	if (nvlist_lookup_nvpair(nvl, name, &nvp) != 0) { +		errno = ENOENT; +		return (-1); +	} + +	if (nvpair_type(nvp) == DATA_TYPE_STRING) { +		return (errno_by_name(nvl, fnvpair_value_string(nvp), rval, +		    rname)); +	} else { +		*rval = fnvpair_value_int32(nvp); +		if (rname != NULL) { +			*rname = name; +		} +		return (0); +	} +} + +static int +process_line(const char *line, nvlist_t *nvl) +{ +	custr_t *nam = NULL, *num = NULL; +	const char *c = line; + +	if (custr_alloc(&nam) != 0 || custr_alloc(&num) != 0) { +		int en = errno; + +		if (nam != NULL) { +			custr_free(nam); +		} +		if (num != NULL) { +			custr_free(num); +		} + +		errno = en; +		return (-1); +	} + +	/* +	 * Valid lines begin with "#define": +	 */ +	if (*c++ != '#' || *c++ != 'd' || *c++ != 'e' || *c++ != 'f' || +	    *c++ != 'i' || *c++ != 'n' || *c++ != 'e') { +		return (0); +	} + +	/* +	 * Eat whitespace: +	 */ +	for (;;) { +		if (*c == '\0') { +			return (0); +		} + +		if (*c != ' ' && *c != '\t') { +			break; +		} + +		c++; +	} + +	/* +	 * Read error number token: +	 */ +	for (;;) { +		if (*c == '\0') { +			return (0); +		} + +		if (*c == ' ' || *c == '\t') { +			break; +		} + +		if (custr_appendc(nam, *c) != 0) { +			return (-1); +		} + +		c++; +	} + +	/* +	 * Eat whitespace: +	 */ +	for (;;) { +		if (*c == '\0') { +			return (0); +		} + +		if (*c != ' ' && *c != '\t') { +			break; +		} + +		c++; +	} + +	/* +	 * Read error number token: +	 */ +	for (;;) { +		if (*c == '\0') { +			break; +		} + +		if (*c == ' ' || *c == '\t') { +			break; +		} + +		if (custr_appendc(num, *c) != 0) { +			return (-1); +		} + +		c++; +	} + +	return (errnum_add(nvl, custr_cstr(nam), custr_cstr(num))); +} + +static int +read_file_into_list(const char *path, nvlist_t *nvl) +{ +	int rval = 0, en = 0; +	FILE *f; +	custr_t *cu = NULL; + +	if (custr_alloc(&cu) != 0 || custr_append(cu, "") != 0) { +		if (cu != NULL) { +			custr_free(cu); +		} +		return (-1); +	} + +	if ((f = fopen(path, "r")) == NULL) { +		return (-1); +	} + +	for (;;) { +		int c; + +		errno = 0; +		switch (c = fgetc(f)) { +		case '\n': +		case EOF: +			if (errno != 0) { +				en = errno; +				rval = -1; +				goto out; +			} +			if (process_line(custr_cstr(cu), nvl) != 0) { +				en = errno; +				rval = -1; +				goto out; +			} +			custr_reset(cu); +			if (c == EOF) { +				goto out; +			} +			break; + +		case '\r': +		case '\0': +			/* +			 * Ignore these characters. +			 */ +			break; + +		default: +			if (custr_appendc(cu, c) != 0) { +				en = errno; +				rval = -1; +				goto out; +			} +			break; +		} +	} + +out: +	(void) fclose(f); +	custr_free(cu); +	errno = en; +	return (rval); +} + +int +main(int argc, char **argv) +{ +	int max; +	int fval; +	int c; + +	if (nvlist_alloc(&native_errors, NV_UNIQUE_NAME, 0) != 0 || +	    nvlist_alloc(&foreign_errors, NV_UNIQUE_NAME, 0) != 0) { +		err(1, "could not allocate memory"); +	} + +	while ((c = getopt(argc, argv, ":N:F:")) != -1) { +		switch (c) { +		case 'N': +			if (read_file_into_list(optarg, native_errors) != 0) { +				err(1, "could not read file: %s", optarg); +			} +			break; + +		case 'F': +			if (read_file_into_list(optarg, foreign_errors) != 0) { +				err(1, "could not read file: %s", optarg); +			} +			break; + +		case ':': +			errx(1, "option -%c requires an operand", c); +			break; + +		case '?': +			errx(1, "option -%c unrecognised", c); +			break; +		} +	} + +	/* +	 * Print an array entry for each error number: +	 */ +	max = errnum_max(native_errors); +	for (fval = 0; fval <= max; fval++) { +		const char *fname; +		const char *tname = NULL; +		int32_t tval; +		const char *msg = NULL; +		const char *comma = (fval != max) ? "," : ""; + +		if (errname_by_num(native_errors, fval, &fname) == -1) { +			fname = NULL; +		} + +		if (fval == 0) { +			/* +			 * The error number "0" is special: it means no worries. +			 */ +			msg = "No Error"; +			tval = 0; +		} else if (fname == NULL) { +			/* +			 * There is no defined name for this error number; it +			 * is unused. +			 */ +			msg = "Unused Number"; +			tval = -1; +		} else { +			/* +			 * Check if we want to override the name of this error +			 * in the foreign error number lookup: +			 */ +			const char *oname = lookup_override(fname); + +			/* +			 * Do the lookup: +			 */ +			if (errno_by_name(foreign_errors, oname != NULL ? +			    oname : fname, &tval, &tname) != 0) { +				/* +				 * There was no foreign error number by that +				 * name. +				 */ +				tname = "No Analogue"; +				tval = -2; +			} +		} + +		if (msg == NULL) { +			size_t flen = strlen(fname); +			size_t tlen = strlen(tname); +			const char *t = flen > 7 ? "\t" : "\t\t"; +			const char *tt = tlen < 7 ? "\t\t\t" : tlen < 15 ? +			    "\t\t" : "\t"; + +			(void) fprintf(stdout, "\t%d%s\t/* %3d: %s%s--> %3d: " +			    "%s%s*/\n", tval, comma, fval, fname, t, tval, +			    tname, tt); +		} else { +			const char *t = "\t\t\t\t\t"; + +			(void) fprintf(stdout, "\t%d%s\t/* %3d: %s%s*/\n", tval, +			    comma, fval, msg, t); +		} +	} + +	(void) nvlist_free(native_errors); +	(void) nvlist_free(foreign_errors); + +	return (0); +} diff --git a/usr/src/common/ctf/ctf_create.c b/usr/src/common/ctf/ctf_create.c index 239d166f44..b51765dd0c 100644 --- a/usr/src/common/ctf/ctf_create.c +++ b/usr/src/common/ctf/ctf_create.c @@ -25,7 +25,7 @@   * Use is subject to license terms.   */  /* - * Copyright (c) 2013, Joyent, Inc.  All rights reserved. + * Copyright (c) 2015, Joyent, Inc.   */  #include <sys/sysmacros.h> @@ -86,6 +86,43 @@ ctf_create(int *errp)  	return (fp);  } +ctf_file_t * +ctf_fdcreate(int fd, int *errp) +{ +	ctf_file_t *fp; +	static const ctf_header_t hdr = { { CTF_MAGIC, CTF_VERSION, 0 } }; + +	const ulong_t hashlen = 128; +	ctf_dtdef_t **hash = ctf_alloc(hashlen * sizeof (ctf_dtdef_t *)); +	ctf_sect_t cts; + +	if (hash == NULL) +		return (ctf_set_open_errno(errp, EAGAIN)); + +	cts.cts_name = _CTF_SECTION; +	cts.cts_type = SHT_PROGBITS; +	cts.cts_flags = 0; +	cts.cts_data = &hdr; +	cts.cts_size = sizeof (hdr); +	cts.cts_entsize = 1; +	cts.cts_offset = 0; + +	if ((fp = ctf_fdcreate_int(fd, errp, &cts)) == NULL) { +		ctf_free(hash, hashlen * sizeof (ctf_dtdef_t *)); +		return (NULL); +	} + +	fp->ctf_flags |= LCTF_RDWR; +	fp->ctf_dthashlen = hashlen; +	bzero(hash, hashlen * sizeof (ctf_dtdef_t *)); +	fp->ctf_dthash = hash; +	fp->ctf_dtstrlen = sizeof (_CTF_STRTAB_TEMPLATE); +	fp->ctf_dtnextid = 1; +	fp->ctf_dtoldid = 0; + +	return (fp); +} +  static uchar_t *  ctf_copy_smembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t)  { @@ -236,14 +273,23 @@ int  ctf_update(ctf_file_t *fp)  {  	ctf_file_t ofp, *nfp; -	ctf_header_t hdr; +	ctf_header_t hdr, *bhdr;  	ctf_dtdef_t *dtd; -	ctf_sect_t cts; +	ctf_dsdef_t *dsd; +	ctf_dldef_t *dld; +	ctf_sect_t cts, *symp, *strp;  	uchar_t *s, *s0, *t; -	size_t size; +	ctf_lblent_t *label; +	uint16_t *obj, *func; +	size_t size, objsize, funcsize, labelsize, plen;  	void *buf;  	int err; +	ulong_t i; +	const char *plabel; + +	uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; +	uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;  	if (!(fp->ctf_flags & LCTF_RDWR))  		return (ctf_set_errno(fp, ECTF_RDONLY)); @@ -261,8 +307,26 @@ ctf_update(ctf_file_t *fp)  	hdr.cth_magic = CTF_MAGIC;  	hdr.cth_version = CTF_VERSION; -	if (fp->ctf_flags & LCTF_CHILD) -		hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */ +	if (fp->ctf_flags & LCTF_CHILD) { +		if (fp->ctf_parname == NULL) { +			plen = 0; +			hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */ +			plabel = NULL; +		} else { +			plen = strlen(fp->ctf_parname) + 1; +			plabel = ctf_label_topmost(fp->ctf_parent); +		} +	} else { +		plabel = NULL; +		plen = 0; +	} + +	/* +	 * Iterate over the labels that we have. +	 */ +	for (labelsize = 0, dld = ctf_list_next(&fp->ctf_dldefs); +	    dld != NULL; dld = ctf_list_next(dld)) +		labelsize += sizeof (ctf_lblent_t);  	/*  	 * Iterate through the dynamic type definition list and compute the @@ -304,25 +368,121 @@ ctf_update(ctf_file_t *fp)  	}  	/* +	 * An entry for each object must exist in the data section. However, if +	 * the symbol is SHN_UNDEF, then it is skipped. For objects, the storage +	 * is just the size of the 2-byte id. For functions it's always 2 bytes, +	 * plus 2 bytes per argument and the return type. +	 */ +	dsd = ctf_list_next(&fp->ctf_dsdefs); +	for (objsize = 0, funcsize = 0, i = 0; i < fp->ctf_nsyms; i++) { +		int type; + +		if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { +			const Elf32_Sym *symp = (Elf32_Sym *)symbase + i; + +			type = ELF32_ST_TYPE(symp->st_info); +			if (ctf_sym_valid(strbase, type, symp->st_shndx, +			    symp->st_value, symp->st_name) == B_FALSE) +				continue; +		} else { +			const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; + +			type = ELF64_ST_TYPE(symp->st_info); +			if (ctf_sym_valid(strbase, type, symp->st_shndx, +			    symp->st_value, symp->st_name) == B_FALSE) +				continue; +		} + +		while (dsd != NULL && i > dsd->dts_symidx) +			dsd = ctf_list_next(dsd); +		if (type == STT_OBJECT) { +			objsize += sizeof (uint16_t); +		} else { +			/* Every function has a uint16_t info no matter what */ +			if (dsd == NULL || i < dsd->dts_symidx) { +				funcsize += sizeof (uint16_t); +			} else { +				funcsize += sizeof (uint16_t) * +				    (dsd->dts_nargs + 2); +			} +		} +	} + +	/* +	 * The objtoff and funcoffset must be 2-byte aligned. We're guaranteed +	 * that this is always true for the objtoff because labels are always 8 +	 * bytes large. Similarly, because objects are always two bytes of data, +	 * this will always be true for funcoff. +	 */ +	hdr.cth_objtoff = hdr.cth_lbloff + labelsize; +	hdr.cth_funcoff = hdr.cth_objtoff + objsize; + +	/* +	 * The type offset must be 4 byte aligned. +	 */ +	hdr.cth_typeoff = hdr.cth_funcoff + funcsize; +	if (hdr.cth_typeoff & 3) +		hdr.cth_typeoff += 4 - (hdr.cth_typeoff & 3); +	ASSERT((hdr.cth_typeoff & 3) == 0); + +	/*  	 * Fill in the string table offset and size, compute the size of the  	 * entire CTF buffer we need, and then allocate a new buffer and  	 * bcopy the finished header to the start of the buffer.  	 */  	hdr.cth_stroff = hdr.cth_typeoff + size; -	hdr.cth_strlen = fp->ctf_dtstrlen; +	hdr.cth_strlen = fp->ctf_dtstrlen + plen;  	size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen; +	ctf_dprintf("lbloff: %d\nobjtoff: %d\nfuncoff: %d\n" +	    "typeoff: %d\nstroff: %d\nstrlen: %d\n", +	    hdr.cth_lbloff, hdr.cth_objtoff, hdr.cth_funcoff, +	    hdr.cth_typeoff, hdr.cth_stroff, hdr.cth_strlen);  	if ((buf = ctf_data_alloc(size)) == MAP_FAILED)  		return (ctf_set_errno(fp, EAGAIN));  	bcopy(&hdr, buf, sizeof (ctf_header_t)); -	t = (uchar_t *)buf + sizeof (ctf_header_t); +	bhdr = buf; +	label = (ctf_lblent_t *)((uintptr_t)buf + sizeof (ctf_header_t)); +	t = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_typeoff;  	s = s0 = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_stroff; +	obj = (uint16_t *)((uintptr_t)buf + sizeof (ctf_header_t) + +	    hdr.cth_objtoff); +	func = (uint16_t *)((uintptr_t)buf + sizeof (ctf_header_t) + +	    hdr.cth_funcoff);  	bcopy(_CTF_STRTAB_TEMPLATE, s, sizeof (_CTF_STRTAB_TEMPLATE));  	s += sizeof (_CTF_STRTAB_TEMPLATE);  	/* +	 * We have an actual parent name and we're a child container, therefore +	 * we should make sure to note our parent's name here. +	 */ +	if (plen != 0) { +		VERIFY(s + plen - s0 <= hdr.cth_strlen); +		bcopy(fp->ctf_parname, s, plen); +		bhdr->cth_parname = s - s0; +		s += plen; +	} + +	/* +	 * First pass over the labels and copy them out. +	 */ +	for (dld = ctf_list_next(&fp->ctf_dldefs); dld != NULL; +	    dld = ctf_list_next(dld), label++) { +		size_t len = strlen(dld->dld_name) + 1; + +		VERIFY(s + len - s0 <= hdr.cth_strlen); +		bcopy(dld->dld_name, s, len); +		label->ctl_typeidx = dld->dld_type; +		label->ctl_label = s - s0; +		s += len; + +		if (plabel != NULL && strcmp(plabel, dld->dld_name) == 0) +			bhdr->cth_parlabel = label->ctl_label; +	} + +	/*  	 * We now take a final lap through the dynamic type definition list and  	 * copy the appropriate type records and strings to the output buffer.  	 */ @@ -339,6 +499,7 @@ ctf_update(ctf_file_t *fp)  		if (dtd->dtd_name != NULL) {  			dtd->dtd_data.ctt_name = (uint_t)(s - s0);  			len = strlen(dtd->dtd_name) + 1; +			VERIFY(s + len - s0 <= hdr.cth_strlen);  			bcopy(dtd->dtd_name, s, len);  			s += len;  		} else @@ -411,6 +572,61 @@ ctf_update(ctf_file_t *fp)  	}  	/* +	 * Now we fill in our dynamic data and function sections. We use the +	 * same criteria as above, but also consult the dsd list. +	 */ +	dsd = ctf_list_next(&fp->ctf_dsdefs); +	for (i = 0; i < fp->ctf_nsyms; i++) { +		int type; +		if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { +			const Elf32_Sym *symp = (Elf32_Sym *)symbase + i; +			type = ELF32_ST_TYPE(symp->st_info); + +			if (ctf_sym_valid(strbase, type, symp->st_shndx, +			    symp->st_value, symp->st_name) == B_FALSE) +				continue; +		} else { +			const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; +			type = ELF64_ST_TYPE(symp->st_info); +			if (ctf_sym_valid(strbase, type, symp->st_shndx, +			    symp->st_value, symp->st_name) == B_FALSE) +				continue; +		} + +		while (dsd != NULL && i > dsd->dts_symidx) { +			dsd = ctf_list_next(dsd); +		} +		if (type == STT_OBJECT) { +			if (dsd == NULL || i < dsd->dts_symidx) { +				*obj = 0; +			} else { +				*obj = dsd->dts_tid; +			} +			obj++; +			VERIFY((uintptr_t)obj <= (uintptr_t)func); +		} else { +			if (dsd == NULL || i < dsd->dts_symidx) { +				ushort_t data = CTF_TYPE_INFO(CTF_K_UNKNOWN, +				    0, 0); +				*func = data; +				func++; +			} else { +				int j; +				ushort_t data = CTF_TYPE_INFO(CTF_K_FUNCTION, 0, +				    dsd->dts_nargs); + +				*func = data; +				func++; +				*func = dsd->dts_tid; +				func++; +				for (j = 0; j < dsd->dts_nargs; j++) +					func[j] = dsd->dts_argc[j]; +				func += dsd->dts_nargs; +			} +		} +	} + +	/*  	 * Finally, we are ready to ctf_bufopen() the new container.  If this  	 * is successful, we then switch nfp and fp and free the old container.  	 */ @@ -423,7 +639,15 @@ ctf_update(ctf_file_t *fp)  	cts.cts_entsize = 1;  	cts.cts_offset = 0; -	if ((nfp = ctf_bufopen(&cts, NULL, NULL, &err)) == NULL) { +	if (fp->ctf_nsyms == 0) { +		symp = NULL; +		strp = NULL; +	} else { +		symp = &fp->ctf_symtab; +		strp = &fp->ctf_strtab; +	} + +	if ((nfp = ctf_bufopen(&cts, symp, strp, &err)) == NULL) {  		ctf_data_free(buf, size);  		return (ctf_set_errno(fp, err));  	} @@ -433,10 +657,11 @@ ctf_update(ctf_file_t *fp)  	nfp->ctf_refcnt = fp->ctf_refcnt;  	nfp->ctf_flags |= fp->ctf_flags & ~LCTF_DIRTY; -	nfp->ctf_data.cts_data = NULL; /* force ctf_data_free() on close */  	nfp->ctf_dthash = fp->ctf_dthash;  	nfp->ctf_dthashlen = fp->ctf_dthashlen;  	nfp->ctf_dtdefs = fp->ctf_dtdefs; +	nfp->ctf_dsdefs = fp->ctf_dsdefs; +	nfp->ctf_dldefs = fp->ctf_dldefs;  	nfp->ctf_dtstrlen = fp->ctf_dtstrlen;  	nfp->ctf_dtnextid = fp->ctf_dtnextid;  	nfp->ctf_dtoldid = fp->ctf_dtnextid - 1; @@ -445,6 +670,11 @@ ctf_update(ctf_file_t *fp)  	fp->ctf_dthash = NULL;  	fp->ctf_dthashlen = 0;  	bzero(&fp->ctf_dtdefs, sizeof (ctf_list_t)); +	bzero(&fp->ctf_dsdefs, sizeof (ctf_list_t)); +	bzero(&fp->ctf_dldefs, sizeof (ctf_list_t)); + +	bzero(&fp->ctf_symtab, sizeof (ctf_sect_t)); +	bzero(&fp->ctf_strtab, sizeof (ctf_sect_t));  	bcopy(fp, &ofp, sizeof (ctf_file_t));  	bcopy(nfp, fp, sizeof (ctf_file_t)); @@ -563,6 +793,101 @@ ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type)  	return (dtd);  } +ctf_dsdef_t * +ctf_dsd_lookup(ctf_file_t *fp, ulong_t idx) +{ +	ctf_dsdef_t *dsd; + +	for (dsd = ctf_list_next(&fp->ctf_dsdefs); dsd != NULL; +	    dsd = ctf_list_next(dsd)) { +		if (dsd->dts_symidx == idx) +			return (dsd); +	} + +	return (NULL); +} + +/* + * We order the ctf_dsdef_t by symbol index to make things better for updates. + */ +void +ctf_dsd_insert(ctf_file_t *fp, ctf_dsdef_t *dsd) +{ +	ctf_dsdef_t *i; + +	for (i = ctf_list_next(&fp->ctf_dsdefs); i != NULL; +	    i = ctf_list_next(i)) { +		if (i->dts_symidx > dsd->dts_symidx) +			break; +	} + +	if (i == NULL) { +		ctf_list_append(&fp->ctf_dsdefs, dsd); +		return; +	} + +	ctf_list_insert_before(&fp->ctf_dsdefs, i, dsd); +} + +/* ARGSUSED */ +void +ctf_dsd_delete(ctf_file_t *fp, ctf_dsdef_t *dsd) +{ +	if (dsd->dts_argc != NULL) +		ctf_free(dsd->dts_argc, +		    sizeof (ctf_id_t) * dsd->dts_nargs); +	ctf_list_delete(&fp->ctf_dsdefs, dsd); +	ctf_free(dsd, sizeof (ctf_dsdef_t)); +} + +ctf_dldef_t * +ctf_dld_lookup(ctf_file_t *fp, const char *name) +{ +	ctf_dldef_t *dld; + +	for (dld = ctf_list_next(&fp->ctf_dldefs); dld != NULL; +	    dld = ctf_list_next(dld)) { +		if (strcmp(name, dld->dld_name) == 0) +			return (dld); +	} + +	return (NULL); +} + +void +ctf_dld_insert(ctf_file_t *fp, ctf_dldef_t *dld, uint_t pos) +{ +	ctf_dldef_t *l; + +	if (pos == 0) { +		ctf_list_prepend(&fp->ctf_dldefs, dld); +		return; +	} + +	for (l = ctf_list_next(&fp->ctf_dldefs); pos != 0 && dld != NULL; +	    l = ctf_list_next(l), pos--) +		; + +	if (l == NULL) +		ctf_list_append(&fp->ctf_dldefs, dld); +	else +		ctf_list_insert_before(&fp->ctf_dsdefs, l, dld); +} + +void +ctf_dld_delete(ctf_file_t *fp, ctf_dldef_t *dld) +{ +	ctf_list_delete(&fp->ctf_dldefs, dld); + +	if (dld->dld_name != NULL) { +		size_t len = strlen(dld->dld_name) + 1; +		ctf_free(dld->dld_name, len); +		fp->ctf_dtstrlen -= len; +	} + +	ctf_free(dld, sizeof (ctf_dldef_t)); +} +  /*   * Discard all of the dynamic type definitions that have been added to the   * container since the last call to ctf_update().  We locate such types by @@ -583,10 +908,10 @@ ctf_discard(ctf_file_t *fp)  		return (0); /* no update required */  	for (dtd = ctf_list_prev(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { +		ntd = ctf_list_prev(dtd);  		if (dtd->dtd_type <= fp->ctf_dtoldid)  			continue; /* skip types that have been committed */ -		ntd = ctf_list_prev(dtd);  		ctf_dtd_delete(fp, dtd);  	} @@ -656,7 +981,7 @@ clp2(size_t x)  	return (x + 1);  } -static ctf_id_t +ctf_id_t  ctf_add_encoded(ctf_file_t *fp, uint_t flag,      const char *name, const ctf_encoding_t *ep, uint_t kind)  { @@ -676,8 +1001,9 @@ ctf_add_encoded(ctf_file_t *fp, uint_t flag,  	return (type);  } -static ctf_id_t -ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind) +ctf_id_t +ctf_add_reftype(ctf_file_t *fp, uint_t flag, +    const char *name, ctf_id_t ref, uint_t kind)  {  	ctf_dtdef_t *dtd;  	ctf_id_t type; @@ -685,7 +1011,7 @@ ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind)  	if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE)  		return (ctf_set_errno(fp, EINVAL)); -	if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) +	if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)  		return (CTF_ERR); /* errno is set for us */  	ctf_ref_inc(fp, ref); @@ -711,9 +1037,9 @@ ctf_add_float(ctf_file_t *fp, uint_t flag,  }  ctf_id_t -ctf_add_pointer(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +ctf_add_pointer(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)  { -	return (ctf_add_reftype(fp, flag, ref, CTF_K_POINTER)); +	return (ctf_add_reftype(fp, flag, name, ref, CTF_K_POINTER));  }  ctf_id_t @@ -781,7 +1107,7 @@ ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)  }  ctf_id_t -ctf_add_function(ctf_file_t *fp, uint_t flag, +ctf_add_funcptr(ctf_file_t *fp, uint_t flag,      const ctf_funcinfo_t *ctc, const ctf_id_t *argv)  {  	ctf_dtdef_t *dtd; @@ -965,21 +1291,21 @@ ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)  }  ctf_id_t -ctf_add_volatile(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +ctf_add_volatile(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)  { -	return (ctf_add_reftype(fp, flag, ref, CTF_K_VOLATILE)); +	return (ctf_add_reftype(fp, flag, name, ref, CTF_K_VOLATILE));  }  ctf_id_t -ctf_add_const(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +ctf_add_const(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)  { -	return (ctf_add_reftype(fp, flag, ref, CTF_K_CONST)); +	return (ctf_add_reftype(fp, flag, name, ref, CTF_K_CONST));  }  ctf_id_t -ctf_add_restrict(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +ctf_add_restrict(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)  { -	return (ctf_add_reftype(fp, flag, ref, CTF_K_RESTRICT)); +	return (ctf_add_reftype(fp, flag, name, ref, CTF_K_RESTRICT));  }  int @@ -1039,7 +1365,8 @@ ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value)  }  int -ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type) +ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type, +    ulong_t offset)  {  	ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, souid);  	ctf_dmdef_t *dmd; @@ -1064,7 +1391,12 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)  	if (vlen == CTF_MAX_VLEN)  		return (ctf_set_errno(fp, ECTF_DTFULL)); -	if (name != NULL) { +	/* +	 * Structures may have members which are anonymous. If they have two of +	 * these, then the duplicte member detection would find it due to the +	 * string of "", so we skip it. +	 */ +	if (name != NULL && *name != '\0') {  		for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);  		    dmd != NULL; dmd = ctf_list_next(dmd)) {  			if (dmd->dmd_name != NULL && @@ -1092,29 +1424,36 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)  	if (kind == CTF_K_STRUCT && vlen != 0) {  		ctf_dmdef_t *lmd = ctf_list_prev(&dtd->dtd_u.dtu_members);  		ctf_id_t ltype = ctf_type_resolve(fp, lmd->dmd_type); -		size_t off = lmd->dmd_offset; - -		ctf_encoding_t linfo; -		ssize_t lsize; - -		if (ctf_type_encoding(fp, ltype, &linfo) != CTF_ERR) -			off += linfo.cte_bits; -		else if ((lsize = ctf_type_size(fp, ltype)) != CTF_ERR) -			off += lsize * NBBY; - -		/* -		 * Round up the offset of the end of the last member to the -		 * next byte boundary, convert 'off' to bytes, and then round -		 * it up again to the next multiple of the alignment required -		 * by the new member.  Finally, convert back to bits and store -		 * the result in dmd_offset.  Technically we could do more -		 * efficient packing if the new member is a bit-field, but -		 * we're the "compiler" and ANSI says we can do as we choose. -		 */ -		off = roundup(off, NBBY) / NBBY; -		off = roundup(off, MAX(malign, 1)); -		dmd->dmd_offset = off * NBBY; -		ssize = off + msize; +		size_t off; + +		if (offset == ULONG_MAX) { +			ctf_encoding_t linfo; +			ssize_t lsize; + +			off = lmd->dmd_offset; +			if (ctf_type_encoding(fp, ltype, &linfo) != CTF_ERR) +				off += linfo.cte_bits; +			else if ((lsize = ctf_type_size(fp, ltype)) != CTF_ERR) +				off += lsize * NBBY; + +			/* +			 * Round up the offset of the end of the last member to +			 * the next byte boundary, convert 'off' to bytes, and +			 * then round it up again to the next multiple of the +			 * alignment required by the new member.  Finally, +			 * convert back to bits and store the result in +			 * dmd_offset.  Technically we could do more efficient +			 * packing if the new member is a bit-field, but we're +			 * the "compiler" and ANSI says we can do as we choose. +			 */ +			off = roundup(off, NBBY) / NBBY; +			off = roundup(off, MAX(malign, 1)); +			dmd->dmd_offset = off * NBBY; +			ssize = off + msize; +		} else { +			dmd->dmd_offset = offset; +			ssize = offset / NBBY + msize; +		}  	} else {  		dmd->dmd_offset = 0;  		ssize = ctf_get_ctt_size(fp, &dtd->dtd_data, NULL, NULL); @@ -1380,7 +1719,7 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)  		if (src_type == CTF_ERR)  			return (CTF_ERR); /* errno is set for us */ -		dst_type = ctf_add_reftype(dst_fp, flag, src_type, kind); +		dst_type = ctf_add_reftype(dst_fp, flag, NULL, src_type, kind);  		break;  	case CTF_K_ARRAY: @@ -1415,7 +1754,7 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)  		if (ctc.ctc_return == CTF_ERR)  			return (CTF_ERR); /* errno is set for us */ -		dst_type = ctf_add_function(dst_fp, flag, &ctc, NULL); +		dst_type = ctf_add_funcptr(dst_fp, flag, &ctc, NULL);  		break;  	case CTF_K_STRUCT: @@ -1540,3 +1879,166 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)  	return (dst_type);  } + +int +ctf_add_function(ctf_file_t *fp, ulong_t idx, const ctf_funcinfo_t *fip, +    const ctf_id_t *argc) +{ +	int i; +	ctf_dsdef_t *dsd; +	ctf_file_t *afp; +	uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; + +	if (!(fp->ctf_flags & LCTF_RDWR)) +		return (ctf_set_errno(fp, ECTF_RDONLY)); + +	if (ctf_dsd_lookup(fp, idx) != NULL) +		return (ctf_set_errno(fp, ECTF_CONFLICT)); + +	if (symbase == NULL) +		return (ctf_set_errno(fp, ECTF_STRTAB)); + +	if (idx > fp->ctf_nsyms) +		return (ctf_set_errno(fp, ECTF_NOTDATA)); + +	if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { +		const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx; +		if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC) +			return (ctf_set_errno(fp, ECTF_NOTFUNC)); +	} else { +		const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx; +		if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC) +			return (ctf_set_errno(fp, ECTF_NOTFUNC)); +	} + +	afp = fp; +	if (ctf_lookup_by_id(&afp, fip->ctc_return) == NULL) +		return (CTF_ERR); /* errno is set for us */ + +	for (i = 0; i < fip->ctc_argc; i++) { +		afp = fp; +		if (ctf_lookup_by_id(&afp, argc[i]) == NULL) +			return (CTF_ERR); /* errno is set for us */ +	} + +	dsd = ctf_alloc(sizeof (ctf_dsdef_t)); +	if (dsd == NULL) +		return (ctf_set_errno(fp, ENOMEM)); +	dsd->dts_nargs = fip->ctc_argc; +	if (fip->ctc_flags & CTF_FUNC_VARARG) +		dsd->dts_nargs++; +	if (dsd->dts_nargs != 0) { +		dsd->dts_argc = ctf_alloc(sizeof (ctf_id_t) * dsd->dts_nargs); +		if (dsd->dts_argc == NULL) { +			ctf_free(dsd, sizeof (ctf_dsdef_t)); +			return (ctf_set_errno(fp, ENOMEM)); +		} +		bcopy(argc, dsd->dts_argc, sizeof (ctf_id_t) * fip->ctc_argc); +		if (fip->ctc_flags & CTF_FUNC_VARARG) +			dsd->dts_argc[fip->ctc_argc] = 0; +	} +	dsd->dts_symidx = idx; +	dsd->dts_tid = fip->ctc_return; + +	ctf_dsd_insert(fp, dsd); +	fp->ctf_flags |= LCTF_DIRTY; + +	return (0); +} + +int +ctf_add_object(ctf_file_t *fp, ulong_t idx, ctf_id_t type) +{ +	ctf_dsdef_t *dsd; +	ctf_file_t *afp; +	uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; + +	if (!(fp->ctf_flags & LCTF_RDWR)) +		return (ctf_set_errno(fp, ECTF_RDONLY)); + +	if (!(fp->ctf_flags & LCTF_RDWR)) +		return (ctf_set_errno(fp, ECTF_RDONLY)); + +	if (ctf_dsd_lookup(fp, idx) != NULL) +		return (ctf_set_errno(fp, ECTF_CONFLICT)); + +	if (symbase == NULL) +		return (ctf_set_errno(fp, ECTF_STRTAB)); + +	if (idx > fp->ctf_nsyms) +		return (ctf_set_errno(fp, ECTF_NOTDATA)); + +	if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { +		const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx; +		if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT) +			return (ctf_set_errno(fp, ECTF_NOTDATA)); +	} else { +		const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx; +		if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) +			return (ctf_set_errno(fp, ECTF_NOTDATA)); +	} + +	afp = fp; +	if (ctf_lookup_by_id(&afp, type) == NULL) +		return (CTF_ERR); /* errno is set for us */ + +	dsd = ctf_alloc(sizeof (ctf_dsdef_t)); +	if (dsd == NULL) +		return (ctf_set_errno(fp, ENOMEM)); +	dsd->dts_symidx = idx; +	dsd->dts_tid = type; +	dsd->dts_argc = NULL; + +	ctf_dsd_insert(fp, dsd); +	fp->ctf_flags |= LCTF_DIRTY; + +	return (0); +} + +void +ctf_dataptr(ctf_file_t *fp, const void **addrp, size_t *sizep) +{ +	if (addrp != NULL) +		*addrp = fp->ctf_base; +	if (sizep != NULL) +		*sizep = fp->ctf_size; +} + +int +ctf_add_label(ctf_file_t *fp, const char *name, ctf_id_t type, uint_t position) +{ +	ctf_file_t *fpd; +	ctf_dldef_t *dld; + +	if (name == NULL) +		return (ctf_set_errno(fp, EINVAL)); + +	if (!(fp->ctf_flags & LCTF_RDWR)) +		return (ctf_set_errno(fp, ECTF_RDONLY)); + +	fpd = fp; +	if (type != 0 && ctf_lookup_by_id(&fpd, type) == NULL) +		return (CTF_ERR); /* errno is set for us */ + +	if (type != 0 && (fp->ctf_flags & LCTF_CHILD) && +	    CTF_TYPE_ISPARENT(type)) +		return (ctf_set_errno(fp, ECTF_NOPARENT)); + +	if (ctf_dld_lookup(fp, name) != NULL) +		return (ctf_set_errno(fp, ECTF_LABELEXISTS)); + +	if ((dld = ctf_alloc(sizeof (ctf_dldef_t))) == NULL) +		return (ctf_set_errno(fp, EAGAIN)); + +	if ((dld->dld_name = ctf_strdup(name)) == NULL) { +		ctf_free(dld, sizeof (ctf_dldef_t)); +		return (ctf_set_errno(fp, EAGAIN)); +	} + +	dld->dld_type = type; +	fp->ctf_dtstrlen += strlen(name) + 1; +	ctf_dld_insert(fp, dld, position); +	fp->ctf_flags |= LCTF_DIRTY; + +	return (0); +} diff --git a/usr/src/common/ctf/ctf_error.c b/usr/src/common/ctf/ctf_error.c index fe3d0de0cb..e2eb12e9dd 100644 --- a/usr/src/common/ctf/ctf_error.c +++ b/usr/src/common/ctf/ctf_error.c @@ -24,7 +24,7 @@   * Use is subject to license terms.   */  /* - * Copyright (c) 2012, Joyent, Inc. + * Copyright (c) 2015, Joyent, Inc.   */  #include <ctf_impl.h> @@ -75,7 +75,12 @@ static const char *const _ctf_errlist[] = {  	"Duplicate member name definition",		 /* ECTF_DUPMEMBER */  	"Conflicting type is already defined",		 /* ECTF_CONFLICT */  	"Type has outstanding references",		 /* ECTF_REFERENCED */ -	"Type is not a dynamic type"			 /* ECTF_NOTDYN */ +	"Type is not a dynamic type",			 /* ECTF_NOTDYN */ +	"Elf library failure",				 /* ECTF_ELF */ +	"Cannot merge child container",			 /* ECTF_MCHILD */ +	"Label already exists",				 /* ECTF_LABEL */ +	"Merged labels conflict",			 /* ECTF_LCONFLICT */ +	"Zlib library failure"				 /* ECTF_ZLIB */  };  static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]); diff --git a/usr/src/common/ctf/ctf_hash.c b/usr/src/common/ctf/ctf_hash.c index b10a7618f6..0c5a71a5ac 100644 --- a/usr/src/common/ctf/ctf_hash.c +++ b/usr/src/common/ctf/ctf_hash.c @@ -25,9 +25,8 @@   * Use is subject to license terms.   */ -#pragma ident	"%Z%%M%	%I%	%E% SMI" -  #include <ctf_impl.h> +#include <sys/debug.h>  static const ushort_t _CTF_EMPTY[1] = { 0 }; diff --git a/usr/src/common/ctf/ctf_impl.h b/usr/src/common/ctf/ctf_impl.h index f56fa6a005..04b12418ae 100644 --- a/usr/src/common/ctf/ctf_impl.h +++ b/usr/src/common/ctf/ctf_impl.h @@ -25,7 +25,7 @@   * Use is subject to license terms.   */  /* - * Copyright (c) 2012, Joyent, Inc.  All rights reserved. + * Copyright (c) 2015, Joyent, Inc.  All rights reserved.   */  #ifndef	_CTF_IMPL_H @@ -41,6 +41,8 @@  #include <sys/systm.h>  #include <sys/cmn_err.h>  #include <sys/varargs.h> +#include <sys/ddi.h> +#include <sys/sunddi.h>  #define	isspace(c) \  	((c) == ' ' || (c) == '\t' || (c) == '\n' || \ @@ -56,6 +58,7 @@  #include <stdio.h>  #include <limits.h>  #include <ctype.h> +#include <stddef.h>  #endif	/* _KERNEL */ @@ -77,6 +80,10 @@ typedef struct ctf_hash {  	uint_t h_free;		/* index of next free hash element */  } ctf_hash_t; +struct ctf_idhash_iter { +	int cii_id;	/* Current iteration id */ +}; +  typedef struct ctf_strs {  	const char *cts_strs;	/* base address of string table */  	size_t cts_len;		/* size of string table in bytes */ @@ -159,6 +166,20 @@ typedef struct ctf_dtdef {  	} dtd_u;  } ctf_dtdef_t; +typedef struct ctf_dsdef { +	ctf_list_t dts_list;	/* list forward/back pointers */ +	ulong_t dts_symidx;	/* symbol id */ +	ctf_id_t dts_tid;	/* type for obj, 0 if function */ +	uint_t dts_nargs; +	ctf_id_t *dts_argc;	/* function argv */ +} ctf_dsdef_t; + +typedef struct ctf_dldef { +	ctf_list_t dld_list;	/* list forward/back pointers */ +	char *dld_name;		/* name of the label */ +	ctf_id_t dld_type;	/* type ID associated with the label */ +} ctf_dldef_t; +  typedef struct ctf_bundle {  	ctf_file_t *ctb_file;	/* CTF container handle */  	ctf_id_t ctb_type;	/* CTF type identifier */ @@ -211,6 +232,9 @@ struct ctf_file {  	ulong_t ctf_dtnextid;	/* next dynamic type id to assign */  	ulong_t ctf_dtoldid;	/* oldest id that has been committed */  	void *ctf_specific;	/* data for ctf_get/setspecific */ +	ctf_list_t ctf_dsdefs;	/* list of dynamic obj/func definitions */ +	ctf_list_t ctf_dldefs;	/* list of dynamic labels */ +	uint_t ctf_hflags;	/* original flags on the header */  };  #define	LCTF_INDEX_TO_TYPEPTR(fp, i) \ @@ -225,62 +249,15 @@ struct ctf_file {  #define	LCTF_RDWR	0x0004	/* CTF container is writable */  #define	LCTF_DIRTY	0x0008	/* CTF container has been modified */ -#define	ECTF_BASE	1000	/* base value for libctf errnos */ - -enum { -	ECTF_FMT = ECTF_BASE,	/* file is not in CTF or ELF format */ -	ECTF_ELFVERS,		/* ELF version is more recent than libctf */ -	ECTF_CTFVERS,		/* CTF version is more recent than libctf */ -	ECTF_ENDIAN,		/* data is different endian-ness than lib */ -	ECTF_SYMTAB,		/* symbol table uses invalid entry size */ -	ECTF_SYMBAD,		/* symbol table data buffer invalid */ -	ECTF_STRBAD,		/* string table data buffer invalid */ -	ECTF_CORRUPT,		/* file data corruption detected */ -	ECTF_NOCTFDATA,		/* ELF file does not contain CTF data */ -	ECTF_NOCTFBUF,		/* buffer does not contain CTF data */ -	ECTF_NOSYMTAB,		/* symbol table data is not available */ -	ECTF_NOPARENT,		/* parent CTF container is not available */ -	ECTF_DMODEL,		/* data model mismatch */ -	ECTF_MMAP,		/* failed to mmap a data section */ -	ECTF_ZMISSING,		/* decompression library not installed */ -	ECTF_ZINIT,		/* failed to initialize decompression library */ -	ECTF_ZALLOC,		/* failed to allocate decompression buffer */ -	ECTF_DECOMPRESS,	/* failed to decompress CTF data */ -	ECTF_STRTAB,		/* string table for this string is missing */ -	ECTF_BADNAME,		/* string offset is corrupt w.r.t. strtab */ -	ECTF_BADID,		/* invalid type ID number */ -	ECTF_NOTSOU,		/* type is not a struct or union */ -	ECTF_NOTENUM,		/* type is not an enum */ -	ECTF_NOTSUE,		/* type is not a struct, union, or enum */ -	ECTF_NOTINTFP,		/* type is not an integer or float */ -	ECTF_NOTARRAY,		/* type is not an array */ -	ECTF_NOTREF,		/* type does not reference another type */ -	ECTF_NAMELEN,		/* buffer is too small to hold type name */ -	ECTF_NOTYPE,		/* no type found corresponding to name */ -	ECTF_SYNTAX,		/* syntax error in type name */ -	ECTF_NOTFUNC,		/* symtab entry does not refer to a function */ -	ECTF_NOFUNCDAT,		/* no func info available for function */ -	ECTF_NOTDATA,		/* symtab entry does not refer to a data obj */ -	ECTF_NOTYPEDAT,		/* no type info available for object */ -	ECTF_NOLABEL,		/* no label found corresponding to name */ -	ECTF_NOLABELDATA,	/* file does not contain any labels */ -	ECTF_NOTSUP,		/* feature not supported */ -	ECTF_NOENUMNAM,		/* enum element name not found */ -	ECTF_NOMEMBNAM,		/* member name not found */ -	ECTF_RDONLY,		/* CTF container is read-only */ -	ECTF_DTFULL,		/* CTF type is full (no more members allowed) */ -	ECTF_FULL,		/* CTF container is full */ -	ECTF_DUPMEMBER,		/* duplicate member name definition */ -	ECTF_CONFLICT,		/* conflicting type definition present */ -	ECTF_REFERENCED,	/* type has outstanding references */ -	ECTF_NOTDYN		/* type is not a dynamic type */ -}; +#define	CTF_ELF_SCN_NAME	".SUNW_ctf"  extern ssize_t ctf_get_ctt_size(const ctf_file_t *, const ctf_type_t *,      ssize_t *, ssize_t *);  extern const ctf_type_t *ctf_lookup_by_id(ctf_file_t **, ctf_id_t); +extern ctf_file_t *ctf_fdcreate_int(int, int *, ctf_sect_t *); +  extern int ctf_hash_create(ctf_hash_t *, ulong_t);  extern int ctf_hash_insert(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);  extern int ctf_hash_define(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t); @@ -294,12 +271,16 @@ extern void ctf_hash_destroy(ctf_hash_t *);  extern void ctf_list_append(ctf_list_t *, void *);  extern void ctf_list_prepend(ctf_list_t *, void *); +extern void ctf_list_insert_before(ctf_list_t *, void *, void *);  extern void ctf_list_delete(ctf_list_t *, void *);  extern void ctf_dtd_insert(ctf_file_t *, ctf_dtdef_t *);  extern void ctf_dtd_delete(ctf_file_t *, ctf_dtdef_t *);  extern ctf_dtdef_t *ctf_dtd_lookup(ctf_file_t *, ctf_id_t); +extern void ctf_dsd_delete(ctf_file_t *, ctf_dsdef_t *); +extern void ctf_dld_delete(ctf_file_t *, ctf_dldef_t *); +  extern void ctf_decl_init(ctf_decl_t *, char *, size_t);  extern void ctf_decl_fini(ctf_decl_t *);  extern void ctf_decl_push(ctf_decl_t *, ctf_file_t *, ctf_id_t); @@ -327,6 +308,13 @@ extern void ctf_dprintf(const char *, ...);  extern void *ctf_zopen(int *); +extern ctf_id_t ctf_add_encoded(ctf_file_t *, uint_t, const char *, +    const ctf_encoding_t *, uint_t); +extern ctf_id_t ctf_add_reftype(ctf_file_t *, uint_t, const char *, ctf_id_t, +    uint_t); +extern boolean_t ctf_sym_valid(uintptr_t, int, uint16_t, uint64_t, +    uint32_t); +  extern const char _CTF_SECTION[];	/* name of CTF ELF section */  extern const char _CTF_NULLSTR[];	/* empty string */ diff --git a/usr/src/common/ctf/ctf_open.c b/usr/src/common/ctf/ctf_open.c index 001cf5c591..82b396e825 100644 --- a/usr/src/common/ctf/ctf_open.c +++ b/usr/src/common/ctf/ctf_open.c @@ -25,7 +25,7 @@   * Use is subject to license terms.   */  /* - * Copyright (c) 2013, Joyent, Inc.  All rights reserved. + * Copyright (c) 2015, Joyent, Inc.  All rights reserved.   */  #include <ctf_impl.h> @@ -550,6 +550,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,  	void *buf, *base;  	size_t size, hdrsz;  	int err; +	uint_t hflags;  	if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL)))  		return (ctf_set_open_errno(errp, EINVAL)); @@ -631,6 +632,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,  	 * the CTF data buffer if it is compressed.  Otherwise we just put  	 * the data section's buffer pointer into ctf_buf, below.  	 */ +	hflags = hp.cth_flags;  	if (hp.cth_flags & CTF_F_COMPRESS) {  		size_t srclen, dstlen;  		const void *src; @@ -680,6 +682,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,  	bzero(fp, sizeof (ctf_file_t));  	fp->ctf_version = hp.cth_version;  	fp->ctf_fileops = &ctf_fileops[hp.cth_version]; +	fp->ctf_hflags = hflags;  	bcopy(ctfsect, &fp->ctf_data, sizeof (ctf_sect_t));  	if (symsect != NULL) { @@ -883,6 +886,8 @@ void  ctf_close(ctf_file_t *fp)  {  	ctf_dtdef_t *dtd, *ntd; +	ctf_dsdef_t *dsd, *nsd; +	ctf_dldef_t *dld, *nld;  	if (fp == NULL)  		return; /* allow ctf_close(NULL) to simplify caller code */ @@ -906,10 +911,25 @@ ctf_close(ctf_file_t *fp)  		ctf_dtd_delete(fp, dtd);  	} +	for (dsd = ctf_list_prev(&fp->ctf_dsdefs); dsd != NULL; dsd = nsd) { +		nsd = ctf_list_prev(dsd); +		ctf_dsd_delete(fp, dsd); +	} + +	for (dld = ctf_list_prev(&fp->ctf_dldefs); dld != NULL; dld = nld) { +		nld = ctf_list_prev(dld); +		ctf_dld_delete(fp, dld); +	} +  	ctf_free(fp->ctf_dthash, fp->ctf_dthashlen * sizeof (ctf_dtdef_t *));  	if (fp->ctf_flags & LCTF_MMAP) { -		if (fp->ctf_data.cts_data != NULL) +		/* +		 * Writeable containers shouldn't necessairily have the CTF +		 * section freed. +		 */ +		if (fp->ctf_data.cts_data != NULL && +		    !(fp->ctf_flags & LCTF_RDWR))  			ctf_sect_munmap(&fp->ctf_data);  		if (fp->ctf_symtab.cts_data != NULL)  			ctf_sect_munmap(&fp->ctf_symtab); @@ -980,6 +1000,16 @@ ctf_parent_name(ctf_file_t *fp)  }  /* + * Return the label of the parent CTF container, if one exists. Otherwise return + * NULL. + */ +const char * +ctf_parent_label(ctf_file_t *fp) +{ +	return (fp->ctf_parlabel); +} + +/*   * Import the types from the specified parent container by storing a pointer   * to it in ctf_parent and incrementing its reference count.  Only one parent   * is allowed: if a parent already exists, it is replaced by the new parent. @@ -1043,3 +1073,9 @@ ctf_getspecific(ctf_file_t *fp)  {  	return (fp->ctf_specific);  } + +uint_t +ctf_flags(ctf_file_t *fp) +{ +	return (fp->ctf_hflags); +} diff --git a/usr/src/common/ctf/ctf_types.c b/usr/src/common/ctf/ctf_types.c index ab1b9ff14b..b5dbb260a3 100644 --- a/usr/src/common/ctf/ctf_types.c +++ b/usr/src/common/ctf/ctf_types.c @@ -24,8 +24,12 @@   * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.   * Use is subject to license terms.   */ +/* + * Copyright (c) 2015, Joyent, Inc. + */  #include <ctf_impl.h> +#include <sys/debug.h>  ssize_t  ctf_get_ctt_size(const ctf_file_t *fp, const ctf_type_t *tp, ssize_t *sizep, @@ -138,19 +142,21 @@ ctf_enum_iter(ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg)  }  /* - * Iterate over every root (user-visible) type in the given CTF container. - * We pass the type ID of each type to the specified callback function. + * Iterate over every type in the given CTF container. If the user doesn't ask + * for all types, then we only give them the user visible, aka root, types.  We + * pass the type ID of each type to the specified callback function.   */  int -ctf_type_iter(ctf_file_t *fp, ctf_type_f *func, void *arg) +ctf_type_iter(ctf_file_t *fp, boolean_t nonroot, ctf_type_f *func, void *arg)  {  	ctf_id_t id, max = fp->ctf_typemax;  	int rc, child = (fp->ctf_flags & LCTF_CHILD);  	for (id = 1; id <= max; id++) {  		const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR(fp, id); -		if (CTF_INFO_ISROOT(tp->ctt_info) && -		    (rc = func(CTF_INDEX_TO_TYPE(id, child), arg)) != 0) +		if ((nonroot || CTF_INFO_ISROOT(tp->ctt_info)) && +		    (rc = func(CTF_INDEX_TO_TYPE(id, child), +		    CTF_INFO_ISROOT(tp->ctt_info),  arg)) != 0)  			return (rc);  	} @@ -380,7 +386,22 @@ ctf_type_size(ctf_file_t *fp, ctf_id_t type)  			return (-1); /* errno is set for us */  		return (size * ar.ctr_nelems); - +	case CTF_K_STRUCT: +	case CTF_K_UNION: +		/* +		 * If we have a zero size, we may be in the process of adding a +		 * structure or union but having not called ctf_update() to deal +		 * with the circular dependencies in such structures and unions. +		 * To handle that case, if we get a size of zero from the ctt, +		 * we look up the dtdef and use its size instead. +		 */ +		size = ctf_get_ctt_size(fp, tp, NULL, NULL); +		if (size == 0) { +			ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type); +			if (dtd != NULL) +				return (dtd->dtd_data.ctt_size); +		} +		return (size);  	default:  		return (ctf_get_ctt_size(fp, tp, NULL, NULL));  	} @@ -868,3 +889,256 @@ ctf_type_visit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg)  {  	return (ctf_type_rvisit(fp, type, func, arg, "", 0, 0));  } + +int +ctf_func_info_by_id(ctf_file_t *fp, ctf_id_t type, ctf_funcinfo_t *fip) +{ +	ctf_file_t *ofp = fp; +	const ctf_type_t *tp; +	const ushort_t *dp; +	int nargs; +	ssize_t increment; + +	if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) +		return (CTF_ERR); /* errno is set for us */ + +	if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_FUNCTION) +		return (ctf_set_errno(ofp, ECTF_NOTFUNC)); + +	fip->ctc_return = tp->ctt_type; +	nargs = LCTF_INFO_VLEN(fp, tp->ctt_info); +	fip->ctc_argc = nargs; +	fip->ctc_flags = 0; + +	/* dp should now point to the first argument */ +	if (nargs != 0) { +		(void) ctf_get_ctt_size(fp, tp, NULL, &increment); +		dp = (ushort_t *)((uintptr_t)fp->ctf_buf + +		    fp->ctf_txlate[CTF_TYPE_TO_INDEX(type)] + increment); +		if (dp[nargs - 1] == 0) { +			fip->ctc_flags |= CTF_FUNC_VARARG; +			fip->ctc_argc--; +		} +	} + +	return (0); +} + +int +ctf_func_args_by_id(ctf_file_t *fp, ctf_id_t type, uint_t argc, ctf_id_t *argv) +{ +	ctf_file_t *ofp = fp; +	const ctf_type_t *tp; +	const ushort_t *dp; +	int nargs; +	ssize_t increment; + +	if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) +		return (CTF_ERR); /* errno is set for us */ + +	if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_FUNCTION) +		return (ctf_set_errno(ofp, ECTF_NOTFUNC)); + +	nargs = LCTF_INFO_VLEN(fp, tp->ctt_info); +	(void) ctf_get_ctt_size(fp, tp, NULL, &increment); +	dp = (ushort_t *)((uintptr_t)fp->ctf_buf + +	    fp->ctf_txlate[CTF_TYPE_TO_INDEX(type)] + +	    increment); +	if (nargs != 0 && dp[nargs - 1] == 0) +		nargs--; + +	for (nargs = MIN(argc, nargs); nargs != 0; nargs--) +		*argv++ = *dp++; + +	return (0); +} + +int +ctf_object_iter(ctf_file_t *fp, ctf_object_f *func, void *arg) +{ +	int i, ret; +	ctf_id_t id; +	uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; +	uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data; + +	if (fp->ctf_symtab.cts_data == NULL) +		return (ctf_set_errno(fp, ECTF_NOSYMTAB)); + +	for (i = 0; i < fp->ctf_nsyms; i++) { +		char *name; +		if (fp->ctf_sxlate[i] == -1u) +			continue; +		id = *(ushort_t *)((uintptr_t)fp->ctf_buf + +		    fp->ctf_sxlate[i]); + +		/* +		 * Validate whether or not we're looking at a data object as +		 * oposed to a function. +		 */ +		if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { +			const Elf32_Sym *symp = (Elf32_Sym *)symbase + i; +			if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT) +				continue; +			if (fp->ctf_strtab.cts_data != NULL && +			    symp->st_name != 0) +				name = (char *)(strbase + symp->st_name); +			else +				name = NULL; +		} else { +			const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; +			if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) +				continue; +			if (fp->ctf_strtab.cts_data != NULL && +			    symp->st_name != 0) +				name = (char *)(strbase + symp->st_name); +			else +				name = NULL; +		} + +		if ((ret = func(name, id, i, arg)) != 0) +			return (ret); +	} + +	return (0); +} + +int +ctf_function_iter(ctf_file_t *fp, ctf_function_f *func, void *arg) +{ +	int i, ret; +	uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; +	uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data; + +	if (fp->ctf_symtab.cts_data == NULL) +		return (ctf_set_errno(fp, ECTF_NOSYMTAB)); + +	for (i = 0; i < fp->ctf_nsyms; i++) { +		char *name; +		ushort_t info, *dp; +		ctf_funcinfo_t fi; +		if (fp->ctf_sxlate[i] == -1u) +			continue; + +		dp = (ushort_t *)((uintptr_t)fp->ctf_buf + +		    fp->ctf_sxlate[i]); +		info = *dp; +		if (info == 0) +			continue; + +		/* +		 * This may be a function or it may be a data object. We have to +		 * consult the symbol table to be certain. Functions are encoded +		 * with their info, data objects with their actual type. +		 */ +		if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { +			const Elf32_Sym *symp = (Elf32_Sym *)symbase + i; +			if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC) +				continue; +			if (fp->ctf_strtab.cts_data != NULL) +				name = (char *)(strbase + symp->st_name); +			else +				name = NULL; +		} else { +			const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; +			if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC) +				continue; +			if (fp->ctf_strtab.cts_data != NULL) +				name = (char *)(strbase + symp->st_name); +			else +				name = NULL; +		} + +		if (LCTF_INFO_KIND(fp, info) != CTF_K_FUNCTION) +			continue; +		dp++; +		fi.ctc_return = *dp; +		dp++; +		fi.ctc_argc = LCTF_INFO_VLEN(fp, info); +		fi.ctc_flags = 0; + +		if (fi.ctc_argc != 0 && dp[fi.ctc_argc - 1] == 0) { +			fi.ctc_flags |= CTF_FUNC_VARARG; +			fi.ctc_argc--; +		} + +		if ((ret = func(name, i, &fi, arg)) != 0) +			return (ret); + +	} + +	return (0); +} + +char * +ctf_symbol_name(ctf_file_t *fp, ulong_t idx, char *buf, size_t len) +{ +	const char *name; +	uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; +	uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data; + +	if (fp->ctf_symtab.cts_data == NULL) { +		(void) ctf_set_errno(fp, ECTF_NOSYMTAB); +		return (NULL); +	} + +	if (fp->ctf_strtab.cts_data == NULL) { +		(void) ctf_set_errno(fp, ECTF_STRTAB); +		return (NULL); +	} + +	if (idx > fp->ctf_nsyms) { +		(void) ctf_set_errno(fp, ECTF_NOTDATA); +		return (NULL); +	} + +	if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { +		const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx; +		if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT && +		    ELF32_ST_TYPE(symp->st_info) != STT_FUNC) { +			(void) ctf_set_errno(fp, ECTF_NOTDATA); +			return (NULL); +		} +		if (symp->st_name == 0) { +			(void) ctf_set_errno(fp, ENOENT); +			return (NULL); +		} +		name = (const char *)(strbase + symp->st_name); +	} else { +		const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx; +		if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC && +		    ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) { +			(void) ctf_set_errno(fp, ECTF_NOTDATA); +			return (NULL); +		} +		if (symp->st_name == 0) { +			(void) ctf_set_errno(fp, ENOENT); +			return (NULL); +		} +		name = (const char *)(strbase + symp->st_name); +	} + +	(void) strlcpy(buf, name, len); + +	return (buf); +} + +int +ctf_string_iter(ctf_file_t *fp, ctf_string_f *func, void *arg) +{ +	int rc; +	const char *strp = fp->ctf_str[CTF_STRTAB_0].cts_strs; +	size_t strl = fp->ctf_str[CTF_STRTAB_0].cts_len; + +	while (strl > 0) { +		size_t len; + +		if ((rc = func(strp, arg)) != 0) +			return (rc); + +		len = strlen(strp) + 1; +		strl -= len; +		strp += len; +	} + +	return (0); +} diff --git a/usr/src/common/ctf/ctf_util.c b/usr/src/common/ctf/ctf_util.c index 740d403e8c..550195b5e1 100644 --- a/usr/src/common/ctf/ctf_util.c +++ b/usr/src/common/ctf/ctf_util.c @@ -23,10 +23,12 @@   * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.   * Use is subject to license terms.   */ - -#pragma ident	"%Z%%M%	%I%	%E% SMI" +/* + * Copyright (c) 2015, Joyent, Inc. + */  #include <ctf_impl.h> +#include <sys/debug.h>  /*   * Simple doubly-linked list append routine.  This implementation assumes that @@ -71,6 +73,24 @@ ctf_list_prepend(ctf_list_t *lp, void *new)  		lp->l_prev = p;  } +void +ctf_list_insert_before(ctf_list_t *head, void *item, void *nitem) +{ +	ctf_list_t *lp = item; +	ctf_list_t *new = nitem; +	ctf_list_t *prev = lp->l_prev; + +	lp->l_prev = new; +	new->l_next = lp; +	new->l_prev = prev; +	if (prev != NULL) { +		prev->l_next = new; +	} else { +		ASSERT(head->l_next == lp); +		head->l_next = new; +	} +} +  /*   * Delete the specified existing element from the given ctf_list_t.  The   * existing pointer should be pointing at a struct with embedded ctf_list_t. @@ -150,3 +170,22 @@ ctf_set_errno(ctf_file_t *fp, int err)  	fp->ctf_errno = err;  	return (CTF_ERR);  } + +boolean_t +ctf_sym_valid(uintptr_t strbase, int type, uint16_t shndx, uint64_t val, +    uint32_t noff) +{ +	const char *name; + +	if (type != STT_OBJECT && type != STT_FUNC) +		return (B_FALSE); +	if (shndx == SHN_UNDEF || noff == 0) +		return (B_FALSE); +	if (type == STT_OBJECT && shndx == SHN_ABS && val == 0) +		return (B_FALSE); +	name = (char *)(strbase + noff); +	if (strcmp(name, "_START_") == 0 || strcmp(name, "_END_") == 0) +		return (B_FALSE); + +	return (B_TRUE); +} diff --git a/usr/src/common/fs/bootfsops.c b/usr/src/common/fs/bootfsops.c new file mode 100644 index 0000000000..5a693b80e5 --- /dev/null +++ b/usr/src/common/fs/bootfsops.c @@ -0,0 +1,329 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source.  A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2013 Joyent, Inc.  All rights reserved. + */ + +#include <sys/bootconf.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/vnode.h> +#include <sys/fs/ufs_fsdir.h> +#include <sys/fs/ufs_fs.h> +#include <sys/fs/ufs_inode.h> +#include <sys/sysmacros.h> +#include <sys/bootvfs.h> +#include <sys/bootinfo.h> +#include <sys/filep.h> + +#ifdef	_BOOT +#include "../common/util.h" +#else +#include <sys/sunddi.h> +#endif + +#define	MAX_FILES	MAX_BOOT_MODULES +#define	MAX_FDS		256 + +extern void *bkmem_alloc(size_t); +extern void bkmem_free(void *, size_t); + +/* + * TODO: Replace these declarations with inclusion of the ordinary userland + * bootfs headers once they're available. + */ +typedef struct bfile { +	char bf_name[MAXPATHLEN]; +	caddr_t bf_addr; +	size_t bf_size; +	struct bfile *bf_next; +	uint64_t bf_ino; +} bfile_t; + +typedef struct bf_fd { +	bfile_t *fd_file; +	off_t fd_pos; +} bf_fd_t; + +static bfile_t *head; +static uint_t init_done; +static bf_fd_t fds[MAX_FDS]; + +static char cpath[MAXPATHLEN];	/* For canonicalising filenames */ + +static void bbootfs_closeall(int); + +static void +canonicalise(const char *fn, char *out) +{ +	const char *p; +	char *q, *s; +	char *last; +	char *oc; +	int is_slash = 0; +	static char scratch[MAXPATHLEN]; + +	if (fn == NULL) { +		*out = '\0'; +		return; +	} + +	/* +	 * Remove leading slashes and condense all multiple slashes into one. +	 */ +	p = fn; +	while (*p == '/') +		++p; + +	for (q = scratch; *p != '\0'; p++) { +		if (*p == '/' && !is_slash) { +			*q++ = '/'; +			is_slash = 1; +		} else if (*p != '/') { +			*q++ = *p; +			is_slash = 0; +		} +	} +	*q = '\0'; + +	if (strncmp(scratch, "system/boot/", 12) == 0 || +	    strcmp(scratch, "system/boot") == 0) { +		s = scratch + 12; +	} else { +		s = scratch; +	} + +	for (last = strsep(&s, "/"), q = oc = out; last != NULL; +	    last = strsep(&s, "/")) { +		if (strcmp(last, ".") == 0) +			continue; +		if (strcmp(last, "..") == 0) { +			for (oc = q; oc > out && *oc != '/'; oc--) +				; +			q = oc; +			continue; +		} +		if (q > out) +			*q++ = '/'; +		q += snprintf(q, MAXPATHLEN - (q - out), "%s", last); +	} + +	*q = '\0'; +} + +/* ARGSUSED */ +static int +bbootfs_mountroot(char *str) +{ +	return (-1); +} + +static int +bbootfs_unmountroot(void) +{ +	return (-1); +} + +static int +bbootfs_init(void) +{ +	bfile_t *fp; +	char propname[32]; +	uint64_t propval; +	uint_t i; + +	for (i = 0; i < MAX_FILES; i++) { +		(void) snprintf(propname, sizeof (propname), +		    "module-name-%u", i); +		if (do_bsys_getproplen(NULL, propname) < 0) +			break; + +		if ((fp = bkmem_alloc(sizeof (bfile_t))) == NULL) { +			bbootfs_closeall(1); +			return (-1); +		} + +		(void) do_bsys_getprop(NULL, propname, cpath); +		canonicalise(cpath, fp->bf_name); + +		(void) snprintf(propname, sizeof (propname), +		    "module-addr-%u", i); +		if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) { +			bkmem_free(fp, sizeof (bfile_t)); +			continue; +		} +		(void) do_bsys_getprop(NULL, propname, &propval); +		fp->bf_addr = (void *)(uintptr_t)propval; + +		(void) snprintf(propname, sizeof (propname), +		    "module-size-%u", i); +		if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) { +			bkmem_free(fp, sizeof (bfile_t)); +			continue; +		} +		(void) do_bsys_getprop(NULL, propname, &propval); +		fp->bf_size = (size_t)propval; +		fp->bf_ino = i; + +		fp->bf_next = head; +		head = fp; +	} + +	return (0); +} + +/*ARGSUSED*/ +static int +bbootfs_open(char *fn, int flags) +{ +	uint_t i; +	bfile_t *fp; + +	if (!init_done) { +		if (bbootfs_init() != 0) +			return (-1); + +		init_done = 1; +	} + +	canonicalise(fn, cpath); + +	for (fp = head; fp != NULL; fp = fp->bf_next) { +		if (strcmp(fp->bf_name, cpath) == 0) +			break; +	} + +	if (fp == NULL) +		return (-1); + +	for (i = 0; i < MAX_FDS; i++) { +		if (fds[i].fd_file == NULL) { +			fds[i].fd_file = fp; +			fds[i].fd_pos = 0; +			return (i); +		} +	} + +	return (-1); +} + +static int +bbootfs_close(int fd) +{ +	if (fds[fd].fd_file == NULL) +		return (-1); + +	fds[fd].fd_file = NULL; +	fds[fd].fd_pos = 0; + +	return (0); +} + +static ssize_t +bbootfs_read(int fd, caddr_t buf, size_t size) +{ +	ssize_t len; +	bf_fd_t *fdp = &fds[fd]; + +	if (fdp->fd_file == NULL) +		return (-1); + +	if (fdp->fd_pos >= fdp->fd_file->bf_size) +		return (-1); + +	if (fdp->fd_pos + size > fdp->fd_file->bf_size) +		len = fdp->fd_file->bf_size - fdp->fd_pos; +	else +		len = size; + +	bcopy(fdp->fd_file->bf_addr + fdp->fd_pos, buf, len); + +	fdp->fd_pos += len; + +	return (len); +} + +static off_t +bbootfs_lseek(int fd, off_t addr, int whence) +{ +	bf_fd_t *fdp = &fds[fd]; + +	if (fdp->fd_file == NULL) +		return (-1); + +	switch (whence) { +	case SEEK_CUR: +		fdp->fd_pos += addr; +		break; +	case SEEK_SET: +		fdp->fd_pos = addr; +		break; +	case SEEK_END: +		fdp->fd_pos = fdp->fd_file->bf_size; +		break; +	default: +		return (-1); +	} + +	return (0); +} + +static int +bbootfs_fstat(int fd, struct bootstat *bsp) +{ +	bf_fd_t *fdp = &fds[fd]; + +	if (fdp->fd_file == NULL) +		return (-1); + +	bsp->st_dev = 1; +	bsp->st_ino = fdp->fd_file->bf_ino; +	bsp->st_mode = 0444; +	bsp->st_nlink = 1; +	bsp->st_uid = bsp->st_gid = 0; +	bsp->st_rdev = 0; +	bsp->st_size = fdp->fd_file->bf_size; +	bsp->st_blksize = 1; +	bsp->st_blocks = fdp->fd_file->bf_size; +	(void) strcpy(bsp->st_fstype, "bootfs"); + +	return (0); +} + +/* ARGSUSED */ +static void +bbootfs_closeall(int flag) +{ +	bfile_t *fp; + +	while (head != NULL) { +		fp = head; +		head = head->bf_next; + +		bkmem_free(fp, sizeof (bfile_t)); +	} + +	init_done = 0; +} + +struct boot_fs_ops bbootfs_ops = { +	"bootfs", +	bbootfs_mountroot, +	bbootfs_unmountroot, +	bbootfs_open, +	bbootfs_close, +	bbootfs_read, +	bbootfs_lseek, +	bbootfs_fstat, +	bbootfs_closeall, +	NULL +}; diff --git a/usr/src/common/idspace/id_space.c b/usr/src/common/idspace/id_space.c new file mode 100644 index 0000000000..7d28a8f533 --- /dev/null +++ b/usr/src/common/idspace/id_space.c @@ -0,0 +1,184 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <sys/types.h> +#include <sys/id_space.h> +#include <sys/debug.h> + +/* + * ID Spaces + * + *   The id_space_t provides a simple implementation of a managed range of + *   integer identifiers using a vmem arena.  An ID space guarantees that the + *   next identifer returned by an allocation is larger than the previous one, + *   unless there are no larger slots remaining in the range.  In this case, + *   the ID space will return the first available slot in the lower part of the + *   range (viewing the previous identifier as a partitioning element).  If no + *   slots are available, id_alloc()/id_allocff() will sleep until an + *   identifier becomes available.  Accordingly, id_space allocations must be + *   initiated from contexts where sleeping is acceptable.  id_alloc_nosleep()/ + *   id_allocff_nosleep() will return -1 if no slots are available or if the + *   system is low on memory.  If id_alloc_nosleep() fails, callers should + *   not try to extend the ID space.  This is to avoid making a possible + *   low-memory situation worse. + * + *   As an ID space is designed for representing a range of id_t's, there + *   is a preexisting maximal range: [0, MAXUID].  ID space requests outside + *   that range will fail on a DEBUG kernel.  The id_allocff*() functions + *   return the first available id, and should be used when there is benefit + *   to having a compact allocated range. + * + *   (Presently, the id_space_t abstraction supports only direct allocations; ID + *   reservation, in which an ID is allocated but placed in a internal + *   dictionary for later use, should be added when a consuming subsystem + *   arrives.) + * + *   This code is also shared with userland. In userland, we don't have the same + *   ability to have sleeping variants, so we effectively turn the normal + *   versions without _nosleep into _nosleep. + */ + +#define	ID_TO_ADDR(id) ((void *)(uintptr_t)(id + 1)) +#define	ADDR_TO_ID(addr) ((id_t)((uintptr_t)addr - 1)) + +/* + * Create an arena to represent the range [low, high). + * Caller must be in a context in which VM_SLEEP is legal, + * for the kernel. Always VM_NOSLEEP in userland. + */ +id_space_t * +id_space_create(const char *name, id_t low, id_t high) +{ +#ifdef _KERNEL +	int flag = VM_SLEEP; +#else +	int flag = VM_NOSLEEP; +#endif +	ASSERT(low >= 0); +	ASSERT(low < high); + +	return (vmem_create(name, ID_TO_ADDR(low), high - low, 1, +	    NULL, NULL, NULL, 0, flag | VMC_IDENTIFIER)); +} + +/* + * Destroy a previously created ID space. + * No restrictions on caller's context. + */ +void +id_space_destroy(id_space_t *isp) +{ +	vmem_destroy(isp); +} + +void +id_space_extend(id_space_t *isp, id_t low, id_t high) +{ +#ifdef _KERNEL +	int flag = VM_SLEEP; +#else +	int flag = VM_NOSLEEP; +#endif +	(void) vmem_add(isp, ID_TO_ADDR(low), high - low, flag); +} + +/* + * Allocate an id_t from specified ID space. + * Caller must be in a context in which VM_SLEEP is legal. + */ +id_t +id_alloc(id_space_t *isp) +{ +#ifdef _KERNEL +	int flag = VM_SLEEP; +#else +	int flag = VM_NOSLEEP; +#endif +	return (ADDR_TO_ID(vmem_alloc(isp, 1, flag | VM_NEXTFIT))); +} + +/* + * Allocate an id_t from specified ID space. + * Returns -1 on failure (see module block comments for more information on + * failure modes). + */ +id_t +id_alloc_nosleep(id_space_t *isp) +{ +	return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_NOSLEEP | VM_NEXTFIT))); +} + +/* + * Allocate an id_t from specified ID space using FIRSTFIT. + * Caller must be in a context in which VM_SLEEP is legal. + */ +id_t +id_allocff(id_space_t *isp) +{ +#ifdef _KERNEL +	int flag = VM_SLEEP; +#else +	int flag = VM_NOSLEEP; +#endif +	return (ADDR_TO_ID(vmem_alloc(isp, 1, flag | VM_FIRSTFIT))); +} + +/* + * Allocate an id_t from specified ID space using FIRSTFIT + * Returns -1 on failure (see module block comments for more information on + * failure modes). + */ +id_t +id_allocff_nosleep(id_space_t *isp) +{ +	return (ADDR_TO_ID(vmem_alloc(isp, 1, VM_NOSLEEP | VM_FIRSTFIT))); +} + +/* + * Allocate a specific identifier if possible, returning the id if + * successful, or -1 on failure. + */ +id_t +id_alloc_specific_nosleep(id_space_t *isp, id_t id) +{ +	void *minaddr = ID_TO_ADDR(id); +	void *maxaddr = ID_TO_ADDR(id + 1); + +	/* +	 * Note that even though we're vmem_free()ing this later, it +	 * should be OK, since there's no quantum cache. +	 */ +	return (ADDR_TO_ID(vmem_xalloc(isp, 1, 1, 0, 0, +	    minaddr, maxaddr, VM_NOSLEEP))); +} + +/* + * Free a previously allocated ID. + * No restrictions on caller's context. + */ +void +id_free(id_space_t *isp, id_t id) +{ +	vmem_free(isp, ID_TO_ADDR(id), 1); +} diff --git a/usr/src/common/inet/inet_hash.c b/usr/src/common/inet/inet_hash.c new file mode 100644 index 0000000000..3a511fe588 --- /dev/null +++ b/usr/src/common/inet/inet_hash.c @@ -0,0 +1,359 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015, Joyent, Inc. + */ + +/* + * Common routines usable by any part of the networking stack for hashing + * packets. The hashing logic originally was part of MAC, but it has more + * utility being usable by the rest of the broader system. + */ + +#include <sys/types.h> +#include <sys/mac.h> +#include <sys/strsubr.h> +#include <sys/strsun.h> +#include <sys/vlan.h> +#include <inet/ip.h> +#include <inet/ip_impl.h> +#include <inet/ip6.h> +#include <sys/dlpi.h> +#include <sys/sunndi.h> +#include <inet/ipsec_impl.h> +#include <inet/sadb.h> +#include <inet/ipsecesp.h> +#include <inet/ipsecah.h> +#include <inet/inet_hash.h> + +/* + * Determines the IPv6 header length accounting for all the optional IPv6 + * headers (hop-by-hop, destination, routing and fragment). The header length + * and next header value (a transport header) is captured. + * + * Returns B_FALSE if all the IP headers are not in the same mblk otherwise + * returns B_TRUE. + */ +static boolean_t +inet_pkthash_ip_hdr_length_v6(ip6_t *ip6h, uint8_t *endptr, +    uint16_t *hdr_length,  uint8_t *next_hdr, ip6_frag_t **fragp) +{ +	uint16_t length; +	uint_t	ehdrlen; +	uint8_t *whereptr; +	uint8_t *nexthdrp; +	ip6_dest_t *desthdr; +	ip6_rthdr_t *rthdr; +	ip6_frag_t *fraghdr; + +	if (((uchar_t *)ip6h + IPV6_HDR_LEN) > endptr) +		return (B_FALSE); +	ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION); +	length = IPV6_HDR_LEN; +	whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */ + +	if (fragp != NULL) +		*fragp = NULL; + +	nexthdrp = &ip6h->ip6_nxt; +	while (whereptr < endptr) { +		/* Is there enough left for len + nexthdr? */ +		if (whereptr + MIN_EHDR_LEN > endptr) +			break; + +		switch (*nexthdrp) { +		case IPPROTO_HOPOPTS: +		case IPPROTO_DSTOPTS: +			/* Assumes the headers are identical for hbh and dst */ +			desthdr = (ip6_dest_t *)whereptr; +			ehdrlen = 8 * (desthdr->ip6d_len + 1); +			if ((uchar_t *)desthdr +  ehdrlen > endptr) +				return (B_FALSE); +			nexthdrp = &desthdr->ip6d_nxt; +			break; +		case IPPROTO_ROUTING: +			rthdr = (ip6_rthdr_t *)whereptr; +			ehdrlen =  8 * (rthdr->ip6r_len + 1); +			if ((uchar_t *)rthdr +  ehdrlen > endptr) +				return (B_FALSE); +			nexthdrp = &rthdr->ip6r_nxt; +			break; +		case IPPROTO_FRAGMENT: +			fraghdr = (ip6_frag_t *)whereptr; +			ehdrlen = sizeof (ip6_frag_t); +			if ((uchar_t *)&fraghdr[1] > endptr) +				return (B_FALSE); +			nexthdrp = &fraghdr->ip6f_nxt; +			if (fragp != NULL) +				*fragp = fraghdr; +			break; +		case IPPROTO_NONE: +			/* No next header means we're finished */ +		default: +			*hdr_length = length; +			*next_hdr = *nexthdrp; +			return (B_TRUE); +		} +		length += ehdrlen; +		whereptr += ehdrlen; +		*hdr_length = length; +		*next_hdr = *nexthdrp; +	} +	switch (*nexthdrp) { +	case IPPROTO_HOPOPTS: +	case IPPROTO_DSTOPTS: +	case IPPROTO_ROUTING: +	case IPPROTO_FRAGMENT: +		/* +		 * If any known extension headers are still to be processed, +		 * the packet's malformed (or at least all the IP header(s) are +		 * not in the same mblk - and that should never happen. +		 */ +		return (B_FALSE); + +	default: +		/* +		 * If we get here, we know that all of the IP headers were in +		 * the same mblk, even if the ULP header is in the next mblk. +		 */ +		*hdr_length = length; +		*next_hdr = *nexthdrp; +		return (B_TRUE); +	} +} + +#define	PKT_HASH_2BYTES(x) ((x)[0] ^ (x)[1]) +#define	PKT_HASH_4BYTES(x) ((x)[0] ^ (x)[1] ^ (x)[2] ^ (x)[3]) +#define	PKT_HASH_MAC(x) ((x)[0] ^ (x)[1] ^ (x)[2] ^ (x)[3] ^ (x)[4] ^ (x)[5]) +uint64_t +inet_pkt_hash(uint_t media, mblk_t *mp, uint8_t policy) +{ +	struct ether_header *ehp; +	uint64_t hash = 0; +	uint16_t sap; +	uint_t skip_len; +	uint8_t proto; +	boolean_t ip_fragmented; + +	/* +	 * We may want to have one of these per MAC type plugin in the +	 * future. For now supports only ethernet. +	 */ +	if (media != DL_ETHER) +		return (0L); + +	/* for now we support only outbound packets */ +	ASSERT(IS_P2ALIGNED(mp->b_rptr, sizeof (uint16_t))); +	ASSERT(MBLKL(mp) >= sizeof (struct ether_header)); + +	/* compute L2 hash */ + +	ehp = (struct ether_header *)mp->b_rptr; + +	if ((policy & INET_PKT_HASH_L2) != 0) { +		uchar_t *mac_src = ehp->ether_shost.ether_addr_octet; +		uchar_t *mac_dst = ehp->ether_dhost.ether_addr_octet; +		hash = PKT_HASH_MAC(mac_src) ^ PKT_HASH_MAC(mac_dst); +		policy &= ~INET_PKT_HASH_L2; +	} + +	if (policy == 0) +		goto done; + +	/* skip ethernet header */ + +	sap = ntohs(ehp->ether_type); +	if (sap == ETHERTYPE_VLAN) { +		struct ether_vlan_header *evhp; +		mblk_t *newmp = NULL; + +		skip_len = sizeof (struct ether_vlan_header); +		if (MBLKL(mp) < skip_len) { +			/* the vlan tag is the payload, pull up first */ +			newmp = msgpullup(mp, -1); +			if ((newmp == NULL) || (MBLKL(newmp) < skip_len)) { +				goto done; +			} +			evhp = (struct ether_vlan_header *)newmp->b_rptr; +		} else { +			evhp = (struct ether_vlan_header *)mp->b_rptr; +		} + +		sap = ntohs(evhp->ether_type); +		freemsg(newmp); +	} else { +		skip_len = sizeof (struct ether_header); +	} + +	/* if ethernet header is in its own mblk, skip it */ +	if (MBLKL(mp) <= skip_len) { +		skip_len -= MBLKL(mp); +		mp = mp->b_cont; +		if (mp == NULL) +			goto done; +	} + +	sap = (sap < ETHERTYPE_802_MIN) ? 0 : sap; + +	/* compute IP src/dst addresses hash and skip IPv{4,6} header */ + +	switch (sap) { +	case ETHERTYPE_IP: { +		ipha_t *iphp; + +		/* +		 * If the header is not aligned or the header doesn't fit +		 * in the mblk, bail now. Note that this may cause packet +		 * reordering. +		 */ +		iphp = (ipha_t *)(mp->b_rptr + skip_len); +		if (((unsigned char *)iphp + sizeof (ipha_t) > mp->b_wptr) || +		    !OK_32PTR((char *)iphp)) +			goto done; + +		proto = iphp->ipha_protocol; +		skip_len += IPH_HDR_LENGTH(iphp); + +		/* Check if the packet is fragmented. */ +		ip_fragmented = ntohs(iphp->ipha_fragment_offset_and_flags) & +		    IPH_OFFSET; + +		/* +		 * For fragmented packets, use addresses in addition to +		 * the frag_id to generate the hash inorder to get +		 * better distribution. +		 */ +		if (ip_fragmented || (policy & INET_PKT_HASH_L3) != 0) { +			uint8_t *ip_src = (uint8_t *)&(iphp->ipha_src); +			uint8_t *ip_dst = (uint8_t *)&(iphp->ipha_dst); + +			hash ^= (PKT_HASH_4BYTES(ip_src) ^ +			    PKT_HASH_4BYTES(ip_dst)); +			policy &= ~INET_PKT_HASH_L3; +		} + +		if (ip_fragmented) { +			uint8_t *identp = (uint8_t *)&iphp->ipha_ident; +			hash ^= PKT_HASH_2BYTES(identp); +			goto done; +		} +		break; +	} +	case ETHERTYPE_IPV6: { +		ip6_t *ip6hp; +		ip6_frag_t *frag = NULL; +		uint16_t hdr_length; + +		/* +		 * If the header is not aligned or the header doesn't fit +		 * in the mblk, bail now. Note that this may cause packets +		 * reordering. +		 */ + +		ip6hp = (ip6_t *)(mp->b_rptr + skip_len); +		if (((unsigned char *)ip6hp + IPV6_HDR_LEN > mp->b_wptr) || +		    !OK_32PTR((char *)ip6hp)) +			goto done; + +		if (!inet_pkthash_ip_hdr_length_v6(ip6hp, mp->b_wptr, +		    &hdr_length, &proto, &frag)) +			goto done; +		skip_len += hdr_length; + +		/* +		 * For fragmented packets, use addresses in addition to +		 * the frag_id to generate the hash inorder to get +		 * better distribution. +		 */ +		if (frag != NULL || (policy & INET_PKT_HASH_L3) != 0) { +			uint8_t *ip_src = &(ip6hp->ip6_src.s6_addr8[12]); +			uint8_t *ip_dst = &(ip6hp->ip6_dst.s6_addr8[12]); + +			hash ^= (PKT_HASH_4BYTES(ip_src) ^ +			    PKT_HASH_4BYTES(ip_dst)); +			policy &= ~INET_PKT_HASH_L3; +		} + +		if (frag != NULL) { +			uint8_t *identp = (uint8_t *)&frag->ip6f_ident; +			hash ^= PKT_HASH_4BYTES(identp); +			goto done; +		} +		break; +	} +	default: +		goto done; +	} + +	if (policy == 0) +		goto done; + +	/* if ip header is in its own mblk, skip it */ +	if (MBLKL(mp) <= skip_len) { +		skip_len -= MBLKL(mp); +		mp = mp->b_cont; +		if (mp == NULL) +			goto done; +	} + +	/* parse ULP header */ +again: +	switch (proto) { +	case IPPROTO_TCP: +	case IPPROTO_UDP: +	case IPPROTO_ESP: +	case IPPROTO_SCTP: +		/* +		 * These Internet Protocols are intentionally designed +		 * for hashing from the git-go.  Port numbers are in the first +		 * word for transports, SPI is first for ESP. +		 */ +		if (mp->b_rptr + skip_len + 4 > mp->b_wptr) +			goto done; +		hash ^= PKT_HASH_4BYTES((mp->b_rptr + skip_len)); +		break; + +	case IPPROTO_AH: { +		ah_t *ah = (ah_t *)(mp->b_rptr + skip_len); +		uint_t ah_length = AH_TOTAL_LEN(ah); + +		if ((unsigned char *)ah + sizeof (ah_t) > mp->b_wptr) +			goto done; + +		proto = ah->ah_nexthdr; +		skip_len += ah_length; + +		/* if AH header is in its own mblk, skip it */ +		if (MBLKL(mp) <= skip_len) { +			skip_len -= MBLKL(mp); +			mp = mp->b_cont; +			if (mp == NULL) +				goto done; +		} + +		goto again; +	} +	} + +done: +	return (hash); +} diff --git a/usr/src/common/util/string.c b/usr/src/common/util/string.c index d54b58d59c..80a076c436 100644 --- a/usr/src/common/util/string.c +++ b/usr/src/common/util/string.c @@ -21,6 +21,7 @@  /*   * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.   * Use is subject to license terms. + * Copyright 2014 Joyent, Inc.  All rights reserved.   */  /* @@ -264,7 +265,7 @@ next_fmt:  		if (sign && pad == '0')  			ADDCHAR('-'); -		while (width-- > sign) +		while ((!left_align) && (width-- > sign))  			ADDCHAR(pad);  		if (sign && pad == ' ')  			ADDCHAR('-'); @@ -280,6 +281,10 @@ next_fmt:  			ADDCHAR(*sp);  		} +		/* add left-alignment padding */ +		while (width-- > sign) +			ADDCHAR(' '); +  		if (c == 'b' && ul != 0) {  			int any = 0;  			c = *bs++; diff --git a/usr/src/common/util/string.h b/usr/src/common/util/string.h index 052eeab4a4..f7acd734bd 100644 --- a/usr/src/common/util/string.h +++ b/usr/src/common/util/string.h @@ -61,6 +61,7 @@ extern char *strncpy(char *, const char *, size_t);  extern char *strrchr(const char *, int c);  extern char *strstr(const char *, const char *);  extern char *strpbrk(const char *, const char *); +extern char *strsep(char **, const char *);  extern char *strncat(char *, const char *, size_t);  extern size_t strlcat(char *, const char *, size_t);  extern size_t strlcpy(char *, const char *, size_t); diff --git a/usr/src/common/util/strtolctype.h b/usr/src/common/util/strtolctype.h index 5675e42be7..535c014d1f 100644 --- a/usr/src/common/util/strtolctype.h +++ b/usr/src/common/util/strtolctype.h @@ -44,7 +44,7 @@ extern "C" {   * safe in probe context.   */ -#if	defined(_KERNEL) && !defined(_BOOT) +#if	defined(_KERNEL) || defined(_BOOT)  #define	isalnum(ch)	(isalpha(ch) || isdigit(ch))  #define	isalpha(ch)	(isupper(ch) || islower(ch)) @@ -56,7 +56,7 @@ extern "C" {  #define	isxdigit(ch)	(isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \  			((ch) >= 'A' && (ch) <= 'F')) -#endif	/* _KERNEL && !_BOOT */ +#endif	/* _KERNEL || _BOOT */  #define	DIGIT(x)	\  	(isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A') diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c index e145b1c866..54aaa027dd 100644 --- a/usr/src/common/zfs/zfs_prop.c +++ b/usr/src/common/zfs/zfs_prop.c @@ -444,6 +444,23 @@ zfs_prop_delegatable(zfs_prop_t prop)  	return (pd->pd_attr != PROP_READONLY);  } +boolean_t +zfs_prop_cacheable(zfs_prop_t prop) +{ +	/* +	 * It'd be nice if each prop had a flags field which could have flag +	 * like PROP_CACHEABLE, but since zprop_attr_t is an enum and this +	 * setting is orthogonal to the concepts of PROP_READONLY, etc., we have +	 * this function. +	 */ +	return (prop == ZFS_PROP_VERSION || +	    prop == ZFS_PROP_NORMALIZE || +	    prop == ZFS_PROP_UTF8ONLY || +	    prop == ZFS_PROP_CASE || +	    prop == ZFS_PROP_VOLSIZE || +	    prop == ZFS_PROP_VOLBLOCKSIZE); +} +  /*   * Given a zfs dataset property name, returns the corresponding property ID.   */ diff --git a/usr/src/common/zfs/zfs_prop.h b/usr/src/common/zfs/zfs_prop.h index a63262311b..1796642c68 100644 --- a/usr/src/common/zfs/zfs_prop.h +++ b/usr/src/common/zfs/zfs_prop.h @@ -21,6 +21,7 @@  /*   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.   * Use is subject to license terms. + * Copyright (c) 2012, Joyent, Inc. All rights reserved.   */  #ifndef	_ZFS_PROP_H @@ -86,6 +87,7 @@ typedef struct {  void zfs_prop_init(void);  zprop_type_t zfs_prop_get_type(zfs_prop_t);  boolean_t zfs_prop_delegatable(zfs_prop_t prop); +boolean_t zfs_prop_cacheable(zfs_prop_t prop);  zprop_desc_t *zfs_prop_get_table(void);  /* | 
