17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 22*e4586ebfSmws 237c478bd9Sstevel@tonic-gate /* 24*e4586ebfSmws * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 257c478bd9Sstevel@tonic-gate * Use is subject to license terms. 267c478bd9Sstevel@tonic-gate */ 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 317c478bd9Sstevel@tonic-gate #include <ctf_impl.h> 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate /* 347c478bd9Sstevel@tonic-gate * Compare the given input string and length against a table of known C storage 35*e4586ebfSmws * qualifier keywords. We just ignore these in ctf_lookup_by_name, below. To 36*e4586ebfSmws * do this quickly, we use a pre-computed Perfect Hash Function similar to the 37*e4586ebfSmws * technique originally described in the classic paper: 38*e4586ebfSmws * 39*e4586ebfSmws * R.J. Cichelli, "Minimal Perfect Hash Functions Made Simple", 40*e4586ebfSmws * Communications of the ACM, Volume 23, Issue 1, January 1980, pp. 17-19. 41*e4586ebfSmws * 42*e4586ebfSmws * For an input string S of length N, we use hash H = S[N - 1] + N - 105, which 43*e4586ebfSmws * for the current set of qualifiers yields a unique H in the range [0 .. 20]. 44*e4586ebfSmws * The hash can be modified when the keyword set changes as necessary. We also 45*e4586ebfSmws * store the length of each keyword and check it prior to the final strcmp(). 467c478bd9Sstevel@tonic-gate */ 477c478bd9Sstevel@tonic-gate static int 487c478bd9Sstevel@tonic-gate isqualifier(const char *s, size_t len) 497c478bd9Sstevel@tonic-gate { 507c478bd9Sstevel@tonic-gate static const struct qual { 517c478bd9Sstevel@tonic-gate const char *q_name; 527c478bd9Sstevel@tonic-gate size_t q_len; 53*e4586ebfSmws } qhash[] = { 54*e4586ebfSmws { "static", 6 }, { "", 0 }, { "", 0 }, { "", 0 }, 55*e4586ebfSmws { "volatile", 8 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, 56*e4586ebfSmws { "", 0 }, { "auto", 4 }, { "extern", 6 }, { "", 0 }, { "", 0 }, 57*e4586ebfSmws { "", 0 }, { "", 0 }, { "const", 5 }, { "register", 8 }, 58*e4586ebfSmws { "", 0 }, { "restrict", 8 }, { "_Restrict", 9 } 597c478bd9Sstevel@tonic-gate }; 607c478bd9Sstevel@tonic-gate 61*e4586ebfSmws int h = s[len - 1] + (int)len - 105; 62*e4586ebfSmws const struct qual *qp = &qhash[h]; 637c478bd9Sstevel@tonic-gate 64*e4586ebfSmws return (h >= 0 && h < sizeof (qhash) / sizeof (qhash[0]) && 65*e4586ebfSmws len == qp->q_len && strncmp(qp->q_name, s, qp->q_len) == 0); 667c478bd9Sstevel@tonic-gate } 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate /* 697c478bd9Sstevel@tonic-gate * Attempt to convert the given C type name into the corresponding CTF type ID. 707c478bd9Sstevel@tonic-gate * It is not possible to do complete and proper conversion of type names 717c478bd9Sstevel@tonic-gate * without implementing a more full-fledged parser, which is necessary to 727c478bd9Sstevel@tonic-gate * handle things like types that are function pointers to functions that 737c478bd9Sstevel@tonic-gate * have arguments that are function pointers, and fun stuff like that. 747c478bd9Sstevel@tonic-gate * Instead, this function implements a very simple conversion algorithm that 757c478bd9Sstevel@tonic-gate * finds the things that we actually care about: structs, unions, enums, 767c478bd9Sstevel@tonic-gate * integers, floats, typedefs, and pointers to any of these named types. 777c478bd9Sstevel@tonic-gate */ 787c478bd9Sstevel@tonic-gate ctf_id_t 797c478bd9Sstevel@tonic-gate ctf_lookup_by_name(ctf_file_t *fp, const char *name) 807c478bd9Sstevel@tonic-gate { 817c478bd9Sstevel@tonic-gate static const char delimiters[] = " \t\n\r\v\f*"; 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate const ctf_lookup_t *lp; 847c478bd9Sstevel@tonic-gate const ctf_helem_t *hp; 857c478bd9Sstevel@tonic-gate const char *p, *q, *end; 867c478bd9Sstevel@tonic-gate ctf_id_t type = 0; 877c478bd9Sstevel@tonic-gate ctf_id_t ntype, ptype; 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate if (name == NULL) 907c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, EINVAL)); 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate for (p = name, end = name + strlen(name); *p != '\0'; p = q) { 937c478bd9Sstevel@tonic-gate while (isspace(*p)) 947c478bd9Sstevel@tonic-gate p++; /* skip leading ws */ 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate if (p == end) 977c478bd9Sstevel@tonic-gate break; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate if ((q = strpbrk(p + 1, delimiters)) == NULL) 1007c478bd9Sstevel@tonic-gate q = end; /* compare until end */ 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate if (*p == '*') { 1037c478bd9Sstevel@tonic-gate /* 1047c478bd9Sstevel@tonic-gate * Find a pointer to type by looking in fp->ctf_ptrtab. 1057c478bd9Sstevel@tonic-gate * If we can't find a pointer to the given type, see if 1067c478bd9Sstevel@tonic-gate * we can compute a pointer to the type resulting from 1077c478bd9Sstevel@tonic-gate * resolving the type down to its base type and use 1087c478bd9Sstevel@tonic-gate * that instead. This helps with cases where the CTF 1097c478bd9Sstevel@tonic-gate * data includes "struct foo *" but not "foo_t *" and 1107c478bd9Sstevel@tonic-gate * the user tries to access "foo_t *" in the debugger. 1117c478bd9Sstevel@tonic-gate */ 1127c478bd9Sstevel@tonic-gate ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]; 1137c478bd9Sstevel@tonic-gate if (ntype == 0) { 1147c478bd9Sstevel@tonic-gate ntype = ctf_type_resolve(fp, type); 1157c478bd9Sstevel@tonic-gate if (ntype == CTF_ERR || (ntype = fp->ctf_ptrtab[ 1167c478bd9Sstevel@tonic-gate CTF_TYPE_TO_INDEX(ntype)]) == 0) { 1177c478bd9Sstevel@tonic-gate (void) ctf_set_errno(fp, ECTF_NOTYPE); 1187c478bd9Sstevel@tonic-gate goto err; 1197c478bd9Sstevel@tonic-gate } 1207c478bd9Sstevel@tonic-gate } 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate type = CTF_INDEX_TO_TYPE(ntype, 1237c478bd9Sstevel@tonic-gate (fp->ctf_flags & LCTF_CHILD)); 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate q = p + 1; 1267c478bd9Sstevel@tonic-gate continue; 1277c478bd9Sstevel@tonic-gate } 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate if (isqualifier(p, (size_t)(q - p))) 1307c478bd9Sstevel@tonic-gate continue; /* skip qualifier keyword */ 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate for (lp = fp->ctf_lookups; lp->ctl_prefix != NULL; lp++) { 1337c478bd9Sstevel@tonic-gate if (lp->ctl_prefix[0] == '\0' || 1347c478bd9Sstevel@tonic-gate strncmp(p, lp->ctl_prefix, (size_t)(q - p)) == 0) { 1357c478bd9Sstevel@tonic-gate for (p += lp->ctl_len; isspace(*p); p++) 1367c478bd9Sstevel@tonic-gate continue; /* skip prefix and next ws */ 1377c478bd9Sstevel@tonic-gate 1387c478bd9Sstevel@tonic-gate if ((q = strchr(p, '*')) == NULL) 1397c478bd9Sstevel@tonic-gate q = end; /* compare until end */ 1407c478bd9Sstevel@tonic-gate 1417c478bd9Sstevel@tonic-gate while (isspace(q[-1])) 1427c478bd9Sstevel@tonic-gate q--; /* exclude trailing ws */ 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate if ((hp = ctf_hash_lookup(lp->ctl_hash, fp, p, 1457c478bd9Sstevel@tonic-gate (size_t)(q - p))) == NULL) { 1467c478bd9Sstevel@tonic-gate (void) ctf_set_errno(fp, ECTF_NOTYPE); 1477c478bd9Sstevel@tonic-gate goto err; 1487c478bd9Sstevel@tonic-gate } 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate type = hp->h_type; 1517c478bd9Sstevel@tonic-gate break; 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate if (lp->ctl_prefix == NULL) { 1567c478bd9Sstevel@tonic-gate (void) ctf_set_errno(fp, ECTF_NOTYPE); 1577c478bd9Sstevel@tonic-gate goto err; 1587c478bd9Sstevel@tonic-gate } 1597c478bd9Sstevel@tonic-gate } 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate if (*p != '\0' || type == 0) 1627c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_SYNTAX)); 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate return (type); 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate err: 1677c478bd9Sstevel@tonic-gate if (fp->ctf_parent != NULL && 1687c478bd9Sstevel@tonic-gate (ptype = ctf_lookup_by_name(fp->ctf_parent, name)) != CTF_ERR) 1697c478bd9Sstevel@tonic-gate return (ptype); 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate return (CTF_ERR); 1727c478bd9Sstevel@tonic-gate } 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate /* 1757c478bd9Sstevel@tonic-gate * Given a symbol table index, return the type of the data object described 1767c478bd9Sstevel@tonic-gate * by the corresponding entry in the symbol table. 1777c478bd9Sstevel@tonic-gate */ 1787c478bd9Sstevel@tonic-gate ctf_id_t 1797c478bd9Sstevel@tonic-gate ctf_lookup_by_symbol(ctf_file_t *fp, ulong_t symidx) 1807c478bd9Sstevel@tonic-gate { 1817c478bd9Sstevel@tonic-gate const ctf_sect_t *sp = &fp->ctf_symtab; 1827c478bd9Sstevel@tonic-gate ctf_id_t type; 1837c478bd9Sstevel@tonic-gate 1847c478bd9Sstevel@tonic-gate if (sp->cts_data == NULL) 1857c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOSYMTAB)); 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate if (symidx >= fp->ctf_nsyms) 1887c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, EINVAL)); 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate if (sp->cts_entsize == sizeof (Elf32_Sym)) { 1917c478bd9Sstevel@tonic-gate const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx; 1927c478bd9Sstevel@tonic-gate if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT) 1937c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOTDATA)); 1947c478bd9Sstevel@tonic-gate } else { 1957c478bd9Sstevel@tonic-gate const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx; 1967c478bd9Sstevel@tonic-gate if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) 1977c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOTDATA)); 1987c478bd9Sstevel@tonic-gate } 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate if (fp->ctf_sxlate[symidx] == -1u) 2017c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOTYPEDAT)); 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate type = *(ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]); 2047c478bd9Sstevel@tonic-gate if (type == 0) 2057c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOTYPEDAT)); 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate return (type); 2087c478bd9Sstevel@tonic-gate } 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate /* 2117c478bd9Sstevel@tonic-gate * Return the pointer to the internal CTF type data corresponding to the 2127c478bd9Sstevel@tonic-gate * given type ID. If the ID is invalid, the function returns NULL. 2137c478bd9Sstevel@tonic-gate * This function is not exported outside of the library. 2147c478bd9Sstevel@tonic-gate */ 2157c478bd9Sstevel@tonic-gate const ctf_type_t * 2167c478bd9Sstevel@tonic-gate ctf_lookup_by_id(ctf_file_t **fpp, ctf_id_t type) 2177c478bd9Sstevel@tonic-gate { 2187c478bd9Sstevel@tonic-gate ctf_file_t *fp = *fpp; /* caller passes in starting CTF container */ 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate if ((fp->ctf_flags & LCTF_CHILD) && CTF_TYPE_ISPARENT(type) && 2217c478bd9Sstevel@tonic-gate (fp = fp->ctf_parent) == NULL) { 2227c478bd9Sstevel@tonic-gate (void) ctf_set_errno(*fpp, ECTF_NOPARENT); 2237c478bd9Sstevel@tonic-gate return (NULL); 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate type = CTF_TYPE_TO_INDEX(type); 2277c478bd9Sstevel@tonic-gate if (type > 0 && type <= fp->ctf_typemax) { 2287c478bd9Sstevel@tonic-gate *fpp = fp; /* function returns ending CTF container */ 2297c478bd9Sstevel@tonic-gate return (LCTF_INDEX_TO_TYPEPTR(fp, type)); 2307c478bd9Sstevel@tonic-gate } 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate (void) ctf_set_errno(fp, ECTF_BADID); 2337c478bd9Sstevel@tonic-gate return (NULL); 2347c478bd9Sstevel@tonic-gate } 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate /* 2377c478bd9Sstevel@tonic-gate * Given a symbol table index, return the info for the function described 2387c478bd9Sstevel@tonic-gate * by the corresponding entry in the symbol table. 2397c478bd9Sstevel@tonic-gate */ 2407c478bd9Sstevel@tonic-gate int 2417c478bd9Sstevel@tonic-gate ctf_func_info(ctf_file_t *fp, ulong_t symidx, ctf_funcinfo_t *fip) 2427c478bd9Sstevel@tonic-gate { 2437c478bd9Sstevel@tonic-gate const ctf_sect_t *sp = &fp->ctf_symtab; 2447c478bd9Sstevel@tonic-gate const ushort_t *dp; 2457c478bd9Sstevel@tonic-gate ushort_t info, kind, n; 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate if (sp->cts_data == NULL) 2487c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOSYMTAB)); 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate if (symidx >= fp->ctf_nsyms) 2517c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, EINVAL)); 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate if (sp->cts_entsize == sizeof (Elf32_Sym)) { 2547c478bd9Sstevel@tonic-gate const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx; 2557c478bd9Sstevel@tonic-gate if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC) 2567c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOTFUNC)); 2577c478bd9Sstevel@tonic-gate } else { 2587c478bd9Sstevel@tonic-gate const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx; 2597c478bd9Sstevel@tonic-gate if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC) 2607c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOTFUNC)); 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate if (fp->ctf_sxlate[symidx] == -1u) 2647c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOFUNCDAT)); 2657c478bd9Sstevel@tonic-gate 2667c478bd9Sstevel@tonic-gate dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]); 2677c478bd9Sstevel@tonic-gate 2687c478bd9Sstevel@tonic-gate info = *dp++; 2697c478bd9Sstevel@tonic-gate kind = LCTF_INFO_KIND(fp, info); 2707c478bd9Sstevel@tonic-gate n = LCTF_INFO_VLEN(fp, info); 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate if (kind == CTF_K_UNKNOWN && n == 0) 2737c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_NOFUNCDAT)); 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate if (kind != CTF_K_FUNCTION) 2767c478bd9Sstevel@tonic-gate return (ctf_set_errno(fp, ECTF_CORRUPT)); 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate fip->ctc_return = *dp++; 2797c478bd9Sstevel@tonic-gate fip->ctc_argc = n; 2807c478bd9Sstevel@tonic-gate fip->ctc_flags = 0; 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate if (n != 0 && dp[n - 1] == 0) { 2837c478bd9Sstevel@tonic-gate fip->ctc_flags |= CTF_FUNC_VARARG; 2847c478bd9Sstevel@tonic-gate fip->ctc_argc--; 2857c478bd9Sstevel@tonic-gate } 2867c478bd9Sstevel@tonic-gate 2877c478bd9Sstevel@tonic-gate return (0); 2887c478bd9Sstevel@tonic-gate } 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate /* 2917c478bd9Sstevel@tonic-gate * Given a symbol table index, return the arguments for the function described 2927c478bd9Sstevel@tonic-gate * by the corresponding entry in the symbol table. 2937c478bd9Sstevel@tonic-gate */ 2947c478bd9Sstevel@tonic-gate int 2957c478bd9Sstevel@tonic-gate ctf_func_args(ctf_file_t *fp, ulong_t symidx, uint_t argc, ctf_id_t *argv) 2967c478bd9Sstevel@tonic-gate { 2977c478bd9Sstevel@tonic-gate const ushort_t *dp; 2987c478bd9Sstevel@tonic-gate ctf_funcinfo_t f; 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate if (ctf_func_info(fp, symidx, &f) == CTF_ERR) 3017c478bd9Sstevel@tonic-gate return (CTF_ERR); /* errno is set for us */ 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate /* 3047c478bd9Sstevel@tonic-gate * The argument data is two ushort_t's past the translation table 3057c478bd9Sstevel@tonic-gate * offset: one for the function info, and one for the return type. 3067c478bd9Sstevel@tonic-gate */ 3077c478bd9Sstevel@tonic-gate dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]) + 2; 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate for (argc = MIN(argc, f.ctc_argc); argc != 0; argc--) 3107c478bd9Sstevel@tonic-gate *argv++ = *dp++; 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate return (0); 3137c478bd9Sstevel@tonic-gate } 314