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); /* |