xref: /freebsd/sys/cddl/dev/fbt/fbt.c (revision 30b68ecd)
191eaf3e1SJohn Birrell /*
291eaf3e1SJohn Birrell  * CDDL HEADER START
391eaf3e1SJohn Birrell  *
491eaf3e1SJohn Birrell  * The contents of this file are subject to the terms of the
591eaf3e1SJohn Birrell  * Common Development and Distribution License (the "License").
691eaf3e1SJohn Birrell  * You may not use this file except in compliance with the License.
791eaf3e1SJohn Birrell  *
891eaf3e1SJohn Birrell  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
991eaf3e1SJohn Birrell  * or http://www.opensolaris.org/os/licensing.
1091eaf3e1SJohn Birrell  * See the License for the specific language governing permissions
1191eaf3e1SJohn Birrell  * and limitations under the License.
1291eaf3e1SJohn Birrell  *
1391eaf3e1SJohn Birrell  * When distributing Covered Code, include this CDDL HEADER in each
1491eaf3e1SJohn Birrell  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1591eaf3e1SJohn Birrell  * If applicable, add the following below this CDDL HEADER, with the
1691eaf3e1SJohn Birrell  * fields enclosed by brackets "[]" replaced with your own identifying
1791eaf3e1SJohn Birrell  * information: Portions Copyright [yyyy] [name of copyright owner]
1891eaf3e1SJohn Birrell  *
1991eaf3e1SJohn Birrell  * CDDL HEADER END
2091eaf3e1SJohn Birrell  *
2191eaf3e1SJohn Birrell  * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
2291eaf3e1SJohn Birrell  *
2391eaf3e1SJohn Birrell  * $FreeBSD$
2491eaf3e1SJohn Birrell  *
2591eaf3e1SJohn Birrell  */
2691eaf3e1SJohn Birrell 
2791eaf3e1SJohn Birrell /*
2891eaf3e1SJohn Birrell  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2991eaf3e1SJohn Birrell  * Use is subject to license terms.
3091eaf3e1SJohn Birrell  */
3191eaf3e1SJohn Birrell 
3291eaf3e1SJohn Birrell #include <sys/cdefs.h>
3391eaf3e1SJohn Birrell #include <sys/param.h>
3491eaf3e1SJohn Birrell #include <sys/systm.h>
3591eaf3e1SJohn Birrell #include <sys/conf.h>
3691eaf3e1SJohn Birrell #include <sys/cpuvar.h>
379e5787d2SMatt Macy #include <sys/endian.h>
3891eaf3e1SJohn Birrell #include <sys/fcntl.h>
3991eaf3e1SJohn Birrell #include <sys/filio.h>
4091eaf3e1SJohn Birrell #include <sys/kdb.h>
4191eaf3e1SJohn Birrell #include <sys/kernel.h>
4291eaf3e1SJohn Birrell #include <sys/kmem.h>
4391eaf3e1SJohn Birrell #include <sys/kthread.h>
4491eaf3e1SJohn Birrell #include <sys/limits.h>
4591eaf3e1SJohn Birrell #include <sys/linker.h>
4691eaf3e1SJohn Birrell #include <sys/lock.h>
4791eaf3e1SJohn Birrell #include <sys/malloc.h>
4891eaf3e1SJohn Birrell #include <sys/module.h>
4991eaf3e1SJohn Birrell #include <sys/mutex.h>
5091eaf3e1SJohn Birrell #include <sys/pcpu.h>
5191eaf3e1SJohn Birrell #include <sys/poll.h>
5291eaf3e1SJohn Birrell #include <sys/proc.h>
5391eaf3e1SJohn Birrell #include <sys/selinfo.h>
5491eaf3e1SJohn Birrell #include <sys/smp.h>
5591eaf3e1SJohn Birrell #include <sys/syscall.h>
5691eaf3e1SJohn Birrell #include <sys/sysent.h>
5791eaf3e1SJohn Birrell #include <sys/sysproto.h>
5891eaf3e1SJohn Birrell #include <sys/uio.h>
5991eaf3e1SJohn Birrell #include <sys/unistd.h>
6091eaf3e1SJohn Birrell #include <machine/stdarg.h>
6191eaf3e1SJohn Birrell 
6291eaf3e1SJohn Birrell #include <sys/dtrace.h>
6391eaf3e1SJohn Birrell #include <sys/dtrace_bsd.h>
6491eaf3e1SJohn Birrell 
65266b4a78SMark Johnston #include "fbt.h"
6691eaf3e1SJohn Birrell 
67266b4a78SMark Johnston MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing");
6891eaf3e1SJohn Birrell 
69266b4a78SMark Johnston dtrace_provider_id_t	fbt_id;
70266b4a78SMark Johnston fbt_probe_t		**fbt_probetab;
71266b4a78SMark Johnston int			fbt_probetab_mask;
7291eaf3e1SJohn Birrell 
7391eaf3e1SJohn Birrell static d_open_t	fbt_open;
7491eaf3e1SJohn Birrell static int	fbt_unload(void);
7591eaf3e1SJohn Birrell static void	fbt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
7691eaf3e1SJohn Birrell static void	fbt_provide_module(void *, modctl_t *);
7791eaf3e1SJohn Birrell static void	fbt_destroy(void *, dtrace_id_t, void *);
7891eaf3e1SJohn Birrell static void	fbt_enable(void *, dtrace_id_t, void *);
7991eaf3e1SJohn Birrell static void	fbt_disable(void *, dtrace_id_t, void *);
8091eaf3e1SJohn Birrell static void	fbt_load(void *);
8191eaf3e1SJohn Birrell static void	fbt_suspend(void *, dtrace_id_t, void *);
8291eaf3e1SJohn Birrell static void	fbt_resume(void *, dtrace_id_t, void *);
8391eaf3e1SJohn Birrell 
8491eaf3e1SJohn Birrell static struct cdevsw fbt_cdevsw = {
8591eaf3e1SJohn Birrell 	.d_version	= D_VERSION,
8691eaf3e1SJohn Birrell 	.d_open		= fbt_open,
8791eaf3e1SJohn Birrell 	.d_name		= "fbt",
8891eaf3e1SJohn Birrell };
8991eaf3e1SJohn Birrell 
9091eaf3e1SJohn Birrell static dtrace_pattr_t fbt_attr = {
9191eaf3e1SJohn Birrell { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
9291eaf3e1SJohn Birrell { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
9391eaf3e1SJohn Birrell { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
9491eaf3e1SJohn Birrell { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
9591eaf3e1SJohn Birrell { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
9691eaf3e1SJohn Birrell };
9791eaf3e1SJohn Birrell 
9891eaf3e1SJohn Birrell static dtrace_pops_t fbt_pops = {
9947f11baaSMark Johnston 	.dtps_provide =		NULL,
10047f11baaSMark Johnston 	.dtps_provide_module =	fbt_provide_module,
10147f11baaSMark Johnston 	.dtps_enable =		fbt_enable,
10247f11baaSMark Johnston 	.dtps_disable =		fbt_disable,
10347f11baaSMark Johnston 	.dtps_suspend =		fbt_suspend,
10447f11baaSMark Johnston 	.dtps_resume =		fbt_resume,
10547f11baaSMark Johnston 	.dtps_getargdesc =	fbt_getargdesc,
10647f11baaSMark Johnston 	.dtps_getargval =	NULL,
10747f11baaSMark Johnston 	.dtps_usermode =	NULL,
10847f11baaSMark Johnston 	.dtps_destroy =		fbt_destroy
10991eaf3e1SJohn Birrell };
11091eaf3e1SJohn Birrell 
11191eaf3e1SJohn Birrell static struct cdev		*fbt_cdev;
11291eaf3e1SJohn Birrell static int			fbt_probetab_size;
11391eaf3e1SJohn Birrell static int			fbt_verbose = 0;
11491eaf3e1SJohn Birrell 
1150ff41755SRuslan Bukin int
1160ff41755SRuslan Bukin fbt_excluded(const char *name)
1170ff41755SRuslan Bukin {
1180ff41755SRuslan Bukin 
1190ff41755SRuslan Bukin 	if (strncmp(name, "dtrace_", 7) == 0 &&
1200ff41755SRuslan Bukin 	    strncmp(name, "dtrace_safe_", 12) != 0) {
1210ff41755SRuslan Bukin 		/*
1220ff41755SRuslan Bukin 		 * Anything beginning with "dtrace_" may be called
1230ff41755SRuslan Bukin 		 * from probe context unless it explicitly indicates
1240ff41755SRuslan Bukin 		 * that it won't be called from probe context by
1250ff41755SRuslan Bukin 		 * using the prefix "dtrace_safe_".
1260ff41755SRuslan Bukin 		 */
1270ff41755SRuslan Bukin 		return (1);
1280ff41755SRuslan Bukin 	}
1290ff41755SRuslan Bukin 
1300ff41755SRuslan Bukin 	/*
13130b68ecdSRobert Watson 	 * Omit instrumentation of functions that are probably in DDB.  It
13230b68ecdSRobert Watson 	 * makes it too hard to debug broken FBT.
13330b68ecdSRobert Watson 	 *
13430b68ecdSRobert Watson 	 * NB: kdb_enter() can be excluded, but its call to printf() can't be.
13530b68ecdSRobert Watson 	 * This is generally OK since we're not yet in debugging context.
13630b68ecdSRobert Watson 	 */
13730b68ecdSRobert Watson 	if (strncmp(name, "db_", 3) == 0 ||
13830b68ecdSRobert Watson 	    strncmp(name, "kdb_", 4) == 0)
13930b68ecdSRobert Watson 		return (1);
14030b68ecdSRobert Watson 
14130b68ecdSRobert Watson 	/*
142f99a5172SMark Johnston 	 * Lock owner methods may be called from probe context.
143f99a5172SMark Johnston 	 */
144f99a5172SMark Johnston 	if (strcmp(name, "owner_mtx") == 0 ||
145f99a5172SMark Johnston 	    strcmp(name, "owner_rm") == 0 ||
146f99a5172SMark Johnston 	    strcmp(name, "owner_rw") == 0 ||
147f99a5172SMark Johnston 	    strcmp(name, "owner_sx") == 0)
148f99a5172SMark Johnston 		return (1);
149f99a5172SMark Johnston 
150f99a5172SMark Johnston 	/*
1519b9e7f4cSJohn Baldwin 	 * Stack unwinders may be called from probe context on some
1529b9e7f4cSJohn Baldwin 	 * platforms.
1539b9e7f4cSJohn Baldwin 	 */
154ae953968SJohn Baldwin #if defined(__aarch64__) || defined(__riscv)
1559b9e7f4cSJohn Baldwin 	if (strcmp(name, "unwind_frame") == 0)
1569b9e7f4cSJohn Baldwin 		return (1);
1579b9e7f4cSJohn Baldwin #endif
1589b9e7f4cSJohn Baldwin 
1599b9e7f4cSJohn Baldwin 	/*
1600ff41755SRuslan Bukin 	 * When DTrace is built into the kernel we need to exclude
1610ff41755SRuslan Bukin 	 * the FBT functions from instrumentation.
1620ff41755SRuslan Bukin 	 */
1630ff41755SRuslan Bukin #ifndef _KLD_MODULE
1640ff41755SRuslan Bukin 	if (strncmp(name, "fbt_", 4) == 0)
1650ff41755SRuslan Bukin 		return (1);
1660ff41755SRuslan Bukin #endif
1670ff41755SRuslan Bukin 
1680ff41755SRuslan Bukin 	return (0);
1690ff41755SRuslan Bukin }
1700ff41755SRuslan Bukin 
17191eaf3e1SJohn Birrell static void
17291eaf3e1SJohn Birrell fbt_doubletrap(void)
17391eaf3e1SJohn Birrell {
17491eaf3e1SJohn Birrell 	fbt_probe_t *fbt;
17591eaf3e1SJohn Birrell 	int i;
17691eaf3e1SJohn Birrell 
17791eaf3e1SJohn Birrell 	for (i = 0; i < fbt_probetab_size; i++) {
17891eaf3e1SJohn Birrell 		fbt = fbt_probetab[i];
17991eaf3e1SJohn Birrell 
180c208cb99SMark Johnston 		for (; fbt != NULL; fbt = fbt->fbtp_probenext)
18135127d3cSMark Johnston 			fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
18291eaf3e1SJohn Birrell 	}
18391eaf3e1SJohn Birrell }
18491eaf3e1SJohn Birrell 
18591eaf3e1SJohn Birrell static void
18691eaf3e1SJohn Birrell fbt_provide_module(void *arg, modctl_t *lf)
18791eaf3e1SJohn Birrell {
18891eaf3e1SJohn Birrell 	char modname[MAXPATHLEN];
18991eaf3e1SJohn Birrell 	int i;
19091eaf3e1SJohn Birrell 	size_t len;
19191eaf3e1SJohn Birrell 
19291eaf3e1SJohn Birrell 	strlcpy(modname, lf->filename, sizeof(modname));
19391eaf3e1SJohn Birrell 	len = strlen(modname);
19491eaf3e1SJohn Birrell 	if (len > 3 && strcmp(modname + len - 3, ".ko") == 0)
19591eaf3e1SJohn Birrell 		modname[len - 3] = '\0';
19691eaf3e1SJohn Birrell 
19791eaf3e1SJohn Birrell 	/*
19891eaf3e1SJohn Birrell 	 * Employees of dtrace and their families are ineligible.  Void
19991eaf3e1SJohn Birrell 	 * where prohibited.
20091eaf3e1SJohn Birrell 	 */
20191eaf3e1SJohn Birrell 	if (strcmp(modname, "dtrace") == 0)
20291eaf3e1SJohn Birrell 		return;
20391eaf3e1SJohn Birrell 
20491eaf3e1SJohn Birrell 	/*
20591eaf3e1SJohn Birrell 	 * To register with DTrace, a module must list 'dtrace' as a
20691eaf3e1SJohn Birrell 	 * dependency in order for the kernel linker to resolve
20791eaf3e1SJohn Birrell 	 * symbols like dtrace_register(). All modules with such a
20891eaf3e1SJohn Birrell 	 * dependency are ineligible for FBT tracing.
20991eaf3e1SJohn Birrell 	 */
21091eaf3e1SJohn Birrell 	for (i = 0; i < lf->ndeps; i++)
21191eaf3e1SJohn Birrell 		if (strncmp(lf->deps[i]->filename, "dtrace", 6) == 0)
21291eaf3e1SJohn Birrell 			return;
21391eaf3e1SJohn Birrell 
21491eaf3e1SJohn Birrell 	if (lf->fbt_nentries) {
21591eaf3e1SJohn Birrell 		/*
21691eaf3e1SJohn Birrell 		 * This module has some FBT entries allocated; we're afraid
21791eaf3e1SJohn Birrell 		 * to screw with it.
21891eaf3e1SJohn Birrell 		 */
21991eaf3e1SJohn Birrell 		return;
22091eaf3e1SJohn Birrell 	}
22191eaf3e1SJohn Birrell 
22291eaf3e1SJohn Birrell 	/*
22391eaf3e1SJohn Birrell 	 * List the functions in the module and the symbol values.
22491eaf3e1SJohn Birrell 	 */
22591eaf3e1SJohn Birrell 	(void) linker_file_function_listall(lf, fbt_provide_module_function, modname);
22691eaf3e1SJohn Birrell }
22791eaf3e1SJohn Birrell 
22891eaf3e1SJohn Birrell static void
229c208cb99SMark Johnston fbt_destroy_one(fbt_probe_t *fbt)
230c208cb99SMark Johnston {
231c208cb99SMark Johnston 	fbt_probe_t *hash, *hashprev, *next;
232c208cb99SMark Johnston 	int ndx;
233c208cb99SMark Johnston 
234c208cb99SMark Johnston 	ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
235c208cb99SMark Johnston 	for (hash = fbt_probetab[ndx], hashprev = NULL; hash != NULL;
236a9d49f9eSMark Johnston 	    hashprev = hash, hash = hash->fbtp_hashnext) {
237c208cb99SMark Johnston 		if (hash == fbt) {
238c208cb99SMark Johnston 			if ((next = fbt->fbtp_tracenext) != NULL)
239c208cb99SMark Johnston 				next->fbtp_hashnext = hash->fbtp_hashnext;
240c208cb99SMark Johnston 			else
241c208cb99SMark Johnston 				next = hash->fbtp_hashnext;
242c208cb99SMark Johnston 			if (hashprev != NULL)
243c208cb99SMark Johnston 				hashprev->fbtp_hashnext = next;
244c208cb99SMark Johnston 			else
245c208cb99SMark Johnston 				fbt_probetab[ndx] = next;
246c208cb99SMark Johnston 			goto free;
247c208cb99SMark Johnston 		} else if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
248c208cb99SMark Johnston 			for (next = hash; next->fbtp_tracenext != NULL;
249c208cb99SMark Johnston 			    next = next->fbtp_tracenext) {
250c208cb99SMark Johnston 				if (fbt == next->fbtp_tracenext) {
251c208cb99SMark Johnston 					next->fbtp_tracenext =
252c208cb99SMark Johnston 					    fbt->fbtp_tracenext;
253c208cb99SMark Johnston 					goto free;
254c208cb99SMark Johnston 				}
255c208cb99SMark Johnston 			}
256c208cb99SMark Johnston 		}
257c208cb99SMark Johnston 	}
258c208cb99SMark Johnston 	panic("probe %p not found in hash table", fbt);
259c208cb99SMark Johnston free:
260c208cb99SMark Johnston 	free(fbt, M_FBT);
261c208cb99SMark Johnston }
262c208cb99SMark Johnston 
263c208cb99SMark Johnston static void
26491eaf3e1SJohn Birrell fbt_destroy(void *arg, dtrace_id_t id, void *parg)
26591eaf3e1SJohn Birrell {
266c208cb99SMark Johnston 	fbt_probe_t *fbt = parg, *next;
26791eaf3e1SJohn Birrell 	modctl_t *ctl;
26891eaf3e1SJohn Birrell 
26991eaf3e1SJohn Birrell 	do {
27091eaf3e1SJohn Birrell 		ctl = fbt->fbtp_ctl;
27191eaf3e1SJohn Birrell 		ctl->fbt_nentries--;
27291eaf3e1SJohn Birrell 
273c208cb99SMark Johnston 		next = fbt->fbtp_probenext;
274c208cb99SMark Johnston 		fbt_destroy_one(fbt);
27591eaf3e1SJohn Birrell 		fbt = next;
27691eaf3e1SJohn Birrell 	} while (fbt != NULL);
27791eaf3e1SJohn Birrell }
27891eaf3e1SJohn Birrell 
27991eaf3e1SJohn Birrell static void
28091eaf3e1SJohn Birrell fbt_enable(void *arg, dtrace_id_t id, void *parg)
28191eaf3e1SJohn Birrell {
28291eaf3e1SJohn Birrell 	fbt_probe_t *fbt = parg;
28391eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
28491eaf3e1SJohn Birrell 
28591eaf3e1SJohn Birrell 	ctl->nenabled++;
28691eaf3e1SJohn Birrell 
28791eaf3e1SJohn Birrell 	/*
28891eaf3e1SJohn Birrell 	 * Now check that our modctl has the expected load count.  If it
28991eaf3e1SJohn Birrell 	 * doesn't, this module must have been unloaded and reloaded -- and
29091eaf3e1SJohn Birrell 	 * we're not going to touch it.
29191eaf3e1SJohn Birrell 	 */
29291eaf3e1SJohn Birrell 	if (ctl->loadcnt != fbt->fbtp_loadcnt) {
29391eaf3e1SJohn Birrell 		if (fbt_verbose) {
29491eaf3e1SJohn Birrell 			printf("fbt is failing for probe %s "
29591eaf3e1SJohn Birrell 			    "(module %s reloaded)",
29691eaf3e1SJohn Birrell 			    fbt->fbtp_name, ctl->filename);
29791eaf3e1SJohn Birrell 		}
29891eaf3e1SJohn Birrell 
29991eaf3e1SJohn Birrell 		return;
30091eaf3e1SJohn Birrell 	}
30191eaf3e1SJohn Birrell 
302c208cb99SMark Johnston 	for (; fbt != NULL; fbt = fbt->fbtp_probenext) {
303266b4a78SMark Johnston 		fbt_patch_tracepoint(fbt, fbt->fbtp_patchval);
304c208cb99SMark Johnston 		fbt->fbtp_enabled++;
305c208cb99SMark Johnston 	}
30691eaf3e1SJohn Birrell }
30791eaf3e1SJohn Birrell 
30891eaf3e1SJohn Birrell static void
30991eaf3e1SJohn Birrell fbt_disable(void *arg, dtrace_id_t id, void *parg)
31091eaf3e1SJohn Birrell {
311c208cb99SMark Johnston 	fbt_probe_t *fbt = parg, *hash;
31291eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
31391eaf3e1SJohn Birrell 
31491eaf3e1SJohn Birrell 	ASSERT(ctl->nenabled > 0);
31591eaf3e1SJohn Birrell 	ctl->nenabled--;
31691eaf3e1SJohn Birrell 
31791eaf3e1SJohn Birrell 	if ((ctl->loadcnt != fbt->fbtp_loadcnt))
31891eaf3e1SJohn Birrell 		return;
31991eaf3e1SJohn Birrell 
320c208cb99SMark Johnston 	for (; fbt != NULL; fbt = fbt->fbtp_probenext) {
321c208cb99SMark Johnston 		fbt->fbtp_enabled--;
322c208cb99SMark Johnston 
323c208cb99SMark Johnston 		for (hash = fbt_probetab[FBT_ADDR2NDX(fbt->fbtp_patchpoint)];
324c208cb99SMark Johnston 		    hash != NULL; hash = hash->fbtp_hashnext) {
325c208cb99SMark Johnston 			if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
326c208cb99SMark Johnston 				for (; hash != NULL; hash = hash->fbtp_tracenext)
327c208cb99SMark Johnston 					if (hash->fbtp_enabled > 0)
328c208cb99SMark Johnston 						break;
329c208cb99SMark Johnston 				break;
330c208cb99SMark Johnston 			}
331c208cb99SMark Johnston 		}
332c208cb99SMark Johnston 		if (hash == NULL)
33335127d3cSMark Johnston 			fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
33491eaf3e1SJohn Birrell 	}
335c208cb99SMark Johnston }
33691eaf3e1SJohn Birrell 
33791eaf3e1SJohn Birrell static void
33891eaf3e1SJohn Birrell fbt_suspend(void *arg, dtrace_id_t id, void *parg)
33991eaf3e1SJohn Birrell {
34091eaf3e1SJohn Birrell 	fbt_probe_t *fbt = parg;
34191eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
34291eaf3e1SJohn Birrell 
34391eaf3e1SJohn Birrell 	ASSERT(ctl->nenabled > 0);
34491eaf3e1SJohn Birrell 
34591eaf3e1SJohn Birrell 	if ((ctl->loadcnt != fbt->fbtp_loadcnt))
34691eaf3e1SJohn Birrell 		return;
34791eaf3e1SJohn Birrell 
348c208cb99SMark Johnston 	for (; fbt != NULL; fbt = fbt->fbtp_probenext)
34935127d3cSMark Johnston 		fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
35091eaf3e1SJohn Birrell }
35191eaf3e1SJohn Birrell 
35291eaf3e1SJohn Birrell static void
35391eaf3e1SJohn Birrell fbt_resume(void *arg, dtrace_id_t id, void *parg)
35491eaf3e1SJohn Birrell {
35591eaf3e1SJohn Birrell 	fbt_probe_t *fbt = parg;
35691eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
35791eaf3e1SJohn Birrell 
35891eaf3e1SJohn Birrell 	ASSERT(ctl->nenabled > 0);
35991eaf3e1SJohn Birrell 
36091eaf3e1SJohn Birrell 	if ((ctl->loadcnt != fbt->fbtp_loadcnt))
36191eaf3e1SJohn Birrell 		return;
36291eaf3e1SJohn Birrell 
363c208cb99SMark Johnston 	for (; fbt != NULL; fbt = fbt->fbtp_probenext)
364266b4a78SMark Johnston 		fbt_patch_tracepoint(fbt, fbt->fbtp_patchval);
36591eaf3e1SJohn Birrell }
36691eaf3e1SJohn Birrell 
36791eaf3e1SJohn Birrell static int
36891eaf3e1SJohn Birrell fbt_ctfoff_init(modctl_t *lf, linker_ctf_t *lc)
36991eaf3e1SJohn Birrell {
37091eaf3e1SJohn Birrell 	const Elf_Sym *symp = lc->symtab;;
37191eaf3e1SJohn Birrell 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
37291eaf3e1SJohn Birrell 	const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
37391eaf3e1SJohn Birrell 	int i;
37491eaf3e1SJohn Birrell 	uint32_t *ctfoff;
37591eaf3e1SJohn Birrell 	uint32_t objtoff = hp->cth_objtoff;
37691eaf3e1SJohn Birrell 	uint32_t funcoff = hp->cth_funcoff;
37791eaf3e1SJohn Birrell 	ushort_t info;
37891eaf3e1SJohn Birrell 	ushort_t vlen;
37991eaf3e1SJohn Birrell 
38091eaf3e1SJohn Birrell 	/* Sanity check. */
38191eaf3e1SJohn Birrell 	if (hp->cth_magic != CTF_MAGIC) {
38291eaf3e1SJohn Birrell 		printf("Bad magic value in CTF data of '%s'\n",lf->pathname);
38391eaf3e1SJohn Birrell 		return (EINVAL);
38491eaf3e1SJohn Birrell 	}
38591eaf3e1SJohn Birrell 
38691eaf3e1SJohn Birrell 	if (lc->symtab == NULL) {
38791eaf3e1SJohn Birrell 		printf("No symbol table in '%s'\n",lf->pathname);
38891eaf3e1SJohn Birrell 		return (EINVAL);
38991eaf3e1SJohn Birrell 	}
39091eaf3e1SJohn Birrell 
391d258fd1dSMark Johnston 	ctfoff = malloc(sizeof(uint32_t) * lc->nsym, M_LINKER, M_WAITOK);
39291eaf3e1SJohn Birrell 	*lc->ctfoffp = ctfoff;
39391eaf3e1SJohn Birrell 
39491eaf3e1SJohn Birrell 	for (i = 0; i < lc->nsym; i++, ctfoff++, symp++) {
39591eaf3e1SJohn Birrell 		if (symp->st_name == 0 || symp->st_shndx == SHN_UNDEF) {
39691eaf3e1SJohn Birrell 			*ctfoff = 0xffffffff;
39791eaf3e1SJohn Birrell 			continue;
39891eaf3e1SJohn Birrell 		}
39991eaf3e1SJohn Birrell 
40091eaf3e1SJohn Birrell 		switch (ELF_ST_TYPE(symp->st_info)) {
40191eaf3e1SJohn Birrell 		case STT_OBJECT:
40291eaf3e1SJohn Birrell 			if (objtoff >= hp->cth_funcoff ||
40391eaf3e1SJohn Birrell                             (symp->st_shndx == SHN_ABS && symp->st_value == 0)) {
40491eaf3e1SJohn Birrell 				*ctfoff = 0xffffffff;
40591eaf3e1SJohn Birrell                                 break;
40691eaf3e1SJohn Birrell                         }
40791eaf3e1SJohn Birrell 
40891eaf3e1SJohn Birrell                         *ctfoff = objtoff;
40991eaf3e1SJohn Birrell                         objtoff += sizeof (ushort_t);
41091eaf3e1SJohn Birrell 			break;
41191eaf3e1SJohn Birrell 
41291eaf3e1SJohn Birrell 		case STT_FUNC:
41391eaf3e1SJohn Birrell 			if (funcoff >= hp->cth_typeoff) {
41491eaf3e1SJohn Birrell 				*ctfoff = 0xffffffff;
41591eaf3e1SJohn Birrell 				break;
41691eaf3e1SJohn Birrell 			}
41791eaf3e1SJohn Birrell 
41891eaf3e1SJohn Birrell 			*ctfoff = funcoff;
41991eaf3e1SJohn Birrell 
42091eaf3e1SJohn Birrell 			info = *((const ushort_t *)(ctfdata + funcoff));
42191eaf3e1SJohn Birrell 			vlen = CTF_INFO_VLEN(info);
42291eaf3e1SJohn Birrell 
42391eaf3e1SJohn Birrell 			/*
42491eaf3e1SJohn Birrell 			 * If we encounter a zero pad at the end, just skip it.
42591eaf3e1SJohn Birrell 			 * Otherwise skip over the function and its return type
42691eaf3e1SJohn Birrell 			 * (+2) and the argument list (vlen).
42791eaf3e1SJohn Birrell 			 */
42891eaf3e1SJohn Birrell 			if (CTF_INFO_KIND(info) == CTF_K_UNKNOWN && vlen == 0)
42991eaf3e1SJohn Birrell 				funcoff += sizeof (ushort_t); /* skip pad */
43091eaf3e1SJohn Birrell 			else
43191eaf3e1SJohn Birrell 				funcoff += sizeof (ushort_t) * (vlen + 2);
43291eaf3e1SJohn Birrell 			break;
43391eaf3e1SJohn Birrell 
43491eaf3e1SJohn Birrell 		default:
43591eaf3e1SJohn Birrell 			*ctfoff = 0xffffffff;
43691eaf3e1SJohn Birrell 			break;
43791eaf3e1SJohn Birrell 		}
43891eaf3e1SJohn Birrell 	}
43991eaf3e1SJohn Birrell 
44091eaf3e1SJohn Birrell 	return (0);
44191eaf3e1SJohn Birrell }
44291eaf3e1SJohn Birrell 
44391eaf3e1SJohn Birrell static ssize_t
44491eaf3e1SJohn Birrell fbt_get_ctt_size(uint8_t version, const ctf_type_t *tp, ssize_t *sizep,
44591eaf3e1SJohn Birrell     ssize_t *incrementp)
44691eaf3e1SJohn Birrell {
44791eaf3e1SJohn Birrell 	ssize_t size, increment;
44891eaf3e1SJohn Birrell 
44991eaf3e1SJohn Birrell 	if (version > CTF_VERSION_1 &&
45091eaf3e1SJohn Birrell 	    tp->ctt_size == CTF_LSIZE_SENT) {
45191eaf3e1SJohn Birrell 		size = CTF_TYPE_LSIZE(tp);
45291eaf3e1SJohn Birrell 		increment = sizeof (ctf_type_t);
45391eaf3e1SJohn Birrell 	} else {
45491eaf3e1SJohn Birrell 		size = tp->ctt_size;
45591eaf3e1SJohn Birrell 		increment = sizeof (ctf_stype_t);
45691eaf3e1SJohn Birrell 	}
45791eaf3e1SJohn Birrell 
45891eaf3e1SJohn Birrell 	if (sizep)
45991eaf3e1SJohn Birrell 		*sizep = size;
46091eaf3e1SJohn Birrell 	if (incrementp)
46191eaf3e1SJohn Birrell 		*incrementp = increment;
46291eaf3e1SJohn Birrell 
46391eaf3e1SJohn Birrell 	return (size);
46491eaf3e1SJohn Birrell }
46591eaf3e1SJohn Birrell 
46691eaf3e1SJohn Birrell static int
46791eaf3e1SJohn Birrell fbt_typoff_init(linker_ctf_t *lc)
46891eaf3e1SJohn Birrell {
46991eaf3e1SJohn Birrell 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
47091eaf3e1SJohn Birrell 	const ctf_type_t *tbuf;
47191eaf3e1SJohn Birrell 	const ctf_type_t *tend;
47291eaf3e1SJohn Birrell 	const ctf_type_t *tp;
47391eaf3e1SJohn Birrell 	const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
47491eaf3e1SJohn Birrell 	int ctf_typemax = 0;
47591eaf3e1SJohn Birrell 	uint32_t *xp;
47691eaf3e1SJohn Birrell 	ulong_t pop[CTF_K_MAX + 1] = { 0 };
47791eaf3e1SJohn Birrell 
47891eaf3e1SJohn Birrell 
47991eaf3e1SJohn Birrell 	/* Sanity check. */
48091eaf3e1SJohn Birrell 	if (hp->cth_magic != CTF_MAGIC)
48191eaf3e1SJohn Birrell 		return (EINVAL);
48291eaf3e1SJohn Birrell 
48391eaf3e1SJohn Birrell 	tbuf = (const ctf_type_t *) (ctfdata + hp->cth_typeoff);
48491eaf3e1SJohn Birrell 	tend = (const ctf_type_t *) (ctfdata + hp->cth_stroff);
48591eaf3e1SJohn Birrell 
48691eaf3e1SJohn Birrell 	int child = hp->cth_parname != 0;
48791eaf3e1SJohn Birrell 
48891eaf3e1SJohn Birrell 	/*
48991eaf3e1SJohn Birrell 	 * We make two passes through the entire type section.  In this first
49091eaf3e1SJohn Birrell 	 * pass, we count the number of each type and the total number of types.
49191eaf3e1SJohn Birrell 	 */
49291eaf3e1SJohn Birrell 	for (tp = tbuf; tp < tend; ctf_typemax++) {
49391eaf3e1SJohn Birrell 		ushort_t kind = CTF_INFO_KIND(tp->ctt_info);
49491eaf3e1SJohn Birrell 		ulong_t vlen = CTF_INFO_VLEN(tp->ctt_info);
49591eaf3e1SJohn Birrell 		ssize_t size, increment;
49691eaf3e1SJohn Birrell 
49791eaf3e1SJohn Birrell 		size_t vbytes;
49891eaf3e1SJohn Birrell 		uint_t n;
49991eaf3e1SJohn Birrell 
50091eaf3e1SJohn Birrell 		(void) fbt_get_ctt_size(hp->cth_version, tp, &size, &increment);
50191eaf3e1SJohn Birrell 
50291eaf3e1SJohn Birrell 		switch (kind) {
50391eaf3e1SJohn Birrell 		case CTF_K_INTEGER:
50491eaf3e1SJohn Birrell 		case CTF_K_FLOAT:
50591eaf3e1SJohn Birrell 			vbytes = sizeof (uint_t);
50691eaf3e1SJohn Birrell 			break;
50791eaf3e1SJohn Birrell 		case CTF_K_ARRAY:
50891eaf3e1SJohn Birrell 			vbytes = sizeof (ctf_array_t);
50991eaf3e1SJohn Birrell 			break;
51091eaf3e1SJohn Birrell 		case CTF_K_FUNCTION:
51191eaf3e1SJohn Birrell 			vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
51291eaf3e1SJohn Birrell 			break;
51391eaf3e1SJohn Birrell 		case CTF_K_STRUCT:
51491eaf3e1SJohn Birrell 		case CTF_K_UNION:
51591eaf3e1SJohn Birrell 			if (size < CTF_LSTRUCT_THRESH) {
51691eaf3e1SJohn Birrell 				ctf_member_t *mp = (ctf_member_t *)
51791eaf3e1SJohn Birrell 				    ((uintptr_t)tp + increment);
51891eaf3e1SJohn Birrell 
51991eaf3e1SJohn Birrell 				vbytes = sizeof (ctf_member_t) * vlen;
52091eaf3e1SJohn Birrell 				for (n = vlen; n != 0; n--, mp++)
52191eaf3e1SJohn Birrell 					child |= CTF_TYPE_ISCHILD(mp->ctm_type);
52291eaf3e1SJohn Birrell 			} else {
52391eaf3e1SJohn Birrell 				ctf_lmember_t *lmp = (ctf_lmember_t *)
52491eaf3e1SJohn Birrell 				    ((uintptr_t)tp + increment);
52591eaf3e1SJohn Birrell 
52691eaf3e1SJohn Birrell 				vbytes = sizeof (ctf_lmember_t) * vlen;
52791eaf3e1SJohn Birrell 				for (n = vlen; n != 0; n--, lmp++)
52891eaf3e1SJohn Birrell 					child |=
52991eaf3e1SJohn Birrell 					    CTF_TYPE_ISCHILD(lmp->ctlm_type);
53091eaf3e1SJohn Birrell 			}
53191eaf3e1SJohn Birrell 			break;
53291eaf3e1SJohn Birrell 		case CTF_K_ENUM:
53391eaf3e1SJohn Birrell 			vbytes = sizeof (ctf_enum_t) * vlen;
53491eaf3e1SJohn Birrell 			break;
53591eaf3e1SJohn Birrell 		case CTF_K_FORWARD:
53691eaf3e1SJohn Birrell 			/*
53791eaf3e1SJohn Birrell 			 * For forward declarations, ctt_type is the CTF_K_*
53891eaf3e1SJohn Birrell 			 * kind for the tag, so bump that population count too.
53991eaf3e1SJohn Birrell 			 * If ctt_type is unknown, treat the tag as a struct.
54091eaf3e1SJohn Birrell 			 */
54191eaf3e1SJohn Birrell 			if (tp->ctt_type == CTF_K_UNKNOWN ||
54291eaf3e1SJohn Birrell 			    tp->ctt_type >= CTF_K_MAX)
54391eaf3e1SJohn Birrell 				pop[CTF_K_STRUCT]++;
54491eaf3e1SJohn Birrell 			else
54591eaf3e1SJohn Birrell 				pop[tp->ctt_type]++;
54691eaf3e1SJohn Birrell 			/*FALLTHRU*/
54791eaf3e1SJohn Birrell 		case CTF_K_UNKNOWN:
54891eaf3e1SJohn Birrell 			vbytes = 0;
54991eaf3e1SJohn Birrell 			break;
55091eaf3e1SJohn Birrell 		case CTF_K_POINTER:
55191eaf3e1SJohn Birrell 		case CTF_K_TYPEDEF:
55291eaf3e1SJohn Birrell 		case CTF_K_VOLATILE:
55391eaf3e1SJohn Birrell 		case CTF_K_CONST:
55491eaf3e1SJohn Birrell 		case CTF_K_RESTRICT:
55591eaf3e1SJohn Birrell 			child |= CTF_TYPE_ISCHILD(tp->ctt_type);
55691eaf3e1SJohn Birrell 			vbytes = 0;
55791eaf3e1SJohn Birrell 			break;
55891eaf3e1SJohn Birrell 		default:
55991eaf3e1SJohn Birrell 			printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind);
56091eaf3e1SJohn Birrell 			return (EIO);
56191eaf3e1SJohn Birrell 		}
56291eaf3e1SJohn Birrell 		tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
56391eaf3e1SJohn Birrell 		pop[kind]++;
56491eaf3e1SJohn Birrell 	}
56591eaf3e1SJohn Birrell 
566a47016e9SAndriy Gapon 	/* account for a sentinel value below */
567a47016e9SAndriy Gapon 	ctf_typemax++;
56891eaf3e1SJohn Birrell 	*lc->typlenp = ctf_typemax;
56991eaf3e1SJohn Birrell 
570d258fd1dSMark Johnston 	xp = malloc(sizeof(uint32_t) * ctf_typemax, M_LINKER,
571d258fd1dSMark Johnston 	    M_ZERO | M_WAITOK);
57291eaf3e1SJohn Birrell 
57391eaf3e1SJohn Birrell 	*lc->typoffp = xp;
57491eaf3e1SJohn Birrell 
57591eaf3e1SJohn Birrell 	/* type id 0 is used as a sentinel value */
57691eaf3e1SJohn Birrell 	*xp++ = 0;
57791eaf3e1SJohn Birrell 
57891eaf3e1SJohn Birrell 	/*
57991eaf3e1SJohn Birrell 	 * In the second pass, fill in the type offset.
58091eaf3e1SJohn Birrell 	 */
58191eaf3e1SJohn Birrell 	for (tp = tbuf; tp < tend; xp++) {
58291eaf3e1SJohn Birrell 		ushort_t kind = CTF_INFO_KIND(tp->ctt_info);
58391eaf3e1SJohn Birrell 		ulong_t vlen = CTF_INFO_VLEN(tp->ctt_info);
58491eaf3e1SJohn Birrell 		ssize_t size, increment;
58591eaf3e1SJohn Birrell 
58691eaf3e1SJohn Birrell 		size_t vbytes;
58791eaf3e1SJohn Birrell 		uint_t n;
58891eaf3e1SJohn Birrell 
58991eaf3e1SJohn Birrell 		(void) fbt_get_ctt_size(hp->cth_version, tp, &size, &increment);
59091eaf3e1SJohn Birrell 
59191eaf3e1SJohn Birrell 		switch (kind) {
59291eaf3e1SJohn Birrell 		case CTF_K_INTEGER:
59391eaf3e1SJohn Birrell 		case CTF_K_FLOAT:
59491eaf3e1SJohn Birrell 			vbytes = sizeof (uint_t);
59591eaf3e1SJohn Birrell 			break;
59691eaf3e1SJohn Birrell 		case CTF_K_ARRAY:
59791eaf3e1SJohn Birrell 			vbytes = sizeof (ctf_array_t);
59891eaf3e1SJohn Birrell 			break;
59991eaf3e1SJohn Birrell 		case CTF_K_FUNCTION:
60091eaf3e1SJohn Birrell 			vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
60191eaf3e1SJohn Birrell 			break;
60291eaf3e1SJohn Birrell 		case CTF_K_STRUCT:
60391eaf3e1SJohn Birrell 		case CTF_K_UNION:
60491eaf3e1SJohn Birrell 			if (size < CTF_LSTRUCT_THRESH) {
60591eaf3e1SJohn Birrell 				ctf_member_t *mp = (ctf_member_t *)
60691eaf3e1SJohn Birrell 				    ((uintptr_t)tp + increment);
60791eaf3e1SJohn Birrell 
60891eaf3e1SJohn Birrell 				vbytes = sizeof (ctf_member_t) * vlen;
60991eaf3e1SJohn Birrell 				for (n = vlen; n != 0; n--, mp++)
61091eaf3e1SJohn Birrell 					child |= CTF_TYPE_ISCHILD(mp->ctm_type);
61191eaf3e1SJohn Birrell 			} else {
61291eaf3e1SJohn Birrell 				ctf_lmember_t *lmp = (ctf_lmember_t *)
61391eaf3e1SJohn Birrell 				    ((uintptr_t)tp + increment);
61491eaf3e1SJohn Birrell 
61591eaf3e1SJohn Birrell 				vbytes = sizeof (ctf_lmember_t) * vlen;
61691eaf3e1SJohn Birrell 				for (n = vlen; n != 0; n--, lmp++)
61791eaf3e1SJohn Birrell 					child |=
61891eaf3e1SJohn Birrell 					    CTF_TYPE_ISCHILD(lmp->ctlm_type);
61991eaf3e1SJohn Birrell 			}
62091eaf3e1SJohn Birrell 			break;
62191eaf3e1SJohn Birrell 		case CTF_K_ENUM:
62291eaf3e1SJohn Birrell 			vbytes = sizeof (ctf_enum_t) * vlen;
62391eaf3e1SJohn Birrell 			break;
62491eaf3e1SJohn Birrell 		case CTF_K_FORWARD:
62591eaf3e1SJohn Birrell 		case CTF_K_UNKNOWN:
62691eaf3e1SJohn Birrell 			vbytes = 0;
62791eaf3e1SJohn Birrell 			break;
62891eaf3e1SJohn Birrell 		case CTF_K_POINTER:
62991eaf3e1SJohn Birrell 		case CTF_K_TYPEDEF:
63091eaf3e1SJohn Birrell 		case CTF_K_VOLATILE:
63191eaf3e1SJohn Birrell 		case CTF_K_CONST:
63291eaf3e1SJohn Birrell 		case CTF_K_RESTRICT:
63391eaf3e1SJohn Birrell 			vbytes = 0;
63491eaf3e1SJohn Birrell 			break;
63591eaf3e1SJohn Birrell 		default:
63691eaf3e1SJohn Birrell 			printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind);
63791eaf3e1SJohn Birrell 			return (EIO);
63891eaf3e1SJohn Birrell 		}
63991eaf3e1SJohn Birrell 		*xp = (uint32_t)((uintptr_t) tp - (uintptr_t) ctfdata);
64091eaf3e1SJohn Birrell 		tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
64191eaf3e1SJohn Birrell 	}
64291eaf3e1SJohn Birrell 
64391eaf3e1SJohn Birrell 	return (0);
64491eaf3e1SJohn Birrell }
64591eaf3e1SJohn Birrell 
64691eaf3e1SJohn Birrell /*
64791eaf3e1SJohn Birrell  * CTF Declaration Stack
64891eaf3e1SJohn Birrell  *
64991eaf3e1SJohn Birrell  * In order to implement ctf_type_name(), we must convert a type graph back
65091eaf3e1SJohn Birrell  * into a C type declaration.  Unfortunately, a type graph represents a storage
65191eaf3e1SJohn Birrell  * class ordering of the type whereas a type declaration must obey the C rules
65291eaf3e1SJohn Birrell  * for operator precedence, and the two orderings are frequently in conflict.
65391eaf3e1SJohn Birrell  * For example, consider these CTF type graphs and their C declarations:
65491eaf3e1SJohn Birrell  *
65591eaf3e1SJohn Birrell  * CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER  : int (*)()
65691eaf3e1SJohn Birrell  * CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER     : int (*)[]
65791eaf3e1SJohn Birrell  *
65891eaf3e1SJohn Birrell  * In each case, parentheses are used to raise operator * to higher lexical
65991eaf3e1SJohn Birrell  * precedence, so the string form of the C declaration cannot be constructed by
66091eaf3e1SJohn Birrell  * walking the type graph links and forming the string from left to right.
66191eaf3e1SJohn Birrell  *
66291eaf3e1SJohn Birrell  * The functions in this file build a set of stacks from the type graph nodes
66391eaf3e1SJohn Birrell  * corresponding to the C operator precedence levels in the appropriate order.
66491eaf3e1SJohn Birrell  * The code in ctf_type_name() can then iterate over the levels and nodes in
66591eaf3e1SJohn Birrell  * lexical precedence order and construct the final C declaration string.
66691eaf3e1SJohn Birrell  */
66791eaf3e1SJohn Birrell typedef struct ctf_list {
66891eaf3e1SJohn Birrell 	struct ctf_list *l_prev; /* previous pointer or tail pointer */
66991eaf3e1SJohn Birrell 	struct ctf_list *l_next; /* next pointer or head pointer */
67091eaf3e1SJohn Birrell } ctf_list_t;
67191eaf3e1SJohn Birrell 
67291eaf3e1SJohn Birrell #define	ctf_list_prev(elem)	((void *)(((ctf_list_t *)(elem))->l_prev))
67391eaf3e1SJohn Birrell #define	ctf_list_next(elem)	((void *)(((ctf_list_t *)(elem))->l_next))
67491eaf3e1SJohn Birrell 
67591eaf3e1SJohn Birrell typedef enum {
67691eaf3e1SJohn Birrell 	CTF_PREC_BASE,
67791eaf3e1SJohn Birrell 	CTF_PREC_POINTER,
67891eaf3e1SJohn Birrell 	CTF_PREC_ARRAY,
67991eaf3e1SJohn Birrell 	CTF_PREC_FUNCTION,
68091eaf3e1SJohn Birrell 	CTF_PREC_MAX
68191eaf3e1SJohn Birrell } ctf_decl_prec_t;
68291eaf3e1SJohn Birrell 
68391eaf3e1SJohn Birrell typedef struct ctf_decl_node {
68491eaf3e1SJohn Birrell 	ctf_list_t cd_list;			/* linked list pointers */
68591eaf3e1SJohn Birrell 	ctf_id_t cd_type;			/* type identifier */
68691eaf3e1SJohn Birrell 	uint_t cd_kind;				/* type kind */
68791eaf3e1SJohn Birrell 	uint_t cd_n;				/* type dimension if array */
68891eaf3e1SJohn Birrell } ctf_decl_node_t;
68991eaf3e1SJohn Birrell 
69091eaf3e1SJohn Birrell typedef struct ctf_decl {
69191eaf3e1SJohn Birrell 	ctf_list_t cd_nodes[CTF_PREC_MAX];	/* declaration node stacks */
69291eaf3e1SJohn Birrell 	int cd_order[CTF_PREC_MAX];		/* storage order of decls */
69391eaf3e1SJohn Birrell 	ctf_decl_prec_t cd_qualp;		/* qualifier precision */
69491eaf3e1SJohn Birrell 	ctf_decl_prec_t cd_ordp;		/* ordered precision */
69591eaf3e1SJohn Birrell 	char *cd_buf;				/* buffer for output */
69691eaf3e1SJohn Birrell 	char *cd_ptr;				/* buffer location */
69791eaf3e1SJohn Birrell 	char *cd_end;				/* buffer limit */
69891eaf3e1SJohn Birrell 	size_t cd_len;				/* buffer space required */
69991eaf3e1SJohn Birrell 	int cd_err;				/* saved error value */
70091eaf3e1SJohn Birrell } ctf_decl_t;
70191eaf3e1SJohn Birrell 
70291eaf3e1SJohn Birrell /*
70391eaf3e1SJohn Birrell  * Simple doubly-linked list append routine.  This implementation assumes that
70491eaf3e1SJohn Birrell  * each list element contains an embedded ctf_list_t as the first member.
70591eaf3e1SJohn Birrell  * An additional ctf_list_t is used to store the head (l_next) and tail
70691eaf3e1SJohn Birrell  * (l_prev) pointers.  The current head and tail list elements have their
70791eaf3e1SJohn Birrell  * previous and next pointers set to NULL, respectively.
70891eaf3e1SJohn Birrell  */
70991eaf3e1SJohn Birrell static void
71091eaf3e1SJohn Birrell ctf_list_append(ctf_list_t *lp, void *new)
71191eaf3e1SJohn Birrell {
71291eaf3e1SJohn Birrell 	ctf_list_t *p = lp->l_prev;	/* p = tail list element */
71391eaf3e1SJohn Birrell 	ctf_list_t *q = new;		/* q = new list element */
71491eaf3e1SJohn Birrell 
71591eaf3e1SJohn Birrell 	lp->l_prev = q;
71691eaf3e1SJohn Birrell 	q->l_prev = p;
71791eaf3e1SJohn Birrell 	q->l_next = NULL;
71891eaf3e1SJohn Birrell 
71991eaf3e1SJohn Birrell 	if (p != NULL)
72091eaf3e1SJohn Birrell 		p->l_next = q;
72191eaf3e1SJohn Birrell 	else
72291eaf3e1SJohn Birrell 		lp->l_next = q;
72391eaf3e1SJohn Birrell }
72491eaf3e1SJohn Birrell 
72591eaf3e1SJohn Birrell /*
72691eaf3e1SJohn Birrell  * Prepend the specified existing element to the given ctf_list_t.  The
72791eaf3e1SJohn Birrell  * existing pointer should be pointing at a struct with embedded ctf_list_t.
72891eaf3e1SJohn Birrell  */
72991eaf3e1SJohn Birrell static void
73091eaf3e1SJohn Birrell ctf_list_prepend(ctf_list_t *lp, void *new)
73191eaf3e1SJohn Birrell {
73291eaf3e1SJohn Birrell 	ctf_list_t *p = new;		/* p = new list element */
73391eaf3e1SJohn Birrell 	ctf_list_t *q = lp->l_next;	/* q = head list element */
73491eaf3e1SJohn Birrell 
73591eaf3e1SJohn Birrell 	lp->l_next = p;
73691eaf3e1SJohn Birrell 	p->l_prev = NULL;
73791eaf3e1SJohn Birrell 	p->l_next = q;
73891eaf3e1SJohn Birrell 
73991eaf3e1SJohn Birrell 	if (q != NULL)
74091eaf3e1SJohn Birrell 		q->l_prev = p;
74191eaf3e1SJohn Birrell 	else
74291eaf3e1SJohn Birrell 		lp->l_prev = p;
74391eaf3e1SJohn Birrell }
74491eaf3e1SJohn Birrell 
74591eaf3e1SJohn Birrell static void
74691eaf3e1SJohn Birrell ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len)
74791eaf3e1SJohn Birrell {
74891eaf3e1SJohn Birrell 	int i;
74991eaf3e1SJohn Birrell 
75091eaf3e1SJohn Birrell 	bzero(cd, sizeof (ctf_decl_t));
75191eaf3e1SJohn Birrell 
75291eaf3e1SJohn Birrell 	for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++)
75391eaf3e1SJohn Birrell 		cd->cd_order[i] = CTF_PREC_BASE - 1;
75491eaf3e1SJohn Birrell 
75591eaf3e1SJohn Birrell 	cd->cd_qualp = CTF_PREC_BASE;
75691eaf3e1SJohn Birrell 	cd->cd_ordp = CTF_PREC_BASE;
75791eaf3e1SJohn Birrell 
75891eaf3e1SJohn Birrell 	cd->cd_buf = buf;
75991eaf3e1SJohn Birrell 	cd->cd_ptr = buf;
76091eaf3e1SJohn Birrell 	cd->cd_end = buf + len;
76191eaf3e1SJohn Birrell }
76291eaf3e1SJohn Birrell 
76391eaf3e1SJohn Birrell static void
76491eaf3e1SJohn Birrell ctf_decl_fini(ctf_decl_t *cd)
76591eaf3e1SJohn Birrell {
76691eaf3e1SJohn Birrell 	ctf_decl_node_t *cdp, *ndp;
76791eaf3e1SJohn Birrell 	int i;
76891eaf3e1SJohn Birrell 
76991eaf3e1SJohn Birrell 	for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) {
77091eaf3e1SJohn Birrell 		for (cdp = ctf_list_next(&cd->cd_nodes[i]);
77191eaf3e1SJohn Birrell 		    cdp != NULL; cdp = ndp) {
77291eaf3e1SJohn Birrell 			ndp = ctf_list_next(cdp);
77391eaf3e1SJohn Birrell 			free(cdp, M_FBT);
77491eaf3e1SJohn Birrell 		}
77591eaf3e1SJohn Birrell 	}
77691eaf3e1SJohn Birrell }
77791eaf3e1SJohn Birrell 
77891eaf3e1SJohn Birrell static const ctf_type_t *
77991eaf3e1SJohn Birrell ctf_lookup_by_id(linker_ctf_t *lc, ctf_id_t type)
78091eaf3e1SJohn Birrell {
78191eaf3e1SJohn Birrell 	const ctf_type_t *tp;
78291eaf3e1SJohn Birrell 	uint32_t offset;
78391eaf3e1SJohn Birrell 	uint32_t *typoff = *lc->typoffp;
78491eaf3e1SJohn Birrell 
78591eaf3e1SJohn Birrell 	if (type >= *lc->typlenp) {
78691eaf3e1SJohn Birrell 		printf("%s(%d): type %d exceeds max %ld\n",__func__,__LINE__,(int) type,*lc->typlenp);
78791eaf3e1SJohn Birrell 		return(NULL);
78891eaf3e1SJohn Birrell 	}
78991eaf3e1SJohn Birrell 
79091eaf3e1SJohn Birrell 	/* Check if the type isn't cross-referenced. */
79191eaf3e1SJohn Birrell 	if ((offset = typoff[type]) == 0) {
79291eaf3e1SJohn Birrell 		printf("%s(%d): type %d isn't cross referenced\n",__func__,__LINE__, (int) type);
79391eaf3e1SJohn Birrell 		return(NULL);
79491eaf3e1SJohn Birrell 	}
79591eaf3e1SJohn Birrell 
79691eaf3e1SJohn Birrell 	tp = (const ctf_type_t *)(lc->ctftab + offset + sizeof(ctf_header_t));
79791eaf3e1SJohn Birrell 
79891eaf3e1SJohn Birrell 	return (tp);
79991eaf3e1SJohn Birrell }
80091eaf3e1SJohn Birrell 
80191eaf3e1SJohn Birrell static void
80291eaf3e1SJohn Birrell fbt_array_info(linker_ctf_t *lc, ctf_id_t type, ctf_arinfo_t *arp)
80391eaf3e1SJohn Birrell {
80491eaf3e1SJohn Birrell 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
80591eaf3e1SJohn Birrell 	const ctf_type_t *tp;
80691eaf3e1SJohn Birrell 	const ctf_array_t *ap;
80791eaf3e1SJohn Birrell 	ssize_t increment;
80891eaf3e1SJohn Birrell 
80991eaf3e1SJohn Birrell 	bzero(arp, sizeof(*arp));
81091eaf3e1SJohn Birrell 
81191eaf3e1SJohn Birrell 	if ((tp = ctf_lookup_by_id(lc, type)) == NULL)
81291eaf3e1SJohn Birrell 		return;
81391eaf3e1SJohn Birrell 
81491eaf3e1SJohn Birrell 	if (CTF_INFO_KIND(tp->ctt_info) != CTF_K_ARRAY)
81591eaf3e1SJohn Birrell 		return;
81691eaf3e1SJohn Birrell 
81791eaf3e1SJohn Birrell 	(void) fbt_get_ctt_size(hp->cth_version, tp, NULL, &increment);
81891eaf3e1SJohn Birrell 
81991eaf3e1SJohn Birrell 	ap = (const ctf_array_t *)((uintptr_t)tp + increment);
82091eaf3e1SJohn Birrell 	arp->ctr_contents = ap->cta_contents;
82191eaf3e1SJohn Birrell 	arp->ctr_index = ap->cta_index;
82291eaf3e1SJohn Birrell 	arp->ctr_nelems = ap->cta_nelems;
82391eaf3e1SJohn Birrell }
82491eaf3e1SJohn Birrell 
82591eaf3e1SJohn Birrell static const char *
82691eaf3e1SJohn Birrell ctf_strptr(linker_ctf_t *lc, int name)
82791eaf3e1SJohn Birrell {
82891eaf3e1SJohn Birrell 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;;
82991eaf3e1SJohn Birrell 	const char *strp = "";
83091eaf3e1SJohn Birrell 
83191eaf3e1SJohn Birrell 	if (name < 0 || name >= hp->cth_strlen)
83291eaf3e1SJohn Birrell 		return(strp);
83391eaf3e1SJohn Birrell 
83491eaf3e1SJohn Birrell 	strp = (const char *)(lc->ctftab + hp->cth_stroff + name + sizeof(ctf_header_t));
83591eaf3e1SJohn Birrell 
83691eaf3e1SJohn Birrell 	return (strp);
83791eaf3e1SJohn Birrell }
83891eaf3e1SJohn Birrell 
83991eaf3e1SJohn Birrell static void
84091eaf3e1SJohn Birrell ctf_decl_push(ctf_decl_t *cd, linker_ctf_t *lc, ctf_id_t type)
84191eaf3e1SJohn Birrell {
84291eaf3e1SJohn Birrell 	ctf_decl_node_t *cdp;
84391eaf3e1SJohn Birrell 	ctf_decl_prec_t prec;
84491eaf3e1SJohn Birrell 	uint_t kind, n = 1;
84591eaf3e1SJohn Birrell 	int is_qual = 0;
84691eaf3e1SJohn Birrell 
84791eaf3e1SJohn Birrell 	const ctf_type_t *tp;
84891eaf3e1SJohn Birrell 	ctf_arinfo_t ar;
84991eaf3e1SJohn Birrell 
85091eaf3e1SJohn Birrell 	if ((tp = ctf_lookup_by_id(lc, type)) == NULL) {
85191eaf3e1SJohn Birrell 		cd->cd_err = ENOENT;
85291eaf3e1SJohn Birrell 		return;
85391eaf3e1SJohn Birrell 	}
85491eaf3e1SJohn Birrell 
85591eaf3e1SJohn Birrell 	switch (kind = CTF_INFO_KIND(tp->ctt_info)) {
85691eaf3e1SJohn Birrell 	case CTF_K_ARRAY:
85791eaf3e1SJohn Birrell 		fbt_array_info(lc, type, &ar);
85891eaf3e1SJohn Birrell 		ctf_decl_push(cd, lc, ar.ctr_contents);
85991eaf3e1SJohn Birrell 		n = ar.ctr_nelems;
86091eaf3e1SJohn Birrell 		prec = CTF_PREC_ARRAY;
86191eaf3e1SJohn Birrell 		break;
86291eaf3e1SJohn Birrell 
86391eaf3e1SJohn Birrell 	case CTF_K_TYPEDEF:
86491eaf3e1SJohn Birrell 		if (ctf_strptr(lc, tp->ctt_name)[0] == '\0') {
86591eaf3e1SJohn Birrell 			ctf_decl_push(cd, lc, tp->ctt_type);
86691eaf3e1SJohn Birrell 			return;
86791eaf3e1SJohn Birrell 		}
86891eaf3e1SJohn Birrell 		prec = CTF_PREC_BASE;
86991eaf3e1SJohn Birrell 		break;
87091eaf3e1SJohn Birrell 
87191eaf3e1SJohn Birrell 	case CTF_K_FUNCTION:
87291eaf3e1SJohn Birrell 		ctf_decl_push(cd, lc, tp->ctt_type);
87391eaf3e1SJohn Birrell 		prec = CTF_PREC_FUNCTION;
87491eaf3e1SJohn Birrell 		break;
87591eaf3e1SJohn Birrell 
87691eaf3e1SJohn Birrell 	case CTF_K_POINTER:
87791eaf3e1SJohn Birrell 		ctf_decl_push(cd, lc, tp->ctt_type);
87891eaf3e1SJohn Birrell 		prec = CTF_PREC_POINTER;
87991eaf3e1SJohn Birrell 		break;
88091eaf3e1SJohn Birrell 
88191eaf3e1SJohn Birrell 	case CTF_K_VOLATILE:
88291eaf3e1SJohn Birrell 	case CTF_K_CONST:
88391eaf3e1SJohn Birrell 	case CTF_K_RESTRICT:
88491eaf3e1SJohn Birrell 		ctf_decl_push(cd, lc, tp->ctt_type);
88591eaf3e1SJohn Birrell 		prec = cd->cd_qualp;
88691eaf3e1SJohn Birrell 		is_qual++;
88791eaf3e1SJohn Birrell 		break;
88891eaf3e1SJohn Birrell 
88991eaf3e1SJohn Birrell 	default:
89091eaf3e1SJohn Birrell 		prec = CTF_PREC_BASE;
89191eaf3e1SJohn Birrell 	}
89291eaf3e1SJohn Birrell 
893d258fd1dSMark Johnston 	cdp = malloc(sizeof(*cdp), M_FBT, M_WAITOK);
89491eaf3e1SJohn Birrell 	cdp->cd_type = type;
89591eaf3e1SJohn Birrell 	cdp->cd_kind = kind;
89691eaf3e1SJohn Birrell 	cdp->cd_n = n;
89791eaf3e1SJohn Birrell 
89891eaf3e1SJohn Birrell 	if (ctf_list_next(&cd->cd_nodes[prec]) == NULL)
89991eaf3e1SJohn Birrell 		cd->cd_order[prec] = cd->cd_ordp++;
90091eaf3e1SJohn Birrell 
90191eaf3e1SJohn Birrell 	/*
90291eaf3e1SJohn Birrell 	 * Reset cd_qualp to the highest precedence level that we've seen so
90391eaf3e1SJohn Birrell 	 * far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER).
90491eaf3e1SJohn Birrell 	 */
90591eaf3e1SJohn Birrell 	if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY)
90691eaf3e1SJohn Birrell 		cd->cd_qualp = prec;
90791eaf3e1SJohn Birrell 
90891eaf3e1SJohn Birrell 	/*
90991eaf3e1SJohn Birrell 	 * C array declarators are ordered inside out so prepend them.  Also by
91091eaf3e1SJohn Birrell 	 * convention qualifiers of base types precede the type specifier (e.g.
91191eaf3e1SJohn Birrell 	 * const int vs. int const) even though the two forms are equivalent.
91291eaf3e1SJohn Birrell 	 */
91391eaf3e1SJohn Birrell 	if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE))
91491eaf3e1SJohn Birrell 		ctf_list_prepend(&cd->cd_nodes[prec], cdp);
91591eaf3e1SJohn Birrell 	else
91691eaf3e1SJohn Birrell 		ctf_list_append(&cd->cd_nodes[prec], cdp);
91791eaf3e1SJohn Birrell }
91891eaf3e1SJohn Birrell 
91991eaf3e1SJohn Birrell static void
92091eaf3e1SJohn Birrell ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...)
92191eaf3e1SJohn Birrell {
92291eaf3e1SJohn Birrell 	size_t len = (size_t)(cd->cd_end - cd->cd_ptr);
92391eaf3e1SJohn Birrell 	va_list ap;
92491eaf3e1SJohn Birrell 	size_t n;
92591eaf3e1SJohn Birrell 
92691eaf3e1SJohn Birrell 	va_start(ap, format);
92791eaf3e1SJohn Birrell 	n = vsnprintf(cd->cd_ptr, len, format, ap);
92891eaf3e1SJohn Birrell 	va_end(ap);
92991eaf3e1SJohn Birrell 
93091eaf3e1SJohn Birrell 	cd->cd_ptr += MIN(n, len);
93191eaf3e1SJohn Birrell 	cd->cd_len += n;
93291eaf3e1SJohn Birrell }
93391eaf3e1SJohn Birrell 
93491eaf3e1SJohn Birrell static ssize_t
93591eaf3e1SJohn Birrell fbt_type_name(linker_ctf_t *lc, ctf_id_t type, char *buf, size_t len)
93691eaf3e1SJohn Birrell {
93791eaf3e1SJohn Birrell 	ctf_decl_t cd;
93891eaf3e1SJohn Birrell 	ctf_decl_node_t *cdp;
93991eaf3e1SJohn Birrell 	ctf_decl_prec_t prec, lp, rp;
94091eaf3e1SJohn Birrell 	int ptr, arr;
94191eaf3e1SJohn Birrell 	uint_t k;
94291eaf3e1SJohn Birrell 
94391eaf3e1SJohn Birrell 	if (lc == NULL && type == CTF_ERR)
94491eaf3e1SJohn Birrell 		return (-1); /* simplify caller code by permitting CTF_ERR */
94591eaf3e1SJohn Birrell 
94691eaf3e1SJohn Birrell 	ctf_decl_init(&cd, buf, len);
94791eaf3e1SJohn Birrell 	ctf_decl_push(&cd, lc, type);
94891eaf3e1SJohn Birrell 
94991eaf3e1SJohn Birrell 	if (cd.cd_err != 0) {
95091eaf3e1SJohn Birrell 		ctf_decl_fini(&cd);
95191eaf3e1SJohn Birrell 		return (-1);
95291eaf3e1SJohn Birrell 	}
95391eaf3e1SJohn Birrell 
95491eaf3e1SJohn Birrell 	/*
95591eaf3e1SJohn Birrell 	 * If the type graph's order conflicts with lexical precedence order
95691eaf3e1SJohn Birrell 	 * for pointers or arrays, then we need to surround the declarations at
95791eaf3e1SJohn Birrell 	 * the corresponding lexical precedence with parentheses.  This can
95891eaf3e1SJohn Birrell 	 * result in either a parenthesized pointer (*) as in int (*)() or
95991eaf3e1SJohn Birrell 	 * int (*)[], or in a parenthesized pointer and array as in int (*[])().
96091eaf3e1SJohn Birrell 	 */
96191eaf3e1SJohn Birrell 	ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER;
96291eaf3e1SJohn Birrell 	arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY;
96391eaf3e1SJohn Birrell 
96491eaf3e1SJohn Birrell 	rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1;
96591eaf3e1SJohn Birrell 	lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1;
96691eaf3e1SJohn Birrell 
96791eaf3e1SJohn Birrell 	k = CTF_K_POINTER; /* avoid leading whitespace (see below) */
96891eaf3e1SJohn Birrell 
96991eaf3e1SJohn Birrell 	for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) {
97091eaf3e1SJohn Birrell 		for (cdp = ctf_list_next(&cd.cd_nodes[prec]);
97191eaf3e1SJohn Birrell 		    cdp != NULL; cdp = ctf_list_next(cdp)) {
97291eaf3e1SJohn Birrell 
97391eaf3e1SJohn Birrell 			const ctf_type_t *tp =
97491eaf3e1SJohn Birrell 			    ctf_lookup_by_id(lc, cdp->cd_type);
97591eaf3e1SJohn Birrell 			const char *name = ctf_strptr(lc, tp->ctt_name);
97691eaf3e1SJohn Birrell 
97791eaf3e1SJohn Birrell 			if (k != CTF_K_POINTER && k != CTF_K_ARRAY)
97891eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, " ");
97991eaf3e1SJohn Birrell 
98091eaf3e1SJohn Birrell 			if (lp == prec) {
98191eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "(");
98291eaf3e1SJohn Birrell 				lp = -1;
98391eaf3e1SJohn Birrell 			}
98491eaf3e1SJohn Birrell 
98591eaf3e1SJohn Birrell 			switch (cdp->cd_kind) {
98691eaf3e1SJohn Birrell 			case CTF_K_INTEGER:
98791eaf3e1SJohn Birrell 			case CTF_K_FLOAT:
98891eaf3e1SJohn Birrell 			case CTF_K_TYPEDEF:
98991eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "%s", name);
99091eaf3e1SJohn Birrell 				break;
99191eaf3e1SJohn Birrell 			case CTF_K_POINTER:
99291eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "*");
99391eaf3e1SJohn Birrell 				break;
99491eaf3e1SJohn Birrell 			case CTF_K_ARRAY:
99591eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n);
99691eaf3e1SJohn Birrell 				break;
99791eaf3e1SJohn Birrell 			case CTF_K_FUNCTION:
99891eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "()");
99991eaf3e1SJohn Birrell 				break;
100091eaf3e1SJohn Birrell 			case CTF_K_STRUCT:
100191eaf3e1SJohn Birrell 			case CTF_K_FORWARD:
100291eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "struct %s", name);
100391eaf3e1SJohn Birrell 				break;
100491eaf3e1SJohn Birrell 			case CTF_K_UNION:
100591eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "union %s", name);
100691eaf3e1SJohn Birrell 				break;
100791eaf3e1SJohn Birrell 			case CTF_K_ENUM:
100891eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "enum %s", name);
100991eaf3e1SJohn Birrell 				break;
101091eaf3e1SJohn Birrell 			case CTF_K_VOLATILE:
101191eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "volatile");
101291eaf3e1SJohn Birrell 				break;
101391eaf3e1SJohn Birrell 			case CTF_K_CONST:
101491eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "const");
101591eaf3e1SJohn Birrell 				break;
101691eaf3e1SJohn Birrell 			case CTF_K_RESTRICT:
101791eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "restrict");
101891eaf3e1SJohn Birrell 				break;
101991eaf3e1SJohn Birrell 			}
102091eaf3e1SJohn Birrell 
102191eaf3e1SJohn Birrell 			k = cdp->cd_kind;
102291eaf3e1SJohn Birrell 		}
102391eaf3e1SJohn Birrell 
102491eaf3e1SJohn Birrell 		if (rp == prec)
102591eaf3e1SJohn Birrell 			ctf_decl_sprintf(&cd, ")");
102691eaf3e1SJohn Birrell 	}
102791eaf3e1SJohn Birrell 
102891eaf3e1SJohn Birrell 	ctf_decl_fini(&cd);
102991eaf3e1SJohn Birrell 	return (cd.cd_len);
103091eaf3e1SJohn Birrell }
103191eaf3e1SJohn Birrell 
103291eaf3e1SJohn Birrell static void
103391eaf3e1SJohn Birrell fbt_getargdesc(void *arg __unused, dtrace_id_t id __unused, void *parg, dtrace_argdesc_t *desc)
103491eaf3e1SJohn Birrell {
103591eaf3e1SJohn Birrell 	const ushort_t *dp;
103691eaf3e1SJohn Birrell 	fbt_probe_t *fbt = parg;
103791eaf3e1SJohn Birrell 	linker_ctf_t lc;
103891eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
103991eaf3e1SJohn Birrell 	int ndx = desc->dtargd_ndx;
104091eaf3e1SJohn Birrell 	int symindx = fbt->fbtp_symindx;
104191eaf3e1SJohn Birrell 	uint32_t *ctfoff;
104291eaf3e1SJohn Birrell 	uint32_t offset;
104391eaf3e1SJohn Birrell 	ushort_t info, kind, n;
104491eaf3e1SJohn Birrell 
1045aaf2546bSAndriy Gapon 	if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
1046aaf2546bSAndriy Gapon 		(void) strcpy(desc->dtargd_native, "int");
1047aaf2546bSAndriy Gapon 		return;
1048aaf2546bSAndriy Gapon 	}
1049aaf2546bSAndriy Gapon 
105091eaf3e1SJohn Birrell 	desc->dtargd_ndx = DTRACE_ARGNONE;
105191eaf3e1SJohn Birrell 
105291eaf3e1SJohn Birrell 	/* Get a pointer to the CTF data and it's length. */
105391eaf3e1SJohn Birrell 	if (linker_ctf_get(ctl, &lc) != 0)
105491eaf3e1SJohn Birrell 		/* No CTF data? Something wrong? *shrug* */
105591eaf3e1SJohn Birrell 		return;
105691eaf3e1SJohn Birrell 
105791eaf3e1SJohn Birrell 	/* Check if this module hasn't been initialised yet. */
105891eaf3e1SJohn Birrell 	if (*lc.ctfoffp == NULL) {
105991eaf3e1SJohn Birrell 		/*
106091eaf3e1SJohn Birrell 		 * Initialise the CTF object and function symindx to
106191eaf3e1SJohn Birrell 		 * byte offset array.
106291eaf3e1SJohn Birrell 		 */
106391eaf3e1SJohn Birrell 		if (fbt_ctfoff_init(ctl, &lc) != 0)
106491eaf3e1SJohn Birrell 			return;
106591eaf3e1SJohn Birrell 
106691eaf3e1SJohn Birrell 		/* Initialise the CTF type to byte offset array. */
106791eaf3e1SJohn Birrell 		if (fbt_typoff_init(&lc) != 0)
106891eaf3e1SJohn Birrell 			return;
106991eaf3e1SJohn Birrell 	}
107091eaf3e1SJohn Birrell 
107191eaf3e1SJohn Birrell 	ctfoff = *lc.ctfoffp;
107291eaf3e1SJohn Birrell 
107391eaf3e1SJohn Birrell 	if (ctfoff == NULL || *lc.typoffp == NULL)
107491eaf3e1SJohn Birrell 		return;
107591eaf3e1SJohn Birrell 
107691eaf3e1SJohn Birrell 	/* Check if the symbol index is out of range. */
107791eaf3e1SJohn Birrell 	if (symindx >= lc.nsym)
107891eaf3e1SJohn Birrell 		return;
107991eaf3e1SJohn Birrell 
108091eaf3e1SJohn Birrell 	/* Check if the symbol isn't cross-referenced. */
108191eaf3e1SJohn Birrell 	if ((offset = ctfoff[symindx]) == 0xffffffff)
108291eaf3e1SJohn Birrell 		return;
108391eaf3e1SJohn Birrell 
108491eaf3e1SJohn Birrell 	dp = (const ushort_t *)(lc.ctftab + offset + sizeof(ctf_header_t));
108591eaf3e1SJohn Birrell 
108691eaf3e1SJohn Birrell 	info = *dp++;
108791eaf3e1SJohn Birrell 	kind = CTF_INFO_KIND(info);
108891eaf3e1SJohn Birrell 	n = CTF_INFO_VLEN(info);
108991eaf3e1SJohn Birrell 
109091eaf3e1SJohn Birrell 	if (kind == CTF_K_UNKNOWN && n == 0) {
109191eaf3e1SJohn Birrell 		printf("%s(%d): Unknown function!\n",__func__,__LINE__);
109291eaf3e1SJohn Birrell 		return;
109391eaf3e1SJohn Birrell 	}
109491eaf3e1SJohn Birrell 
109591eaf3e1SJohn Birrell 	if (kind != CTF_K_FUNCTION) {
109691eaf3e1SJohn Birrell 		printf("%s(%d): Expected a function!\n",__func__,__LINE__);
109791eaf3e1SJohn Birrell 		return;
109891eaf3e1SJohn Birrell 	}
109991eaf3e1SJohn Birrell 
1100aaf2546bSAndriy Gapon 	if (fbt->fbtp_roffset != 0) {
1101aaf2546bSAndriy Gapon 		/* Only return type is available for args[1] in return probe. */
1102aaf2546bSAndriy Gapon 		if (ndx > 1)
1103aaf2546bSAndriy Gapon 			return;
1104aaf2546bSAndriy Gapon 		ASSERT(ndx == 1);
1105aaf2546bSAndriy Gapon 	} else {
110691eaf3e1SJohn Birrell 		/* Check if the requested argument doesn't exist. */
110791eaf3e1SJohn Birrell 		if (ndx >= n)
110891eaf3e1SJohn Birrell 			return;
110991eaf3e1SJohn Birrell 
111091eaf3e1SJohn Birrell 		/* Skip the return type and arguments up to the one requested. */
111191eaf3e1SJohn Birrell 		dp += ndx + 1;
1112aaf2546bSAndriy Gapon 	}
111391eaf3e1SJohn Birrell 
111491eaf3e1SJohn Birrell 	if (fbt_type_name(&lc, *dp, desc->dtargd_native, sizeof(desc->dtargd_native)) > 0)
111591eaf3e1SJohn Birrell 		desc->dtargd_ndx = ndx;
111691eaf3e1SJohn Birrell 
111791eaf3e1SJohn Birrell 	return;
111891eaf3e1SJohn Birrell }
111991eaf3e1SJohn Birrell 
11208776669bSMark Johnston static int
11218776669bSMark Johnston fbt_linker_file_cb(linker_file_t lf, void *arg)
11228776669bSMark Johnston {
11238776669bSMark Johnston 
11248776669bSMark Johnston 	fbt_provide_module(arg, lf);
11258776669bSMark Johnston 
11268776669bSMark Johnston 	return (0);
11278776669bSMark Johnston }
11288776669bSMark Johnston 
112991eaf3e1SJohn Birrell static void
113091eaf3e1SJohn Birrell fbt_load(void *dummy)
113191eaf3e1SJohn Birrell {
113291eaf3e1SJohn Birrell 	/* Create the /dev/dtrace/fbt entry. */
113391eaf3e1SJohn Birrell 	fbt_cdev = make_dev(&fbt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
113491eaf3e1SJohn Birrell 	    "dtrace/fbt");
113591eaf3e1SJohn Birrell 
113691eaf3e1SJohn Birrell 	/* Default the probe table size if not specified. */
113791eaf3e1SJohn Birrell 	if (fbt_probetab_size == 0)
113891eaf3e1SJohn Birrell 		fbt_probetab_size = FBT_PROBETAB_SIZE;
113991eaf3e1SJohn Birrell 
114091eaf3e1SJohn Birrell 	/* Choose the hash mask for the probe table. */
114191eaf3e1SJohn Birrell 	fbt_probetab_mask = fbt_probetab_size - 1;
114291eaf3e1SJohn Birrell 
114391eaf3e1SJohn Birrell 	/* Allocate memory for the probe table. */
114491eaf3e1SJohn Birrell 	fbt_probetab =
114591eaf3e1SJohn Birrell 	    malloc(fbt_probetab_size * sizeof (fbt_probe_t *), M_FBT, M_WAITOK | M_ZERO);
114691eaf3e1SJohn Birrell 
114791eaf3e1SJohn Birrell 	dtrace_doubletrap_func = fbt_doubletrap;
114891eaf3e1SJohn Birrell 	dtrace_invop_add(fbt_invop);
114991eaf3e1SJohn Birrell 
115091eaf3e1SJohn Birrell 	if (dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_USER,
115191eaf3e1SJohn Birrell 	    NULL, &fbt_pops, NULL, &fbt_id) != 0)
115291eaf3e1SJohn Birrell 		return;
115391eaf3e1SJohn Birrell 
11548776669bSMark Johnston 	/* Create probes for the kernel and already-loaded modules. */
11558776669bSMark Johnston 	linker_file_foreach(fbt_linker_file_cb, NULL);
11568776669bSMark Johnston }
115791eaf3e1SJohn Birrell 
115891eaf3e1SJohn Birrell static int
115991eaf3e1SJohn Birrell fbt_unload()
116091eaf3e1SJohn Birrell {
116191eaf3e1SJohn Birrell 	int error = 0;
116291eaf3e1SJohn Birrell 
116391eaf3e1SJohn Birrell 	/* De-register the invalid opcode handler. */
116491eaf3e1SJohn Birrell 	dtrace_invop_remove(fbt_invop);
116591eaf3e1SJohn Birrell 
116691eaf3e1SJohn Birrell 	dtrace_doubletrap_func = NULL;
116791eaf3e1SJohn Birrell 
116891eaf3e1SJohn Birrell 	/* De-register this DTrace provider. */
116991eaf3e1SJohn Birrell 	if ((error = dtrace_unregister(fbt_id)) != 0)
117091eaf3e1SJohn Birrell 		return (error);
117191eaf3e1SJohn Birrell 
117291eaf3e1SJohn Birrell 	/* Free the probe table. */
117391eaf3e1SJohn Birrell 	free(fbt_probetab, M_FBT);
117491eaf3e1SJohn Birrell 	fbt_probetab = NULL;
117591eaf3e1SJohn Birrell 	fbt_probetab_mask = 0;
117691eaf3e1SJohn Birrell 
117791eaf3e1SJohn Birrell 	destroy_dev(fbt_cdev);
117891eaf3e1SJohn Birrell 
117991eaf3e1SJohn Birrell 	return (error);
118091eaf3e1SJohn Birrell }
118191eaf3e1SJohn Birrell 
118291eaf3e1SJohn Birrell static int
118391eaf3e1SJohn Birrell fbt_modevent(module_t mod __unused, int type, void *data __unused)
118491eaf3e1SJohn Birrell {
118591eaf3e1SJohn Birrell 	int error = 0;
118691eaf3e1SJohn Birrell 
118791eaf3e1SJohn Birrell 	switch (type) {
118891eaf3e1SJohn Birrell 	case MOD_LOAD:
118991eaf3e1SJohn Birrell 		break;
119091eaf3e1SJohn Birrell 
119191eaf3e1SJohn Birrell 	case MOD_UNLOAD:
119291eaf3e1SJohn Birrell 		break;
119391eaf3e1SJohn Birrell 
119491eaf3e1SJohn Birrell 	case MOD_SHUTDOWN:
119591eaf3e1SJohn Birrell 		break;
119691eaf3e1SJohn Birrell 
119791eaf3e1SJohn Birrell 	default:
119891eaf3e1SJohn Birrell 		error = EOPNOTSUPP;
119991eaf3e1SJohn Birrell 		break;
120091eaf3e1SJohn Birrell 
120191eaf3e1SJohn Birrell 	}
120291eaf3e1SJohn Birrell 
120391eaf3e1SJohn Birrell 	return (error);
120491eaf3e1SJohn Birrell }
120591eaf3e1SJohn Birrell 
120691eaf3e1SJohn Birrell static int
120791eaf3e1SJohn Birrell fbt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
120891eaf3e1SJohn Birrell {
120991eaf3e1SJohn Birrell 	return (0);
121091eaf3e1SJohn Birrell }
121191eaf3e1SJohn Birrell 
121291eaf3e1SJohn Birrell SYSINIT(fbt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fbt_load, NULL);
121391eaf3e1SJohn Birrell SYSUNINIT(fbt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fbt_unload, NULL);
121491eaf3e1SJohn Birrell 
121591eaf3e1SJohn Birrell DEV_MODULE(fbt, fbt_modevent, NULL);
121691eaf3e1SJohn Birrell MODULE_VERSION(fbt, 1);
121791eaf3e1SJohn Birrell MODULE_DEPEND(fbt, dtrace, 1, 1, 1);
121891eaf3e1SJohn Birrell MODULE_DEPEND(fbt, opensolaris, 1, 1, 1);
1219