xref: /freebsd/sys/cddl/dev/fbt/fbt.c (revision fdafd315)
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  */
2491eaf3e1SJohn Birrell 
2591eaf3e1SJohn Birrell /*
2691eaf3e1SJohn Birrell  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2791eaf3e1SJohn Birrell  * Use is subject to license terms.
2891eaf3e1SJohn Birrell  */
2991eaf3e1SJohn Birrell 
3091eaf3e1SJohn Birrell #include <sys/param.h>
3191eaf3e1SJohn Birrell #include <sys/systm.h>
3291eaf3e1SJohn Birrell #include <sys/conf.h>
3391eaf3e1SJohn Birrell #include <sys/cpuvar.h>
349e5787d2SMatt Macy #include <sys/endian.h>
3591eaf3e1SJohn Birrell #include <sys/fcntl.h>
3691eaf3e1SJohn Birrell #include <sys/filio.h>
3791eaf3e1SJohn Birrell #include <sys/kdb.h>
3891eaf3e1SJohn Birrell #include <sys/kernel.h>
3991eaf3e1SJohn Birrell #include <sys/kmem.h>
4091eaf3e1SJohn Birrell #include <sys/kthread.h>
4191eaf3e1SJohn Birrell #include <sys/limits.h>
4291eaf3e1SJohn Birrell #include <sys/linker.h>
4391eaf3e1SJohn Birrell #include <sys/lock.h>
4491eaf3e1SJohn Birrell #include <sys/malloc.h>
4591eaf3e1SJohn Birrell #include <sys/module.h>
4691eaf3e1SJohn Birrell #include <sys/mutex.h>
4791eaf3e1SJohn Birrell #include <sys/pcpu.h>
4891eaf3e1SJohn Birrell #include <sys/poll.h>
4991eaf3e1SJohn Birrell #include <sys/proc.h>
5091eaf3e1SJohn Birrell #include <sys/selinfo.h>
5191eaf3e1SJohn Birrell #include <sys/smp.h>
5291eaf3e1SJohn Birrell #include <sys/syscall.h>
5391eaf3e1SJohn Birrell #include <sys/sysent.h>
5491eaf3e1SJohn Birrell #include <sys/sysproto.h>
5591eaf3e1SJohn Birrell #include <sys/uio.h>
5691eaf3e1SJohn Birrell #include <sys/unistd.h>
5791eaf3e1SJohn Birrell #include <machine/stdarg.h>
5891eaf3e1SJohn Birrell 
5991eaf3e1SJohn Birrell #include <sys/dtrace.h>
6091eaf3e1SJohn Birrell #include <sys/dtrace_bsd.h>
6191eaf3e1SJohn Birrell 
62266b4a78SMark Johnston #include "fbt.h"
6391eaf3e1SJohn Birrell 
64266b4a78SMark Johnston MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing");
6591eaf3e1SJohn Birrell 
66266b4a78SMark Johnston dtrace_provider_id_t	fbt_id;
67266b4a78SMark Johnston fbt_probe_t		**fbt_probetab;
68266b4a78SMark Johnston int			fbt_probetab_mask;
6991eaf3e1SJohn Birrell 
7091eaf3e1SJohn Birrell static int	fbt_unload(void);
7191eaf3e1SJohn Birrell static void	fbt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
7291eaf3e1SJohn Birrell static void	fbt_provide_module(void *, modctl_t *);
7391eaf3e1SJohn Birrell static void	fbt_destroy(void *, dtrace_id_t, void *);
7491eaf3e1SJohn Birrell static void	fbt_enable(void *, dtrace_id_t, void *);
7591eaf3e1SJohn Birrell static void	fbt_disable(void *, dtrace_id_t, void *);
7691eaf3e1SJohn Birrell static void	fbt_load(void *);
7791eaf3e1SJohn Birrell static void	fbt_suspend(void *, dtrace_id_t, void *);
7891eaf3e1SJohn Birrell static void	fbt_resume(void *, dtrace_id_t, void *);
7991eaf3e1SJohn Birrell 
8091eaf3e1SJohn Birrell static dtrace_pattr_t fbt_attr = {
8191eaf3e1SJohn Birrell { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
8291eaf3e1SJohn Birrell { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
8391eaf3e1SJohn Birrell { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
8491eaf3e1SJohn Birrell { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
8591eaf3e1SJohn Birrell { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
8691eaf3e1SJohn Birrell };
8791eaf3e1SJohn Birrell 
8891eaf3e1SJohn Birrell static dtrace_pops_t fbt_pops = {
8947f11baaSMark Johnston 	.dtps_provide =		NULL,
9047f11baaSMark Johnston 	.dtps_provide_module =	fbt_provide_module,
9147f11baaSMark Johnston 	.dtps_enable =		fbt_enable,
9247f11baaSMark Johnston 	.dtps_disable =		fbt_disable,
9347f11baaSMark Johnston 	.dtps_suspend =		fbt_suspend,
9447f11baaSMark Johnston 	.dtps_resume =		fbt_resume,
9547f11baaSMark Johnston 	.dtps_getargdesc =	fbt_getargdesc,
9647f11baaSMark Johnston 	.dtps_getargval =	NULL,
9747f11baaSMark Johnston 	.dtps_usermode =	NULL,
9847f11baaSMark Johnston 	.dtps_destroy =		fbt_destroy
9991eaf3e1SJohn Birrell };
10091eaf3e1SJohn Birrell 
10191eaf3e1SJohn Birrell static int			fbt_probetab_size;
10291eaf3e1SJohn Birrell static int			fbt_verbose = 0;
10391eaf3e1SJohn Birrell 
1040ff41755SRuslan Bukin int
fbt_excluded(const char * name)1050ff41755SRuslan Bukin fbt_excluded(const char *name)
1060ff41755SRuslan Bukin {
1070ff41755SRuslan Bukin 
1080ff41755SRuslan Bukin 	if (strncmp(name, "dtrace_", 7) == 0 &&
1090ff41755SRuslan Bukin 	    strncmp(name, "dtrace_safe_", 12) != 0) {
1100ff41755SRuslan Bukin 		/*
1110ff41755SRuslan Bukin 		 * Anything beginning with "dtrace_" may be called
1120ff41755SRuslan Bukin 		 * from probe context unless it explicitly indicates
1130ff41755SRuslan Bukin 		 * that it won't be called from probe context by
1140ff41755SRuslan Bukin 		 * using the prefix "dtrace_safe_".
1150ff41755SRuslan Bukin 		 */
1160ff41755SRuslan Bukin 		return (1);
1170ff41755SRuslan Bukin 	}
1180ff41755SRuslan Bukin 
1190ff41755SRuslan Bukin 	/*
12030b68ecdSRobert Watson 	 * Omit instrumentation of functions that are probably in DDB.  It
12130b68ecdSRobert Watson 	 * makes it too hard to debug broken FBT.
12230b68ecdSRobert Watson 	 *
12330b68ecdSRobert Watson 	 * NB: kdb_enter() can be excluded, but its call to printf() can't be.
12430b68ecdSRobert Watson 	 * This is generally OK since we're not yet in debugging context.
12530b68ecdSRobert Watson 	 */
12630b68ecdSRobert Watson 	if (strncmp(name, "db_", 3) == 0 ||
12730b68ecdSRobert Watson 	    strncmp(name, "kdb_", 4) == 0)
12830b68ecdSRobert Watson 		return (1);
12930b68ecdSRobert Watson 
13030b68ecdSRobert Watson 	/*
131f99a5172SMark Johnston 	 * Lock owner methods may be called from probe context.
132f99a5172SMark Johnston 	 */
133f99a5172SMark Johnston 	if (strcmp(name, "owner_mtx") == 0 ||
134f99a5172SMark Johnston 	    strcmp(name, "owner_rm") == 0 ||
135f99a5172SMark Johnston 	    strcmp(name, "owner_rw") == 0 ||
136f99a5172SMark Johnston 	    strcmp(name, "owner_sx") == 0)
137f99a5172SMark Johnston 		return (1);
138f99a5172SMark Johnston 
139f99a5172SMark Johnston 	/*
1409b9e7f4cSJohn Baldwin 	 * Stack unwinders may be called from probe context on some
1419b9e7f4cSJohn Baldwin 	 * platforms.
1429b9e7f4cSJohn Baldwin 	 */
143ae953968SJohn Baldwin #if defined(__aarch64__) || defined(__riscv)
1449b9e7f4cSJohn Baldwin 	if (strcmp(name, "unwind_frame") == 0)
1459b9e7f4cSJohn Baldwin 		return (1);
1469b9e7f4cSJohn Baldwin #endif
1479b9e7f4cSJohn Baldwin 
1489b9e7f4cSJohn Baldwin 	/*
1490ff41755SRuslan Bukin 	 * When DTrace is built into the kernel we need to exclude
1500ff41755SRuslan Bukin 	 * the FBT functions from instrumentation.
1510ff41755SRuslan Bukin 	 */
1520ff41755SRuslan Bukin #ifndef _KLD_MODULE
1530ff41755SRuslan Bukin 	if (strncmp(name, "fbt_", 4) == 0)
1540ff41755SRuslan Bukin 		return (1);
1550ff41755SRuslan Bukin #endif
1560ff41755SRuslan Bukin 
1570ff41755SRuslan Bukin 	return (0);
1580ff41755SRuslan Bukin }
1590ff41755SRuslan Bukin 
16091eaf3e1SJohn Birrell static void
fbt_doubletrap(void)16191eaf3e1SJohn Birrell fbt_doubletrap(void)
16291eaf3e1SJohn Birrell {
16391eaf3e1SJohn Birrell 	fbt_probe_t *fbt;
16491eaf3e1SJohn Birrell 	int i;
16591eaf3e1SJohn Birrell 
16691eaf3e1SJohn Birrell 	for (i = 0; i < fbt_probetab_size; i++) {
16791eaf3e1SJohn Birrell 		fbt = fbt_probetab[i];
16891eaf3e1SJohn Birrell 
169c208cb99SMark Johnston 		for (; fbt != NULL; fbt = fbt->fbtp_probenext)
17035127d3cSMark Johnston 			fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
17191eaf3e1SJohn Birrell 	}
17291eaf3e1SJohn Birrell }
17391eaf3e1SJohn Birrell 
17491eaf3e1SJohn Birrell static void
fbt_provide_module(void * arg,modctl_t * lf)17591eaf3e1SJohn Birrell fbt_provide_module(void *arg, modctl_t *lf)
17691eaf3e1SJohn Birrell {
17791eaf3e1SJohn Birrell 	char modname[MAXPATHLEN];
17891eaf3e1SJohn Birrell 	int i;
17991eaf3e1SJohn Birrell 	size_t len;
18091eaf3e1SJohn Birrell 
18191eaf3e1SJohn Birrell 	strlcpy(modname, lf->filename, sizeof(modname));
18291eaf3e1SJohn Birrell 	len = strlen(modname);
18391eaf3e1SJohn Birrell 	if (len > 3 && strcmp(modname + len - 3, ".ko") == 0)
18491eaf3e1SJohn Birrell 		modname[len - 3] = '\0';
18591eaf3e1SJohn Birrell 
18691eaf3e1SJohn Birrell 	/*
18791eaf3e1SJohn Birrell 	 * Employees of dtrace and their families are ineligible.  Void
18891eaf3e1SJohn Birrell 	 * where prohibited.
18991eaf3e1SJohn Birrell 	 */
19091eaf3e1SJohn Birrell 	if (strcmp(modname, "dtrace") == 0)
19191eaf3e1SJohn Birrell 		return;
19291eaf3e1SJohn Birrell 
19391eaf3e1SJohn Birrell 	/*
19491eaf3e1SJohn Birrell 	 * To register with DTrace, a module must list 'dtrace' as a
19591eaf3e1SJohn Birrell 	 * dependency in order for the kernel linker to resolve
19691eaf3e1SJohn Birrell 	 * symbols like dtrace_register(). All modules with such a
19791eaf3e1SJohn Birrell 	 * dependency are ineligible for FBT tracing.
19891eaf3e1SJohn Birrell 	 */
19991eaf3e1SJohn Birrell 	for (i = 0; i < lf->ndeps; i++)
20091eaf3e1SJohn Birrell 		if (strncmp(lf->deps[i]->filename, "dtrace", 6) == 0)
20191eaf3e1SJohn Birrell 			return;
20291eaf3e1SJohn Birrell 
20391eaf3e1SJohn Birrell 	if (lf->fbt_nentries) {
20491eaf3e1SJohn Birrell 		/*
20591eaf3e1SJohn Birrell 		 * This module has some FBT entries allocated; we're afraid
20691eaf3e1SJohn Birrell 		 * to screw with it.
20791eaf3e1SJohn Birrell 		 */
20891eaf3e1SJohn Birrell 		return;
20991eaf3e1SJohn Birrell 	}
21091eaf3e1SJohn Birrell 
21191eaf3e1SJohn Birrell 	/*
21291eaf3e1SJohn Birrell 	 * List the functions in the module and the symbol values.
21391eaf3e1SJohn Birrell 	 */
21491eaf3e1SJohn Birrell 	(void) linker_file_function_listall(lf, fbt_provide_module_function, modname);
21591eaf3e1SJohn Birrell }
21691eaf3e1SJohn Birrell 
21791eaf3e1SJohn Birrell static void
fbt_destroy_one(fbt_probe_t * fbt)218c208cb99SMark Johnston fbt_destroy_one(fbt_probe_t *fbt)
219c208cb99SMark Johnston {
220c208cb99SMark Johnston 	fbt_probe_t *hash, *hashprev, *next;
221c208cb99SMark Johnston 	int ndx;
222c208cb99SMark Johnston 
223c208cb99SMark Johnston 	ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
224c208cb99SMark Johnston 	for (hash = fbt_probetab[ndx], hashprev = NULL; hash != NULL;
225a9d49f9eSMark Johnston 	    hashprev = hash, hash = hash->fbtp_hashnext) {
226c208cb99SMark Johnston 		if (hash == fbt) {
227c208cb99SMark Johnston 			if ((next = fbt->fbtp_tracenext) != NULL)
228c208cb99SMark Johnston 				next->fbtp_hashnext = hash->fbtp_hashnext;
229c208cb99SMark Johnston 			else
230c208cb99SMark Johnston 				next = hash->fbtp_hashnext;
231c208cb99SMark Johnston 			if (hashprev != NULL)
232c208cb99SMark Johnston 				hashprev->fbtp_hashnext = next;
233c208cb99SMark Johnston 			else
234c208cb99SMark Johnston 				fbt_probetab[ndx] = next;
235c208cb99SMark Johnston 			goto free;
236c208cb99SMark Johnston 		} else if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
237c208cb99SMark Johnston 			for (next = hash; next->fbtp_tracenext != NULL;
238c208cb99SMark Johnston 			    next = next->fbtp_tracenext) {
239c208cb99SMark Johnston 				if (fbt == next->fbtp_tracenext) {
240c208cb99SMark Johnston 					next->fbtp_tracenext =
241c208cb99SMark Johnston 					    fbt->fbtp_tracenext;
242c208cb99SMark Johnston 					goto free;
243c208cb99SMark Johnston 				}
244c208cb99SMark Johnston 			}
245c208cb99SMark Johnston 		}
246c208cb99SMark Johnston 	}
247c208cb99SMark Johnston 	panic("probe %p not found in hash table", fbt);
248c208cb99SMark Johnston free:
249c208cb99SMark Johnston 	free(fbt, M_FBT);
250c208cb99SMark Johnston }
251c208cb99SMark Johnston 
252c208cb99SMark Johnston static void
fbt_destroy(void * arg,dtrace_id_t id,void * parg)25391eaf3e1SJohn Birrell fbt_destroy(void *arg, dtrace_id_t id, void *parg)
25491eaf3e1SJohn Birrell {
255c208cb99SMark Johnston 	fbt_probe_t *fbt = parg, *next;
25691eaf3e1SJohn Birrell 	modctl_t *ctl;
25791eaf3e1SJohn Birrell 
25891eaf3e1SJohn Birrell 	do {
25991eaf3e1SJohn Birrell 		ctl = fbt->fbtp_ctl;
26091eaf3e1SJohn Birrell 		ctl->fbt_nentries--;
26191eaf3e1SJohn Birrell 
262c208cb99SMark Johnston 		next = fbt->fbtp_probenext;
263c208cb99SMark Johnston 		fbt_destroy_one(fbt);
26491eaf3e1SJohn Birrell 		fbt = next;
26591eaf3e1SJohn Birrell 	} while (fbt != NULL);
26691eaf3e1SJohn Birrell }
26791eaf3e1SJohn Birrell 
26891eaf3e1SJohn Birrell static void
fbt_enable(void * arg,dtrace_id_t id,void * parg)26991eaf3e1SJohn Birrell fbt_enable(void *arg, dtrace_id_t id, void *parg)
27091eaf3e1SJohn Birrell {
27191eaf3e1SJohn Birrell 	fbt_probe_t *fbt = parg;
27291eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
27391eaf3e1SJohn Birrell 
27491eaf3e1SJohn Birrell 	ctl->nenabled++;
27591eaf3e1SJohn Birrell 
27691eaf3e1SJohn Birrell 	/*
27791eaf3e1SJohn Birrell 	 * Now check that our modctl has the expected load count.  If it
27891eaf3e1SJohn Birrell 	 * doesn't, this module must have been unloaded and reloaded -- and
27991eaf3e1SJohn Birrell 	 * we're not going to touch it.
28091eaf3e1SJohn Birrell 	 */
28191eaf3e1SJohn Birrell 	if (ctl->loadcnt != fbt->fbtp_loadcnt) {
28291eaf3e1SJohn Birrell 		if (fbt_verbose) {
28391eaf3e1SJohn Birrell 			printf("fbt is failing for probe %s "
28491eaf3e1SJohn Birrell 			    "(module %s reloaded)",
28591eaf3e1SJohn Birrell 			    fbt->fbtp_name, ctl->filename);
28691eaf3e1SJohn Birrell 		}
28791eaf3e1SJohn Birrell 
28891eaf3e1SJohn Birrell 		return;
28991eaf3e1SJohn Birrell 	}
29091eaf3e1SJohn Birrell 
291c208cb99SMark Johnston 	for (; fbt != NULL; fbt = fbt->fbtp_probenext) {
292266b4a78SMark Johnston 		fbt_patch_tracepoint(fbt, fbt->fbtp_patchval);
293c208cb99SMark Johnston 		fbt->fbtp_enabled++;
294c208cb99SMark Johnston 	}
29591eaf3e1SJohn Birrell }
29691eaf3e1SJohn Birrell 
29791eaf3e1SJohn Birrell static void
fbt_disable(void * arg,dtrace_id_t id,void * parg)29891eaf3e1SJohn Birrell fbt_disable(void *arg, dtrace_id_t id, void *parg)
29991eaf3e1SJohn Birrell {
300c208cb99SMark Johnston 	fbt_probe_t *fbt = parg, *hash;
30191eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
30291eaf3e1SJohn Birrell 
30391eaf3e1SJohn Birrell 	ASSERT(ctl->nenabled > 0);
30491eaf3e1SJohn Birrell 	ctl->nenabled--;
30591eaf3e1SJohn Birrell 
30691eaf3e1SJohn Birrell 	if ((ctl->loadcnt != fbt->fbtp_loadcnt))
30791eaf3e1SJohn Birrell 		return;
30891eaf3e1SJohn Birrell 
309c208cb99SMark Johnston 	for (; fbt != NULL; fbt = fbt->fbtp_probenext) {
310c208cb99SMark Johnston 		fbt->fbtp_enabled--;
311c208cb99SMark Johnston 
312c208cb99SMark Johnston 		for (hash = fbt_probetab[FBT_ADDR2NDX(fbt->fbtp_patchpoint)];
313c208cb99SMark Johnston 		    hash != NULL; hash = hash->fbtp_hashnext) {
314c208cb99SMark Johnston 			if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
315c208cb99SMark Johnston 				for (; hash != NULL; hash = hash->fbtp_tracenext)
316c208cb99SMark Johnston 					if (hash->fbtp_enabled > 0)
317c208cb99SMark Johnston 						break;
318c208cb99SMark Johnston 				break;
319c208cb99SMark Johnston 			}
320c208cb99SMark Johnston 		}
321c208cb99SMark Johnston 		if (hash == NULL)
32235127d3cSMark Johnston 			fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
32391eaf3e1SJohn Birrell 	}
324c208cb99SMark Johnston }
32591eaf3e1SJohn Birrell 
32691eaf3e1SJohn Birrell static void
fbt_suspend(void * arg,dtrace_id_t id,void * parg)32791eaf3e1SJohn Birrell fbt_suspend(void *arg, dtrace_id_t id, void *parg)
32891eaf3e1SJohn Birrell {
32991eaf3e1SJohn Birrell 	fbt_probe_t *fbt = parg;
33091eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
33191eaf3e1SJohn Birrell 
33291eaf3e1SJohn Birrell 	ASSERT(ctl->nenabled > 0);
33391eaf3e1SJohn Birrell 
33491eaf3e1SJohn Birrell 	if ((ctl->loadcnt != fbt->fbtp_loadcnt))
33591eaf3e1SJohn Birrell 		return;
33691eaf3e1SJohn Birrell 
337c208cb99SMark Johnston 	for (; fbt != NULL; fbt = fbt->fbtp_probenext)
33835127d3cSMark Johnston 		fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
33991eaf3e1SJohn Birrell }
34091eaf3e1SJohn Birrell 
34191eaf3e1SJohn Birrell static void
fbt_resume(void * arg,dtrace_id_t id,void * parg)34291eaf3e1SJohn Birrell fbt_resume(void *arg, dtrace_id_t id, void *parg)
34391eaf3e1SJohn Birrell {
34491eaf3e1SJohn Birrell 	fbt_probe_t *fbt = parg;
34591eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
34691eaf3e1SJohn Birrell 
34791eaf3e1SJohn Birrell 	ASSERT(ctl->nenabled > 0);
34891eaf3e1SJohn Birrell 
34991eaf3e1SJohn Birrell 	if ((ctl->loadcnt != fbt->fbtp_loadcnt))
35091eaf3e1SJohn Birrell 		return;
35191eaf3e1SJohn Birrell 
352c208cb99SMark Johnston 	for (; fbt != NULL; fbt = fbt->fbtp_probenext)
353266b4a78SMark Johnston 		fbt_patch_tracepoint(fbt, fbt->fbtp_patchval);
35491eaf3e1SJohn Birrell }
35591eaf3e1SJohn Birrell 
35691eaf3e1SJohn Birrell static int
fbt_ctfoff_init(modctl_t * lf,linker_ctf_t * lc)35791eaf3e1SJohn Birrell fbt_ctfoff_init(modctl_t *lf, linker_ctf_t *lc)
35891eaf3e1SJohn Birrell {
359b1a217a3SEd Maste 	const Elf_Sym *symp = lc->symtab;
36091eaf3e1SJohn Birrell 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
36191eaf3e1SJohn Birrell 	const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
362d9175438SMark Johnston 	size_t idwidth;
36391eaf3e1SJohn Birrell 	int i;
36491eaf3e1SJohn Birrell 	uint32_t *ctfoff;
36591eaf3e1SJohn Birrell 	uint32_t objtoff = hp->cth_objtoff;
36691eaf3e1SJohn Birrell 	uint32_t funcoff = hp->cth_funcoff;
367d9175438SMark Johnston 	uint_t kind, info, vlen;
36891eaf3e1SJohn Birrell 
36991eaf3e1SJohn Birrell 	/* Sanity check. */
37091eaf3e1SJohn Birrell 	if (hp->cth_magic != CTF_MAGIC) {
37191eaf3e1SJohn Birrell 		printf("Bad magic value in CTF data of '%s'\n",lf->pathname);
37291eaf3e1SJohn Birrell 		return (EINVAL);
37391eaf3e1SJohn Birrell 	}
37491eaf3e1SJohn Birrell 
37591eaf3e1SJohn Birrell 	if (lc->symtab == NULL) {
37691eaf3e1SJohn Birrell 		printf("No symbol table in '%s'\n",lf->pathname);
37791eaf3e1SJohn Birrell 		return (EINVAL);
37891eaf3e1SJohn Birrell 	}
37991eaf3e1SJohn Birrell 
380d258fd1dSMark Johnston 	ctfoff = malloc(sizeof(uint32_t) * lc->nsym, M_LINKER, M_WAITOK);
38191eaf3e1SJohn Birrell 	*lc->ctfoffp = ctfoff;
38291eaf3e1SJohn Birrell 
383d9175438SMark Johnston 	idwidth = hp->cth_version == CTF_VERSION_2 ? 2 : 4;
384d9175438SMark Johnston 
38591eaf3e1SJohn Birrell 	for (i = 0; i < lc->nsym; i++, ctfoff++, symp++) {
38691eaf3e1SJohn Birrell 		if (symp->st_name == 0 || symp->st_shndx == SHN_UNDEF) {
38791eaf3e1SJohn Birrell 			*ctfoff = 0xffffffff;
38891eaf3e1SJohn Birrell 			continue;
38991eaf3e1SJohn Birrell 		}
39091eaf3e1SJohn Birrell 
39191eaf3e1SJohn Birrell 		switch (ELF_ST_TYPE(symp->st_info)) {
39291eaf3e1SJohn Birrell 		case STT_OBJECT:
39391eaf3e1SJohn Birrell 			if (objtoff >= hp->cth_funcoff ||
39491eaf3e1SJohn Birrell 			    (symp->st_shndx == SHN_ABS && symp->st_value == 0)) {
39591eaf3e1SJohn Birrell 				*ctfoff = 0xffffffff;
39691eaf3e1SJohn Birrell 				break;
39791eaf3e1SJohn Birrell 			}
39891eaf3e1SJohn Birrell 
39991eaf3e1SJohn Birrell 			*ctfoff = objtoff;
400d9175438SMark Johnston 			objtoff += idwidth;
40191eaf3e1SJohn Birrell 			break;
40291eaf3e1SJohn Birrell 
40391eaf3e1SJohn Birrell 		case STT_FUNC:
40491eaf3e1SJohn Birrell 			if (funcoff >= hp->cth_typeoff) {
40591eaf3e1SJohn Birrell 				*ctfoff = 0xffffffff;
40691eaf3e1SJohn Birrell 				break;
40791eaf3e1SJohn Birrell 			}
40891eaf3e1SJohn Birrell 
40991eaf3e1SJohn Birrell 			*ctfoff = funcoff;
41091eaf3e1SJohn Birrell 
411d9175438SMark Johnston 			info = 0;
412d9175438SMark Johnston 			memcpy(&info, ctfdata + funcoff, idwidth);
413d9175438SMark Johnston 			if (hp->cth_version == CTF_VERSION_2) {
414d9175438SMark Johnston 				kind = CTF_V2_INFO_KIND(info);
415d9175438SMark Johnston 				vlen = CTF_V2_INFO_VLEN(info);
416d9175438SMark Johnston 			} else {
417d9175438SMark Johnston 				kind = CTF_V3_INFO_KIND(info);
418d9175438SMark Johnston 				vlen = CTF_V3_INFO_VLEN(info);
419d9175438SMark Johnston 			}
42091eaf3e1SJohn Birrell 
42191eaf3e1SJohn Birrell 			/*
42291eaf3e1SJohn Birrell 			 * If we encounter a zero pad at the end, just skip it.
42391eaf3e1SJohn Birrell 			 * Otherwise skip over the function and its return type
42491eaf3e1SJohn Birrell 			 * (+2) and the argument list (vlen).
42591eaf3e1SJohn Birrell 			 */
426d9175438SMark Johnston 			if (kind == CTF_K_UNKNOWN && vlen == 0)
427d9175438SMark Johnston 				funcoff += idwidth;
42891eaf3e1SJohn Birrell 			else
429d9175438SMark Johnston 				funcoff += idwidth * (vlen + 2);
43091eaf3e1SJohn Birrell 			break;
43191eaf3e1SJohn Birrell 
43291eaf3e1SJohn Birrell 		default:
43391eaf3e1SJohn Birrell 			*ctfoff = 0xffffffff;
43491eaf3e1SJohn Birrell 			break;
43591eaf3e1SJohn Birrell 		}
43691eaf3e1SJohn Birrell 	}
43791eaf3e1SJohn Birrell 
43891eaf3e1SJohn Birrell 	return (0);
43991eaf3e1SJohn Birrell }
44091eaf3e1SJohn Birrell 
441d9175438SMark Johnston static void
fbt_get_ctt_index(uint8_t version,const void * v,uint_t * indexp,uint_t * typep,int * ischildp)442d9175438SMark Johnston fbt_get_ctt_index(uint8_t version, const void *v, uint_t *indexp,
443d9175438SMark Johnston     uint_t *typep, int *ischildp)
444d9175438SMark Johnston {
445d9175438SMark Johnston 	uint_t index, type;
446d9175438SMark Johnston 	int ischild;
447d9175438SMark Johnston 
448d9175438SMark Johnston 	if (version == CTF_VERSION_2) {
449d9175438SMark Johnston 		const struct ctf_type_v2 *ctt = v;
450d9175438SMark Johnston 
451d9175438SMark Johnston 		type = ctt->ctt_type;
452d9175438SMark Johnston 		index = CTF_V2_TYPE_TO_INDEX(ctt->ctt_type);
453d9175438SMark Johnston 		ischild = CTF_V2_TYPE_ISCHILD(ctt->ctt_type);
454d9175438SMark Johnston 	} else {
455d9175438SMark Johnston 		const struct ctf_type_v3 *ctt = v;
456d9175438SMark Johnston 
457d9175438SMark Johnston 		type = ctt->ctt_type;
458d9175438SMark Johnston 		index = CTF_V3_TYPE_TO_INDEX(ctt->ctt_type);
459d9175438SMark Johnston 		ischild = CTF_V3_TYPE_ISCHILD(ctt->ctt_type);
460d9175438SMark Johnston 	}
461d9175438SMark Johnston 
462d9175438SMark Johnston 	if (indexp != NULL)
463d9175438SMark Johnston 		*indexp = index;
464d9175438SMark Johnston 	if (typep != NULL)
465d9175438SMark Johnston 		*typep = type;
466d9175438SMark Johnston 	if (ischildp != NULL)
467d9175438SMark Johnston 		*ischildp = ischild;
468d9175438SMark Johnston }
469d9175438SMark Johnston 
47091eaf3e1SJohn Birrell static ssize_t
fbt_get_ctt_size(uint8_t version,const void * tp,ssize_t * sizep,ssize_t * incrementp)471d9175438SMark Johnston fbt_get_ctt_size(uint8_t version, const void *tp, ssize_t *sizep,
47291eaf3e1SJohn Birrell     ssize_t *incrementp)
47391eaf3e1SJohn Birrell {
47491eaf3e1SJohn Birrell 	ssize_t size, increment;
47591eaf3e1SJohn Birrell 
476d9175438SMark Johnston 	if (version == CTF_VERSION_2) {
477d9175438SMark Johnston 		const struct ctf_type_v2 *ctt = tp;
478d9175438SMark Johnston 
479d9175438SMark Johnston 		if (ctt->ctt_size == CTF_V2_LSIZE_SENT) {
480d9175438SMark Johnston 			size = CTF_TYPE_LSIZE(ctt);
481d9175438SMark Johnston 			increment = sizeof (struct ctf_type_v2);
48291eaf3e1SJohn Birrell 		} else {
483d9175438SMark Johnston 			size = ctt->ctt_size;
484d9175438SMark Johnston 			increment = sizeof (struct ctf_stype_v2);
485d9175438SMark Johnston 		}
486d9175438SMark Johnston 	} else {
487d9175438SMark Johnston 		const struct ctf_type_v3 *ctt = tp;
488d9175438SMark Johnston 
489d9175438SMark Johnston 		if (ctt->ctt_size == CTF_V3_LSIZE_SENT) {
490d9175438SMark Johnston 			size = CTF_TYPE_LSIZE(ctt);
491d9175438SMark Johnston 			increment = sizeof (struct ctf_type_v3);
492d9175438SMark Johnston 		} else {
493d9175438SMark Johnston 			size = ctt->ctt_size;
494d9175438SMark Johnston 			increment = sizeof (struct ctf_stype_v3);
495d9175438SMark Johnston 		}
49691eaf3e1SJohn Birrell 	}
49791eaf3e1SJohn Birrell 
49891eaf3e1SJohn Birrell 	if (sizep)
49991eaf3e1SJohn Birrell 		*sizep = size;
50091eaf3e1SJohn Birrell 	if (incrementp)
50191eaf3e1SJohn Birrell 		*incrementp = increment;
50291eaf3e1SJohn Birrell 
50391eaf3e1SJohn Birrell 	return (size);
50491eaf3e1SJohn Birrell }
50591eaf3e1SJohn Birrell 
506d9175438SMark Johnston static void
fbt_get_ctt_info(uint8_t version,const void * tp,uint_t * kindp,uint_t * vlenp,int * isrootp)507d9175438SMark Johnston fbt_get_ctt_info(uint8_t version, const void *tp, uint_t *kindp, uint_t *vlenp,
508d9175438SMark Johnston     int *isrootp)
509d9175438SMark Johnston {
510d9175438SMark Johnston 	uint_t kind, vlen;
511d9175438SMark Johnston 	int isroot;
512d9175438SMark Johnston 
513d9175438SMark Johnston 	if (version == CTF_VERSION_2) {
514d9175438SMark Johnston 		const struct ctf_type_v2 *ctt = tp;
515d9175438SMark Johnston 
516d9175438SMark Johnston 		kind = CTF_V2_INFO_KIND(ctt->ctt_info);
517d9175438SMark Johnston 		vlen = CTF_V2_INFO_VLEN(ctt->ctt_info);
518d9175438SMark Johnston 		isroot = CTF_V2_INFO_ISROOT(ctt->ctt_info);
519d9175438SMark Johnston 	} else {
520d9175438SMark Johnston 		const struct ctf_type_v3 *ctt = tp;
521d9175438SMark Johnston 
522d9175438SMark Johnston 		kind = CTF_V3_INFO_KIND(ctt->ctt_info);
523d9175438SMark Johnston 		vlen = CTF_V3_INFO_VLEN(ctt->ctt_info);
524d9175438SMark Johnston 		isroot = CTF_V3_INFO_ISROOT(ctt->ctt_info);
525d9175438SMark Johnston 	}
526d9175438SMark Johnston 
527d9175438SMark Johnston 	if (kindp != NULL)
528d9175438SMark Johnston 		*kindp = kind;
529d9175438SMark Johnston 	if (vlenp != NULL)
530d9175438SMark Johnston 		*vlenp = vlen;
531d9175438SMark Johnston 	if (isrootp != NULL)
532d9175438SMark Johnston 		*isrootp = isroot;
533d9175438SMark Johnston }
534d9175438SMark Johnston 
53591eaf3e1SJohn Birrell static int
fbt_typoff_init(linker_ctf_t * lc)53691eaf3e1SJohn Birrell fbt_typoff_init(linker_ctf_t *lc)
53791eaf3e1SJohn Birrell {
53891eaf3e1SJohn Birrell 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
539d9175438SMark Johnston 	const void *tbuf, *tend, *tp;
54091eaf3e1SJohn Birrell 	const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
541d9175438SMark Johnston 	size_t idwidth;
54291eaf3e1SJohn Birrell 	int ctf_typemax = 0;
54391eaf3e1SJohn Birrell 	uint32_t *xp;
54491eaf3e1SJohn Birrell 	ulong_t pop[CTF_K_MAX + 1] = { 0 };
5454d221f59SMark Johnston 	uint8_t version;
54691eaf3e1SJohn Birrell 
54791eaf3e1SJohn Birrell 	/* Sanity check. */
54891eaf3e1SJohn Birrell 	if (hp->cth_magic != CTF_MAGIC)
54991eaf3e1SJohn Birrell 		return (EINVAL);
55091eaf3e1SJohn Birrell 
5514d221f59SMark Johnston 	version = hp->cth_version;
552d9175438SMark Johnston 	idwidth = version == CTF_VERSION_2 ? 2 : 4;
5534d221f59SMark Johnston 
554d9175438SMark Johnston 	tbuf = (const void *) (ctfdata + hp->cth_typeoff);
555d9175438SMark Johnston 	tend = (const void *) (ctfdata + hp->cth_stroff);
55691eaf3e1SJohn Birrell 
55791eaf3e1SJohn Birrell 	/*
55891eaf3e1SJohn Birrell 	 * We make two passes through the entire type section.  In this first
55991eaf3e1SJohn Birrell 	 * pass, we count the number of each type and the total number of types.
56091eaf3e1SJohn Birrell 	 */
56191eaf3e1SJohn Birrell 	for (tp = tbuf; tp < tend; ctf_typemax++) {
562d9175438SMark Johnston 		uint_t kind, type, vlen;
56391eaf3e1SJohn Birrell 		ssize_t size, increment;
56491eaf3e1SJohn Birrell 		size_t vbytes;
56591eaf3e1SJohn Birrell 
5664d221f59SMark Johnston 		(void) fbt_get_ctt_size(version, tp, &size, &increment);
567d9175438SMark Johnston 		fbt_get_ctt_info(version, tp, &kind, &vlen, NULL);
568d9175438SMark Johnston 		fbt_get_ctt_index(version, tp, NULL, &type, NULL);
56991eaf3e1SJohn Birrell 
57091eaf3e1SJohn Birrell 		switch (kind) {
57191eaf3e1SJohn Birrell 		case CTF_K_INTEGER:
57291eaf3e1SJohn Birrell 		case CTF_K_FLOAT:
57391eaf3e1SJohn Birrell 			vbytes = sizeof (uint_t);
57491eaf3e1SJohn Birrell 			break;
57591eaf3e1SJohn Birrell 		case CTF_K_ARRAY:
576d9175438SMark Johnston 			if (version == CTF_VERSION_2)
577d9175438SMark Johnston 				vbytes = sizeof (struct ctf_array_v2);
578d9175438SMark Johnston 			else
579d9175438SMark Johnston 				vbytes = sizeof (struct ctf_array_v3);
58091eaf3e1SJohn Birrell 			break;
58191eaf3e1SJohn Birrell 		case CTF_K_FUNCTION:
582d9175438SMark Johnston 			vbytes = roundup2(idwidth * vlen, sizeof(uint32_t));
58391eaf3e1SJohn Birrell 			break;
58491eaf3e1SJohn Birrell 		case CTF_K_STRUCT:
58591eaf3e1SJohn Birrell 		case CTF_K_UNION:
586d9175438SMark Johnston 			if (version == CTF_VERSION_2) {
587d9175438SMark Johnston 				if (size < CTF_V2_LSTRUCT_THRESH)
588d9175438SMark Johnston 					vbytes =
589d9175438SMark Johnston 					    sizeof (struct ctf_member_v2) * vlen;
5904d221f59SMark Johnston 				else
591d9175438SMark Johnston 					vbytes =
592d9175438SMark Johnston 					    sizeof (struct ctf_lmember_v2) * vlen;
593d9175438SMark Johnston 			} else {
594d9175438SMark Johnston 				if (size < CTF_V3_LSTRUCT_THRESH)
595d9175438SMark Johnston 					vbytes =
596d9175438SMark Johnston 					    sizeof (struct ctf_member_v3) * vlen;
597d9175438SMark Johnston 				else
598d9175438SMark Johnston 					vbytes =
599d9175438SMark Johnston 					    sizeof (struct ctf_lmember_v3) * vlen;
600d9175438SMark Johnston 			}
60191eaf3e1SJohn Birrell 			break;
60291eaf3e1SJohn Birrell 		case CTF_K_ENUM:
60391eaf3e1SJohn Birrell 			vbytes = sizeof (ctf_enum_t) * vlen;
60491eaf3e1SJohn Birrell 			break;
60591eaf3e1SJohn Birrell 		case CTF_K_FORWARD:
60691eaf3e1SJohn Birrell 			/*
60791eaf3e1SJohn Birrell 			 * For forward declarations, ctt_type is the CTF_K_*
60891eaf3e1SJohn Birrell 			 * kind for the tag, so bump that population count too.
60991eaf3e1SJohn Birrell 			 * If ctt_type is unknown, treat the tag as a struct.
61091eaf3e1SJohn Birrell 			 */
611d9175438SMark Johnston 			if (type == CTF_K_UNKNOWN || type >= CTF_K_MAX)
61291eaf3e1SJohn Birrell 				pop[CTF_K_STRUCT]++;
61391eaf3e1SJohn Birrell 			else
614d9175438SMark Johnston 				pop[type]++;
61591eaf3e1SJohn Birrell 			/*FALLTHRU*/
61691eaf3e1SJohn Birrell 		case CTF_K_UNKNOWN:
61791eaf3e1SJohn Birrell 			vbytes = 0;
61891eaf3e1SJohn Birrell 			break;
61991eaf3e1SJohn Birrell 		case CTF_K_POINTER:
62091eaf3e1SJohn Birrell 		case CTF_K_TYPEDEF:
62191eaf3e1SJohn Birrell 		case CTF_K_VOLATILE:
62291eaf3e1SJohn Birrell 		case CTF_K_CONST:
62391eaf3e1SJohn Birrell 		case CTF_K_RESTRICT:
62491eaf3e1SJohn Birrell 			vbytes = 0;
62591eaf3e1SJohn Birrell 			break;
62691eaf3e1SJohn Birrell 		default:
62791eaf3e1SJohn Birrell 			printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind);
62891eaf3e1SJohn Birrell 			return (EIO);
62991eaf3e1SJohn Birrell 		}
630d9175438SMark Johnston 		tp = (const void *)((uintptr_t)tp + increment + vbytes);
63191eaf3e1SJohn Birrell 		pop[kind]++;
63291eaf3e1SJohn Birrell 	}
63391eaf3e1SJohn Birrell 
634a47016e9SAndriy Gapon 	/* account for a sentinel value below */
635a47016e9SAndriy Gapon 	ctf_typemax++;
63691eaf3e1SJohn Birrell 	*lc->typlenp = ctf_typemax;
63791eaf3e1SJohn Birrell 
638d258fd1dSMark Johnston 	xp = malloc(sizeof(uint32_t) * ctf_typemax, M_LINKER,
639d258fd1dSMark Johnston 	    M_ZERO | M_WAITOK);
64091eaf3e1SJohn Birrell 
64191eaf3e1SJohn Birrell 	*lc->typoffp = xp;
64291eaf3e1SJohn Birrell 
64391eaf3e1SJohn Birrell 	/* type id 0 is used as a sentinel value */
64491eaf3e1SJohn Birrell 	*xp++ = 0;
64591eaf3e1SJohn Birrell 
64691eaf3e1SJohn Birrell 	/*
64791eaf3e1SJohn Birrell 	 * In the second pass, fill in the type offset.
64891eaf3e1SJohn Birrell 	 */
64991eaf3e1SJohn Birrell 	for (tp = tbuf; tp < tend; xp++) {
65091eaf3e1SJohn Birrell 		ssize_t size, increment;
651d9175438SMark Johnston 		uint_t kind, vlen;
65291eaf3e1SJohn Birrell 
65391eaf3e1SJohn Birrell 		size_t vbytes;
65491eaf3e1SJohn Birrell 
6554d221f59SMark Johnston 		(void) fbt_get_ctt_size(version, tp, &size, &increment);
656d9175438SMark Johnston 		fbt_get_ctt_info(version, tp, &kind, &vlen, NULL);
65791eaf3e1SJohn Birrell 
65891eaf3e1SJohn Birrell 		switch (kind) {
65991eaf3e1SJohn Birrell 		case CTF_K_INTEGER:
66091eaf3e1SJohn Birrell 		case CTF_K_FLOAT:
66191eaf3e1SJohn Birrell 			vbytes = sizeof (uint_t);
66291eaf3e1SJohn Birrell 			break;
66391eaf3e1SJohn Birrell 		case CTF_K_ARRAY:
664d9175438SMark Johnston 			if (version == CTF_VERSION_2)
665d9175438SMark Johnston 				vbytes = sizeof (struct ctf_array_v2);
666d9175438SMark Johnston 			else
667d9175438SMark Johnston 				vbytes = sizeof (struct ctf_array_v3);
66891eaf3e1SJohn Birrell 			break;
66991eaf3e1SJohn Birrell 		case CTF_K_FUNCTION:
670d9175438SMark Johnston 			vbytes = roundup2(idwidth * vlen, sizeof(uint32_t));
67191eaf3e1SJohn Birrell 			break;
67291eaf3e1SJohn Birrell 		case CTF_K_STRUCT:
67391eaf3e1SJohn Birrell 		case CTF_K_UNION:
674d9175438SMark Johnston 			if (version == CTF_VERSION_2) {
675d9175438SMark Johnston 				if (size < CTF_V2_LSTRUCT_THRESH)
676d9175438SMark Johnston 					vbytes =
677d9175438SMark Johnston 					    sizeof (struct ctf_member_v2) * vlen;
6784d221f59SMark Johnston 				else
679d9175438SMark Johnston 					vbytes =
680d9175438SMark Johnston 					    sizeof (struct ctf_lmember_v2) * vlen;
681d9175438SMark Johnston 			} else {
682d9175438SMark Johnston 				if (size < CTF_V3_LSTRUCT_THRESH)
683d9175438SMark Johnston 					vbytes =
684d9175438SMark Johnston 					    sizeof (struct ctf_member_v3) * vlen;
685d9175438SMark Johnston 				else
686d9175438SMark Johnston 					vbytes =
687d9175438SMark Johnston 					    sizeof (struct ctf_lmember_v3) * vlen;
688d9175438SMark Johnston 			}
68991eaf3e1SJohn Birrell 			break;
69091eaf3e1SJohn Birrell 		case CTF_K_ENUM:
69191eaf3e1SJohn Birrell 			vbytes = sizeof (ctf_enum_t) * vlen;
69291eaf3e1SJohn Birrell 			break;
69391eaf3e1SJohn Birrell 		case CTF_K_FORWARD:
69491eaf3e1SJohn Birrell 		case CTF_K_UNKNOWN:
69591eaf3e1SJohn Birrell 			vbytes = 0;
69691eaf3e1SJohn Birrell 			break;
69791eaf3e1SJohn Birrell 		case CTF_K_POINTER:
69891eaf3e1SJohn Birrell 		case CTF_K_TYPEDEF:
69991eaf3e1SJohn Birrell 		case CTF_K_VOLATILE:
70091eaf3e1SJohn Birrell 		case CTF_K_CONST:
70191eaf3e1SJohn Birrell 		case CTF_K_RESTRICT:
70291eaf3e1SJohn Birrell 			vbytes = 0;
70391eaf3e1SJohn Birrell 			break;
70491eaf3e1SJohn Birrell 		default:
70591eaf3e1SJohn Birrell 			printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind);
70691eaf3e1SJohn Birrell 			return (EIO);
70791eaf3e1SJohn Birrell 		}
70891eaf3e1SJohn Birrell 		*xp = (uint32_t)((uintptr_t) tp - (uintptr_t) ctfdata);
709d9175438SMark Johnston 		tp = (const void *)((uintptr_t)tp + increment + vbytes);
71091eaf3e1SJohn Birrell 	}
71191eaf3e1SJohn Birrell 
71291eaf3e1SJohn Birrell 	return (0);
71391eaf3e1SJohn Birrell }
71491eaf3e1SJohn Birrell 
71591eaf3e1SJohn Birrell /*
71691eaf3e1SJohn Birrell  * CTF Declaration Stack
71791eaf3e1SJohn Birrell  *
71891eaf3e1SJohn Birrell  * In order to implement ctf_type_name(), we must convert a type graph back
71991eaf3e1SJohn Birrell  * into a C type declaration.  Unfortunately, a type graph represents a storage
72091eaf3e1SJohn Birrell  * class ordering of the type whereas a type declaration must obey the C rules
72191eaf3e1SJohn Birrell  * for operator precedence, and the two orderings are frequently in conflict.
72291eaf3e1SJohn Birrell  * For example, consider these CTF type graphs and their C declarations:
72391eaf3e1SJohn Birrell  *
72491eaf3e1SJohn Birrell  * CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER  : int (*)()
72591eaf3e1SJohn Birrell  * CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER     : int (*)[]
72691eaf3e1SJohn Birrell  *
72791eaf3e1SJohn Birrell  * In each case, parentheses are used to raise operator * to higher lexical
72891eaf3e1SJohn Birrell  * precedence, so the string form of the C declaration cannot be constructed by
72991eaf3e1SJohn Birrell  * walking the type graph links and forming the string from left to right.
73091eaf3e1SJohn Birrell  *
73191eaf3e1SJohn Birrell  * The functions in this file build a set of stacks from the type graph nodes
73291eaf3e1SJohn Birrell  * corresponding to the C operator precedence levels in the appropriate order.
73391eaf3e1SJohn Birrell  * The code in ctf_type_name() can then iterate over the levels and nodes in
73491eaf3e1SJohn Birrell  * lexical precedence order and construct the final C declaration string.
73591eaf3e1SJohn Birrell  */
73691eaf3e1SJohn Birrell typedef struct ctf_list {
73791eaf3e1SJohn Birrell 	struct ctf_list *l_prev; /* previous pointer or tail pointer */
73891eaf3e1SJohn Birrell 	struct ctf_list *l_next; /* next pointer or head pointer */
73991eaf3e1SJohn Birrell } ctf_list_t;
74091eaf3e1SJohn Birrell 
74191eaf3e1SJohn Birrell #define	ctf_list_prev(elem)	((void *)(((ctf_list_t *)(elem))->l_prev))
74291eaf3e1SJohn Birrell #define	ctf_list_next(elem)	((void *)(((ctf_list_t *)(elem))->l_next))
74391eaf3e1SJohn Birrell 
74491eaf3e1SJohn Birrell typedef enum {
74591eaf3e1SJohn Birrell 	CTF_PREC_BASE,
74691eaf3e1SJohn Birrell 	CTF_PREC_POINTER,
74791eaf3e1SJohn Birrell 	CTF_PREC_ARRAY,
74891eaf3e1SJohn Birrell 	CTF_PREC_FUNCTION,
74991eaf3e1SJohn Birrell 	CTF_PREC_MAX
75091eaf3e1SJohn Birrell } ctf_decl_prec_t;
75191eaf3e1SJohn Birrell 
75291eaf3e1SJohn Birrell typedef struct ctf_decl_node {
75391eaf3e1SJohn Birrell 	ctf_list_t cd_list;			/* linked list pointers */
75491eaf3e1SJohn Birrell 	ctf_id_t cd_type;			/* type identifier */
75591eaf3e1SJohn Birrell 	uint_t cd_kind;				/* type kind */
75691eaf3e1SJohn Birrell 	uint_t cd_n;				/* type dimension if array */
75791eaf3e1SJohn Birrell } ctf_decl_node_t;
75891eaf3e1SJohn Birrell 
75991eaf3e1SJohn Birrell typedef struct ctf_decl {
76091eaf3e1SJohn Birrell 	ctf_list_t cd_nodes[CTF_PREC_MAX];	/* declaration node stacks */
76191eaf3e1SJohn Birrell 	int cd_order[CTF_PREC_MAX];		/* storage order of decls */
76291eaf3e1SJohn Birrell 	ctf_decl_prec_t cd_qualp;		/* qualifier precision */
76391eaf3e1SJohn Birrell 	ctf_decl_prec_t cd_ordp;		/* ordered precision */
76491eaf3e1SJohn Birrell 	char *cd_buf;				/* buffer for output */
76591eaf3e1SJohn Birrell 	char *cd_ptr;				/* buffer location */
76691eaf3e1SJohn Birrell 	char *cd_end;				/* buffer limit */
76791eaf3e1SJohn Birrell 	size_t cd_len;				/* buffer space required */
76891eaf3e1SJohn Birrell 	int cd_err;				/* saved error value */
76991eaf3e1SJohn Birrell } ctf_decl_t;
77091eaf3e1SJohn Birrell 
77191eaf3e1SJohn Birrell /*
77291eaf3e1SJohn Birrell  * Simple doubly-linked list append routine.  This implementation assumes that
77391eaf3e1SJohn Birrell  * each list element contains an embedded ctf_list_t as the first member.
77491eaf3e1SJohn Birrell  * An additional ctf_list_t is used to store the head (l_next) and tail
77591eaf3e1SJohn Birrell  * (l_prev) pointers.  The current head and tail list elements have their
77691eaf3e1SJohn Birrell  * previous and next pointers set to NULL, respectively.
77791eaf3e1SJohn Birrell  */
77891eaf3e1SJohn Birrell static void
ctf_list_append(ctf_list_t * lp,void * new)77991eaf3e1SJohn Birrell ctf_list_append(ctf_list_t *lp, void *new)
78091eaf3e1SJohn Birrell {
78191eaf3e1SJohn Birrell 	ctf_list_t *p = lp->l_prev;	/* p = tail list element */
78291eaf3e1SJohn Birrell 	ctf_list_t *q = new;		/* q = new list element */
78391eaf3e1SJohn Birrell 
78491eaf3e1SJohn Birrell 	lp->l_prev = q;
78591eaf3e1SJohn Birrell 	q->l_prev = p;
78691eaf3e1SJohn Birrell 	q->l_next = NULL;
78791eaf3e1SJohn Birrell 
78891eaf3e1SJohn Birrell 	if (p != NULL)
78991eaf3e1SJohn Birrell 		p->l_next = q;
79091eaf3e1SJohn Birrell 	else
79191eaf3e1SJohn Birrell 		lp->l_next = q;
79291eaf3e1SJohn Birrell }
79391eaf3e1SJohn Birrell 
79491eaf3e1SJohn Birrell /*
79591eaf3e1SJohn Birrell  * Prepend the specified existing element to the given ctf_list_t.  The
79691eaf3e1SJohn Birrell  * existing pointer should be pointing at a struct with embedded ctf_list_t.
79791eaf3e1SJohn Birrell  */
79891eaf3e1SJohn Birrell static void
ctf_list_prepend(ctf_list_t * lp,void * new)79991eaf3e1SJohn Birrell ctf_list_prepend(ctf_list_t *lp, void *new)
80091eaf3e1SJohn Birrell {
80191eaf3e1SJohn Birrell 	ctf_list_t *p = new;		/* p = new list element */
80291eaf3e1SJohn Birrell 	ctf_list_t *q = lp->l_next;	/* q = head list element */
80391eaf3e1SJohn Birrell 
80491eaf3e1SJohn Birrell 	lp->l_next = p;
80591eaf3e1SJohn Birrell 	p->l_prev = NULL;
80691eaf3e1SJohn Birrell 	p->l_next = q;
80791eaf3e1SJohn Birrell 
80891eaf3e1SJohn Birrell 	if (q != NULL)
80991eaf3e1SJohn Birrell 		q->l_prev = p;
81091eaf3e1SJohn Birrell 	else
81191eaf3e1SJohn Birrell 		lp->l_prev = p;
81291eaf3e1SJohn Birrell }
81391eaf3e1SJohn Birrell 
81491eaf3e1SJohn Birrell static void
ctf_decl_init(ctf_decl_t * cd,char * buf,size_t len)81591eaf3e1SJohn Birrell ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len)
81691eaf3e1SJohn Birrell {
81791eaf3e1SJohn Birrell 	int i;
81891eaf3e1SJohn Birrell 
81991eaf3e1SJohn Birrell 	bzero(cd, sizeof (ctf_decl_t));
82091eaf3e1SJohn Birrell 
82191eaf3e1SJohn Birrell 	for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++)
82291eaf3e1SJohn Birrell 		cd->cd_order[i] = CTF_PREC_BASE - 1;
82391eaf3e1SJohn Birrell 
82491eaf3e1SJohn Birrell 	cd->cd_qualp = CTF_PREC_BASE;
82591eaf3e1SJohn Birrell 	cd->cd_ordp = CTF_PREC_BASE;
82691eaf3e1SJohn Birrell 
82791eaf3e1SJohn Birrell 	cd->cd_buf = buf;
82891eaf3e1SJohn Birrell 	cd->cd_ptr = buf;
82991eaf3e1SJohn Birrell 	cd->cd_end = buf + len;
83091eaf3e1SJohn Birrell }
83191eaf3e1SJohn Birrell 
83291eaf3e1SJohn Birrell static void
ctf_decl_fini(ctf_decl_t * cd)83391eaf3e1SJohn Birrell ctf_decl_fini(ctf_decl_t *cd)
83491eaf3e1SJohn Birrell {
83591eaf3e1SJohn Birrell 	ctf_decl_node_t *cdp, *ndp;
83691eaf3e1SJohn Birrell 	int i;
83791eaf3e1SJohn Birrell 
83891eaf3e1SJohn Birrell 	for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) {
83991eaf3e1SJohn Birrell 		for (cdp = ctf_list_next(&cd->cd_nodes[i]);
84091eaf3e1SJohn Birrell 		    cdp != NULL; cdp = ndp) {
84191eaf3e1SJohn Birrell 			ndp = ctf_list_next(cdp);
84291eaf3e1SJohn Birrell 			free(cdp, M_FBT);
84391eaf3e1SJohn Birrell 		}
84491eaf3e1SJohn Birrell 	}
84591eaf3e1SJohn Birrell }
84691eaf3e1SJohn Birrell 
847d9175438SMark Johnston static const void *
ctf_lookup_by_id(linker_ctf_t * lc,ctf_id_t type)84891eaf3e1SJohn Birrell ctf_lookup_by_id(linker_ctf_t *lc, ctf_id_t type)
84991eaf3e1SJohn Birrell {
850d9175438SMark Johnston 	const void *tp;
85191eaf3e1SJohn Birrell 	uint32_t offset;
85291eaf3e1SJohn Birrell 	uint32_t *typoff = *lc->typoffp;
85391eaf3e1SJohn Birrell 
85491eaf3e1SJohn Birrell 	if (type >= *lc->typlenp) {
85591eaf3e1SJohn Birrell 		printf("%s(%d): type %d exceeds max %ld\n",__func__,__LINE__,(int) type,*lc->typlenp);
85691eaf3e1SJohn Birrell 		return(NULL);
85791eaf3e1SJohn Birrell 	}
85891eaf3e1SJohn Birrell 
85991eaf3e1SJohn Birrell 	/* Check if the type isn't cross-referenced. */
86091eaf3e1SJohn Birrell 	if ((offset = typoff[type]) == 0) {
86191eaf3e1SJohn Birrell 		printf("%s(%d): type %d isn't cross referenced\n",__func__,__LINE__, (int) type);
86291eaf3e1SJohn Birrell 		return(NULL);
86391eaf3e1SJohn Birrell 	}
86491eaf3e1SJohn Birrell 
865d9175438SMark Johnston 	tp = (const void *) (lc->ctftab + offset + sizeof(ctf_header_t));
86691eaf3e1SJohn Birrell 
86791eaf3e1SJohn Birrell 	return (tp);
86891eaf3e1SJohn Birrell }
86991eaf3e1SJohn Birrell 
87091eaf3e1SJohn Birrell static void
fbt_array_info(linker_ctf_t * lc,ctf_id_t type,ctf_arinfo_t * arp)87191eaf3e1SJohn Birrell fbt_array_info(linker_ctf_t *lc, ctf_id_t type, ctf_arinfo_t *arp)
87291eaf3e1SJohn Birrell {
87391eaf3e1SJohn Birrell 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
874d9175438SMark Johnston 	const void *tp;
87591eaf3e1SJohn Birrell 	ssize_t increment;
876d9175438SMark Johnston 	uint_t kind;
87791eaf3e1SJohn Birrell 
87891eaf3e1SJohn Birrell 	bzero(arp, sizeof(*arp));
87991eaf3e1SJohn Birrell 
88091eaf3e1SJohn Birrell 	if ((tp = ctf_lookup_by_id(lc, type)) == NULL)
88191eaf3e1SJohn Birrell 		return;
88291eaf3e1SJohn Birrell 
883d9175438SMark Johnston 	fbt_get_ctt_info(hp->cth_version, tp, &kind, NULL, NULL);
884d9175438SMark Johnston 	if (kind != CTF_K_ARRAY)
88591eaf3e1SJohn Birrell 		return;
88691eaf3e1SJohn Birrell 
88791eaf3e1SJohn Birrell 	(void) fbt_get_ctt_size(hp->cth_version, tp, NULL, &increment);
88891eaf3e1SJohn Birrell 
889d9175438SMark Johnston 	if (hp->cth_version == CTF_VERSION_2) {
890d9175438SMark Johnston 		const struct ctf_array_v2 *ap;
891d9175438SMark Johnston 
892d9175438SMark Johnston 		ap = (const struct ctf_array_v2 *)((uintptr_t)tp + increment);
89391eaf3e1SJohn Birrell 		arp->ctr_contents = ap->cta_contents;
89491eaf3e1SJohn Birrell 		arp->ctr_index = ap->cta_index;
89591eaf3e1SJohn Birrell 		arp->ctr_nelems = ap->cta_nelems;
896d9175438SMark Johnston 	} else {
897d9175438SMark Johnston 		const struct ctf_array_v3 *ap;
898d9175438SMark Johnston 
899d9175438SMark Johnston 		ap = (const struct ctf_array_v3 *)((uintptr_t)tp + increment);
900d9175438SMark Johnston 		arp->ctr_contents = ap->cta_contents;
901d9175438SMark Johnston 		arp->ctr_index = ap->cta_index;
902d9175438SMark Johnston 		arp->ctr_nelems = ap->cta_nelems;
903d9175438SMark Johnston 	}
90491eaf3e1SJohn Birrell }
90591eaf3e1SJohn Birrell 
90691eaf3e1SJohn Birrell static const char *
ctf_strptr(linker_ctf_t * lc,int name)90791eaf3e1SJohn Birrell ctf_strptr(linker_ctf_t *lc, int name)
90891eaf3e1SJohn Birrell {
909b1a217a3SEd Maste 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
91091eaf3e1SJohn Birrell 	const char *strp = "";
91191eaf3e1SJohn Birrell 
91291eaf3e1SJohn Birrell 	if (name < 0 || name >= hp->cth_strlen)
91391eaf3e1SJohn Birrell 		return(strp);
91491eaf3e1SJohn Birrell 
91591eaf3e1SJohn Birrell 	strp = (const char *)(lc->ctftab + hp->cth_stroff + name + sizeof(ctf_header_t));
91691eaf3e1SJohn Birrell 
91791eaf3e1SJohn Birrell 	return (strp);
91891eaf3e1SJohn Birrell }
91991eaf3e1SJohn Birrell 
920d9175438SMark Johnston static const char *
ctf_type_rname(linker_ctf_t * lc,const void * v)921d9175438SMark Johnston ctf_type_rname(linker_ctf_t *lc, const void *v)
922d9175438SMark Johnston {
923d9175438SMark Johnston 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
924d9175438SMark Johnston 	uint_t name;
925d9175438SMark Johnston 
926d9175438SMark Johnston 	if (hp->cth_version == CTF_VERSION_2) {
927d9175438SMark Johnston 		const struct ctf_type_v2 *ctt = v;
928d9175438SMark Johnston 
929d9175438SMark Johnston 		name = ctt->ctt_name;
930d9175438SMark Johnston 	} else {
931d9175438SMark Johnston 		const struct ctf_type_v3 *ctt = v;
932d9175438SMark Johnston 
933d9175438SMark Johnston 		name = ctt->ctt_name;
934d9175438SMark Johnston 	}
935d9175438SMark Johnston 
936d9175438SMark Johnston 	return (ctf_strptr(lc, name));
937d9175438SMark Johnston }
938d9175438SMark Johnston 
93991eaf3e1SJohn Birrell static void
ctf_decl_push(ctf_decl_t * cd,linker_ctf_t * lc,ctf_id_t type)94091eaf3e1SJohn Birrell ctf_decl_push(ctf_decl_t *cd, linker_ctf_t *lc, ctf_id_t type)
94191eaf3e1SJohn Birrell {
942d9175438SMark Johnston 	const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
94391eaf3e1SJohn Birrell 	ctf_decl_node_t *cdp;
94491eaf3e1SJohn Birrell 	ctf_decl_prec_t prec;
945d9175438SMark Johnston 	uint_t kind, n = 1, t;
94691eaf3e1SJohn Birrell 	int is_qual = 0;
94791eaf3e1SJohn Birrell 
948d9175438SMark Johnston 	const void *tp;
94991eaf3e1SJohn Birrell 	ctf_arinfo_t ar;
95091eaf3e1SJohn Birrell 
95191eaf3e1SJohn Birrell 	if ((tp = ctf_lookup_by_id(lc, type)) == NULL) {
95291eaf3e1SJohn Birrell 		cd->cd_err = ENOENT;
95391eaf3e1SJohn Birrell 		return;
95491eaf3e1SJohn Birrell 	}
95591eaf3e1SJohn Birrell 
956d9175438SMark Johnston 	fbt_get_ctt_info(hp->cth_version, tp, &kind, NULL, NULL);
957d9175438SMark Johnston 	fbt_get_ctt_index(hp->cth_version, tp, NULL, &t, NULL);
958d9175438SMark Johnston 
959d9175438SMark Johnston 	switch (kind) {
96091eaf3e1SJohn Birrell 	case CTF_K_ARRAY:
96191eaf3e1SJohn Birrell 		fbt_array_info(lc, type, &ar);
96291eaf3e1SJohn Birrell 		ctf_decl_push(cd, lc, ar.ctr_contents);
96391eaf3e1SJohn Birrell 		n = ar.ctr_nelems;
96491eaf3e1SJohn Birrell 		prec = CTF_PREC_ARRAY;
96591eaf3e1SJohn Birrell 		break;
96691eaf3e1SJohn Birrell 
96791eaf3e1SJohn Birrell 	case CTF_K_TYPEDEF:
968d9175438SMark Johnston 		if (ctf_type_rname(lc, tp)[0] == '\0') {
969d9175438SMark Johnston 			ctf_decl_push(cd, lc, t);
97091eaf3e1SJohn Birrell 			return;
97191eaf3e1SJohn Birrell 		}
97291eaf3e1SJohn Birrell 		prec = CTF_PREC_BASE;
97391eaf3e1SJohn Birrell 		break;
97491eaf3e1SJohn Birrell 
97591eaf3e1SJohn Birrell 	case CTF_K_FUNCTION:
976d9175438SMark Johnston 		ctf_decl_push(cd, lc, t);
97791eaf3e1SJohn Birrell 		prec = CTF_PREC_FUNCTION;
97891eaf3e1SJohn Birrell 		break;
97991eaf3e1SJohn Birrell 
98091eaf3e1SJohn Birrell 	case CTF_K_POINTER:
981d9175438SMark Johnston 		ctf_decl_push(cd, lc, t);
98291eaf3e1SJohn Birrell 		prec = CTF_PREC_POINTER;
98391eaf3e1SJohn Birrell 		break;
98491eaf3e1SJohn Birrell 
98591eaf3e1SJohn Birrell 	case CTF_K_VOLATILE:
98691eaf3e1SJohn Birrell 	case CTF_K_CONST:
98791eaf3e1SJohn Birrell 	case CTF_K_RESTRICT:
988d9175438SMark Johnston 		ctf_decl_push(cd, lc, t);
98991eaf3e1SJohn Birrell 		prec = cd->cd_qualp;
99091eaf3e1SJohn Birrell 		is_qual++;
99191eaf3e1SJohn Birrell 		break;
99291eaf3e1SJohn Birrell 
99391eaf3e1SJohn Birrell 	default:
99491eaf3e1SJohn Birrell 		prec = CTF_PREC_BASE;
99591eaf3e1SJohn Birrell 	}
99691eaf3e1SJohn Birrell 
997d258fd1dSMark Johnston 	cdp = malloc(sizeof(*cdp), M_FBT, M_WAITOK);
99891eaf3e1SJohn Birrell 	cdp->cd_type = type;
99991eaf3e1SJohn Birrell 	cdp->cd_kind = kind;
100091eaf3e1SJohn Birrell 	cdp->cd_n = n;
100191eaf3e1SJohn Birrell 
100291eaf3e1SJohn Birrell 	if (ctf_list_next(&cd->cd_nodes[prec]) == NULL)
100391eaf3e1SJohn Birrell 		cd->cd_order[prec] = cd->cd_ordp++;
100491eaf3e1SJohn Birrell 
100591eaf3e1SJohn Birrell 	/*
100691eaf3e1SJohn Birrell 	 * Reset cd_qualp to the highest precedence level that we've seen so
100791eaf3e1SJohn Birrell 	 * far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER).
100891eaf3e1SJohn Birrell 	 */
100991eaf3e1SJohn Birrell 	if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY)
101091eaf3e1SJohn Birrell 		cd->cd_qualp = prec;
101191eaf3e1SJohn Birrell 
101291eaf3e1SJohn Birrell 	/*
101391eaf3e1SJohn Birrell 	 * C array declarators are ordered inside out so prepend them.  Also by
101491eaf3e1SJohn Birrell 	 * convention qualifiers of base types precede the type specifier (e.g.
101591eaf3e1SJohn Birrell 	 * const int vs. int const) even though the two forms are equivalent.
101691eaf3e1SJohn Birrell 	 */
101791eaf3e1SJohn Birrell 	if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE))
101891eaf3e1SJohn Birrell 		ctf_list_prepend(&cd->cd_nodes[prec], cdp);
101991eaf3e1SJohn Birrell 	else
102091eaf3e1SJohn Birrell 		ctf_list_append(&cd->cd_nodes[prec], cdp);
102191eaf3e1SJohn Birrell }
102291eaf3e1SJohn Birrell 
102391eaf3e1SJohn Birrell static void
ctf_decl_sprintf(ctf_decl_t * cd,const char * format,...)102491eaf3e1SJohn Birrell ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...)
102591eaf3e1SJohn Birrell {
102691eaf3e1SJohn Birrell 	size_t len = (size_t)(cd->cd_end - cd->cd_ptr);
102791eaf3e1SJohn Birrell 	va_list ap;
102891eaf3e1SJohn Birrell 	size_t n;
102991eaf3e1SJohn Birrell 
103091eaf3e1SJohn Birrell 	va_start(ap, format);
103191eaf3e1SJohn Birrell 	n = vsnprintf(cd->cd_ptr, len, format, ap);
103291eaf3e1SJohn Birrell 	va_end(ap);
103391eaf3e1SJohn Birrell 
103491eaf3e1SJohn Birrell 	cd->cd_ptr += MIN(n, len);
103591eaf3e1SJohn Birrell 	cd->cd_len += n;
103691eaf3e1SJohn Birrell }
103791eaf3e1SJohn Birrell 
103891eaf3e1SJohn Birrell static ssize_t
fbt_type_name(linker_ctf_t * lc,ctf_id_t type,char * buf,size_t len)103991eaf3e1SJohn Birrell fbt_type_name(linker_ctf_t *lc, ctf_id_t type, char *buf, size_t len)
104091eaf3e1SJohn Birrell {
104191eaf3e1SJohn Birrell 	ctf_decl_t cd;
104291eaf3e1SJohn Birrell 	ctf_decl_node_t *cdp;
104391eaf3e1SJohn Birrell 	ctf_decl_prec_t prec, lp, rp;
104491eaf3e1SJohn Birrell 	int ptr, arr;
104591eaf3e1SJohn Birrell 	uint_t k;
104691eaf3e1SJohn Birrell 
104791eaf3e1SJohn Birrell 	if (lc == NULL && type == CTF_ERR)
104891eaf3e1SJohn Birrell 		return (-1); /* simplify caller code by permitting CTF_ERR */
104991eaf3e1SJohn Birrell 
105091eaf3e1SJohn Birrell 	ctf_decl_init(&cd, buf, len);
105191eaf3e1SJohn Birrell 	ctf_decl_push(&cd, lc, type);
105291eaf3e1SJohn Birrell 
105391eaf3e1SJohn Birrell 	if (cd.cd_err != 0) {
105491eaf3e1SJohn Birrell 		ctf_decl_fini(&cd);
105591eaf3e1SJohn Birrell 		return (-1);
105691eaf3e1SJohn Birrell 	}
105791eaf3e1SJohn Birrell 
105891eaf3e1SJohn Birrell 	/*
105991eaf3e1SJohn Birrell 	 * If the type graph's order conflicts with lexical precedence order
106091eaf3e1SJohn Birrell 	 * for pointers or arrays, then we need to surround the declarations at
106191eaf3e1SJohn Birrell 	 * the corresponding lexical precedence with parentheses.  This can
106291eaf3e1SJohn Birrell 	 * result in either a parenthesized pointer (*) as in int (*)() or
106391eaf3e1SJohn Birrell 	 * int (*)[], or in a parenthesized pointer and array as in int (*[])().
106491eaf3e1SJohn Birrell 	 */
106591eaf3e1SJohn Birrell 	ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER;
106691eaf3e1SJohn Birrell 	arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY;
106791eaf3e1SJohn Birrell 
106891eaf3e1SJohn Birrell 	rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1;
106991eaf3e1SJohn Birrell 	lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1;
107091eaf3e1SJohn Birrell 
107191eaf3e1SJohn Birrell 	k = CTF_K_POINTER; /* avoid leading whitespace (see below) */
107291eaf3e1SJohn Birrell 
107391eaf3e1SJohn Birrell 	for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) {
107491eaf3e1SJohn Birrell 		for (cdp = ctf_list_next(&cd.cd_nodes[prec]);
107591eaf3e1SJohn Birrell 		    cdp != NULL; cdp = ctf_list_next(cdp)) {
107691eaf3e1SJohn Birrell 
1077d9175438SMark Johnston 			const void *tp = ctf_lookup_by_id(lc, cdp->cd_type);
1078d9175438SMark Johnston 			const char *name = ctf_type_rname(lc, tp);
107991eaf3e1SJohn Birrell 
108091eaf3e1SJohn Birrell 			if (k != CTF_K_POINTER && k != CTF_K_ARRAY)
108191eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, " ");
108291eaf3e1SJohn Birrell 
108391eaf3e1SJohn Birrell 			if (lp == prec) {
108491eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "(");
108591eaf3e1SJohn Birrell 				lp = -1;
108691eaf3e1SJohn Birrell 			}
108791eaf3e1SJohn Birrell 
108891eaf3e1SJohn Birrell 			switch (cdp->cd_kind) {
108991eaf3e1SJohn Birrell 			case CTF_K_INTEGER:
109091eaf3e1SJohn Birrell 			case CTF_K_FLOAT:
109191eaf3e1SJohn Birrell 			case CTF_K_TYPEDEF:
109291eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "%s", name);
109391eaf3e1SJohn Birrell 				break;
109491eaf3e1SJohn Birrell 			case CTF_K_POINTER:
109591eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "*");
109691eaf3e1SJohn Birrell 				break;
109791eaf3e1SJohn Birrell 			case CTF_K_ARRAY:
109891eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n);
109991eaf3e1SJohn Birrell 				break;
110091eaf3e1SJohn Birrell 			case CTF_K_FUNCTION:
110191eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "()");
110291eaf3e1SJohn Birrell 				break;
110391eaf3e1SJohn Birrell 			case CTF_K_STRUCT:
110491eaf3e1SJohn Birrell 			case CTF_K_FORWARD:
110591eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "struct %s", name);
110691eaf3e1SJohn Birrell 				break;
110791eaf3e1SJohn Birrell 			case CTF_K_UNION:
110891eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "union %s", name);
110991eaf3e1SJohn Birrell 				break;
111091eaf3e1SJohn Birrell 			case CTF_K_ENUM:
111191eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "enum %s", name);
111291eaf3e1SJohn Birrell 				break;
111391eaf3e1SJohn Birrell 			case CTF_K_VOLATILE:
111491eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "volatile");
111591eaf3e1SJohn Birrell 				break;
111691eaf3e1SJohn Birrell 			case CTF_K_CONST:
111791eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "const");
111891eaf3e1SJohn Birrell 				break;
111991eaf3e1SJohn Birrell 			case CTF_K_RESTRICT:
112091eaf3e1SJohn Birrell 				ctf_decl_sprintf(&cd, "restrict");
112191eaf3e1SJohn Birrell 				break;
112291eaf3e1SJohn Birrell 			}
112391eaf3e1SJohn Birrell 
112491eaf3e1SJohn Birrell 			k = cdp->cd_kind;
112591eaf3e1SJohn Birrell 		}
112691eaf3e1SJohn Birrell 
112791eaf3e1SJohn Birrell 		if (rp == prec)
112891eaf3e1SJohn Birrell 			ctf_decl_sprintf(&cd, ")");
112991eaf3e1SJohn Birrell 	}
113091eaf3e1SJohn Birrell 
113191eaf3e1SJohn Birrell 	ctf_decl_fini(&cd);
113291eaf3e1SJohn Birrell 	return (cd.cd_len);
113391eaf3e1SJohn Birrell }
113491eaf3e1SJohn Birrell 
113591eaf3e1SJohn Birrell static void
fbt_getargdesc(void * arg __unused,dtrace_id_t id __unused,void * parg,dtrace_argdesc_t * desc)113691eaf3e1SJohn Birrell fbt_getargdesc(void *arg __unused, dtrace_id_t id __unused, void *parg, dtrace_argdesc_t *desc)
113791eaf3e1SJohn Birrell {
1138d9175438SMark Johnston 	const ctf_header_t *hp;
1139d9175438SMark Johnston 	const char *dp;
114091eaf3e1SJohn Birrell 	fbt_probe_t *fbt = parg;
114191eaf3e1SJohn Birrell 	linker_ctf_t lc;
114291eaf3e1SJohn Birrell 	modctl_t *ctl = fbt->fbtp_ctl;
1143d9175438SMark Johnston 	size_t idwidth;
114491eaf3e1SJohn Birrell 	int ndx = desc->dtargd_ndx;
114591eaf3e1SJohn Birrell 	int symindx = fbt->fbtp_symindx;
114691eaf3e1SJohn Birrell 	uint32_t *ctfoff;
1147d9175438SMark Johnston 	uint32_t offset, type;
1148d9175438SMark Johnston 	uint_t info, n;
1149d9175438SMark Johnston 	ushort_t kind;
115091eaf3e1SJohn Birrell 
1151aaf2546bSAndriy Gapon 	if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
1152aaf2546bSAndriy Gapon 		(void) strcpy(desc->dtargd_native, "int");
1153aaf2546bSAndriy Gapon 		return;
1154aaf2546bSAndriy Gapon 	}
1155aaf2546bSAndriy Gapon 
115691eaf3e1SJohn Birrell 	desc->dtargd_ndx = DTRACE_ARGNONE;
115791eaf3e1SJohn Birrell 
115891eaf3e1SJohn Birrell 	/* Get a pointer to the CTF data and it's length. */
115991eaf3e1SJohn Birrell 	if (linker_ctf_get(ctl, &lc) != 0)
116091eaf3e1SJohn Birrell 		/* No CTF data? Something wrong? *shrug* */
116191eaf3e1SJohn Birrell 		return;
116291eaf3e1SJohn Birrell 
116391eaf3e1SJohn Birrell 	/* Check if this module hasn't been initialised yet. */
116491eaf3e1SJohn Birrell 	if (*lc.ctfoffp == NULL) {
116591eaf3e1SJohn Birrell 		/*
116691eaf3e1SJohn Birrell 		 * Initialise the CTF object and function symindx to
116791eaf3e1SJohn Birrell 		 * byte offset array.
116891eaf3e1SJohn Birrell 		 */
116991eaf3e1SJohn Birrell 		if (fbt_ctfoff_init(ctl, &lc) != 0)
117091eaf3e1SJohn Birrell 			return;
117191eaf3e1SJohn Birrell 
117291eaf3e1SJohn Birrell 		/* Initialise the CTF type to byte offset array. */
117391eaf3e1SJohn Birrell 		if (fbt_typoff_init(&lc) != 0)
117491eaf3e1SJohn Birrell 			return;
117591eaf3e1SJohn Birrell 	}
117691eaf3e1SJohn Birrell 
117791eaf3e1SJohn Birrell 	ctfoff = *lc.ctfoffp;
117891eaf3e1SJohn Birrell 
117991eaf3e1SJohn Birrell 	if (ctfoff == NULL || *lc.typoffp == NULL)
118091eaf3e1SJohn Birrell 		return;
118191eaf3e1SJohn Birrell 
118291eaf3e1SJohn Birrell 	/* Check if the symbol index is out of range. */
118391eaf3e1SJohn Birrell 	if (symindx >= lc.nsym)
118491eaf3e1SJohn Birrell 		return;
118591eaf3e1SJohn Birrell 
118691eaf3e1SJohn Birrell 	/* Check if the symbol isn't cross-referenced. */
118791eaf3e1SJohn Birrell 	if ((offset = ctfoff[symindx]) == 0xffffffff)
118891eaf3e1SJohn Birrell 		return;
118991eaf3e1SJohn Birrell 
1190d9175438SMark Johnston 	hp = (const ctf_header_t *) lc.ctftab;
1191d9175438SMark Johnston 	idwidth = hp->cth_version == CTF_VERSION_2 ? 2 : 4;
1192d9175438SMark Johnston 	dp = (const char *)(lc.ctftab + offset + sizeof(ctf_header_t));
119391eaf3e1SJohn Birrell 
1194d9175438SMark Johnston 	info = 0;
1195d9175438SMark Johnston 	memcpy(&info, dp, idwidth);
1196d9175438SMark Johnston 	dp += idwidth;
1197d9175438SMark Johnston 	if (hp->cth_version == CTF_VERSION_2) {
1198d9175438SMark Johnston 		kind = CTF_V2_INFO_KIND(info);
1199d9175438SMark Johnston 		n = CTF_V2_INFO_VLEN(info);
1200d9175438SMark Johnston 	} else {
1201d9175438SMark Johnston 		kind = CTF_V3_INFO_KIND(info);
1202d9175438SMark Johnston 		n = CTF_V3_INFO_VLEN(info);
1203d9175438SMark Johnston 	}
120491eaf3e1SJohn Birrell 
120591eaf3e1SJohn Birrell 	if (kind == CTF_K_UNKNOWN && n == 0) {
120691eaf3e1SJohn Birrell 		printf("%s(%d): Unknown function!\n",__func__,__LINE__);
120791eaf3e1SJohn Birrell 		return;
120891eaf3e1SJohn Birrell 	}
120991eaf3e1SJohn Birrell 
121091eaf3e1SJohn Birrell 	if (kind != CTF_K_FUNCTION) {
121191eaf3e1SJohn Birrell 		printf("%s(%d): Expected a function!\n",__func__,__LINE__);
121291eaf3e1SJohn Birrell 		return;
121391eaf3e1SJohn Birrell 	}
121491eaf3e1SJohn Birrell 
1215aaf2546bSAndriy Gapon 	if (fbt->fbtp_roffset != 0) {
1216aaf2546bSAndriy Gapon 		/* Only return type is available for args[1] in return probe. */
1217aaf2546bSAndriy Gapon 		if (ndx > 1)
1218aaf2546bSAndriy Gapon 			return;
1219aaf2546bSAndriy Gapon 		ASSERT(ndx == 1);
1220aaf2546bSAndriy Gapon 	} else {
122191eaf3e1SJohn Birrell 		/* Check if the requested argument doesn't exist. */
122291eaf3e1SJohn Birrell 		if (ndx >= n)
122391eaf3e1SJohn Birrell 			return;
122491eaf3e1SJohn Birrell 
122591eaf3e1SJohn Birrell 		/* Skip the return type and arguments up to the one requested. */
1226d9175438SMark Johnston 		dp += idwidth * (ndx + 1);
1227aaf2546bSAndriy Gapon 	}
122891eaf3e1SJohn Birrell 
1229d9175438SMark Johnston 	type = 0;
1230d9175438SMark Johnston 	memcpy(&type, dp, idwidth);
1231d9175438SMark Johnston 	if (fbt_type_name(&lc, type, desc->dtargd_native, sizeof(desc->dtargd_native)) > 0)
123291eaf3e1SJohn Birrell 		desc->dtargd_ndx = ndx;
123391eaf3e1SJohn Birrell }
123491eaf3e1SJohn Birrell 
12358776669bSMark Johnston static int
fbt_linker_file_cb(linker_file_t lf,void * arg)12368776669bSMark Johnston fbt_linker_file_cb(linker_file_t lf, void *arg)
12378776669bSMark Johnston {
12388776669bSMark Johnston 
12398776669bSMark Johnston 	fbt_provide_module(arg, lf);
12408776669bSMark Johnston 
12418776669bSMark Johnston 	return (0);
12428776669bSMark Johnston }
12438776669bSMark Johnston 
124491eaf3e1SJohn Birrell static void
fbt_load(void * dummy)124591eaf3e1SJohn Birrell fbt_load(void *dummy)
124691eaf3e1SJohn Birrell {
124791eaf3e1SJohn Birrell 	/* Default the probe table size if not specified. */
124891eaf3e1SJohn Birrell 	if (fbt_probetab_size == 0)
124991eaf3e1SJohn Birrell 		fbt_probetab_size = FBT_PROBETAB_SIZE;
125091eaf3e1SJohn Birrell 
125191eaf3e1SJohn Birrell 	/* Choose the hash mask for the probe table. */
125291eaf3e1SJohn Birrell 	fbt_probetab_mask = fbt_probetab_size - 1;
125391eaf3e1SJohn Birrell 
125491eaf3e1SJohn Birrell 	/* Allocate memory for the probe table. */
125591eaf3e1SJohn Birrell 	fbt_probetab =
125691eaf3e1SJohn Birrell 	    malloc(fbt_probetab_size * sizeof (fbt_probe_t *), M_FBT, M_WAITOK | M_ZERO);
125791eaf3e1SJohn Birrell 
125891eaf3e1SJohn Birrell 	dtrace_doubletrap_func = fbt_doubletrap;
125991eaf3e1SJohn Birrell 	dtrace_invop_add(fbt_invop);
126091eaf3e1SJohn Birrell 
126191eaf3e1SJohn Birrell 	if (dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_USER,
126291eaf3e1SJohn Birrell 	    NULL, &fbt_pops, NULL, &fbt_id) != 0)
126391eaf3e1SJohn Birrell 		return;
126491eaf3e1SJohn Birrell 
12658776669bSMark Johnston 	/* Create probes for the kernel and already-loaded modules. */
12668776669bSMark Johnston 	linker_file_foreach(fbt_linker_file_cb, NULL);
12678776669bSMark Johnston }
126891eaf3e1SJohn Birrell 
126991eaf3e1SJohn Birrell static int
fbt_unload(void)12706339314cSDimitry Andric fbt_unload(void)
127191eaf3e1SJohn Birrell {
127291eaf3e1SJohn Birrell 	int error = 0;
127391eaf3e1SJohn Birrell 
127491eaf3e1SJohn Birrell 	/* De-register the invalid opcode handler. */
127591eaf3e1SJohn Birrell 	dtrace_invop_remove(fbt_invop);
127691eaf3e1SJohn Birrell 
127791eaf3e1SJohn Birrell 	dtrace_doubletrap_func = NULL;
127891eaf3e1SJohn Birrell 
127991eaf3e1SJohn Birrell 	/* De-register this DTrace provider. */
128091eaf3e1SJohn Birrell 	if ((error = dtrace_unregister(fbt_id)) != 0)
128191eaf3e1SJohn Birrell 		return (error);
128291eaf3e1SJohn Birrell 
128391eaf3e1SJohn Birrell 	/* Free the probe table. */
128491eaf3e1SJohn Birrell 	free(fbt_probetab, M_FBT);
128591eaf3e1SJohn Birrell 	fbt_probetab = NULL;
128691eaf3e1SJohn Birrell 	fbt_probetab_mask = 0;
128791eaf3e1SJohn Birrell 
128891eaf3e1SJohn Birrell 	return (error);
128991eaf3e1SJohn Birrell }
129091eaf3e1SJohn Birrell 
129191eaf3e1SJohn Birrell static int
fbt_modevent(module_t mod __unused,int type,void * data __unused)129291eaf3e1SJohn Birrell fbt_modevent(module_t mod __unused, int type, void *data __unused)
129391eaf3e1SJohn Birrell {
129491eaf3e1SJohn Birrell 	int error = 0;
129591eaf3e1SJohn Birrell 
129691eaf3e1SJohn Birrell 	switch (type) {
129791eaf3e1SJohn Birrell 	case MOD_LOAD:
129891eaf3e1SJohn Birrell 		break;
129991eaf3e1SJohn Birrell 
130091eaf3e1SJohn Birrell 	case MOD_UNLOAD:
130191eaf3e1SJohn Birrell 		break;
130291eaf3e1SJohn Birrell 
130391eaf3e1SJohn Birrell 	case MOD_SHUTDOWN:
130491eaf3e1SJohn Birrell 		break;
130591eaf3e1SJohn Birrell 
130691eaf3e1SJohn Birrell 	default:
130791eaf3e1SJohn Birrell 		error = EOPNOTSUPP;
130891eaf3e1SJohn Birrell 		break;
130991eaf3e1SJohn Birrell 
131091eaf3e1SJohn Birrell 	}
131191eaf3e1SJohn Birrell 
131291eaf3e1SJohn Birrell 	return (error);
131391eaf3e1SJohn Birrell }
131491eaf3e1SJohn Birrell 
131591eaf3e1SJohn Birrell SYSINIT(fbt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fbt_load, NULL);
131691eaf3e1SJohn Birrell SYSUNINIT(fbt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fbt_unload, NULL);
131791eaf3e1SJohn Birrell 
131891eaf3e1SJohn Birrell DEV_MODULE(fbt, fbt_modevent, NULL);
131991eaf3e1SJohn Birrell MODULE_VERSION(fbt, 1);
132091eaf3e1SJohn Birrell MODULE_DEPEND(fbt, dtrace, 1, 1, 1);
132191eaf3e1SJohn Birrell MODULE_DEPEND(fbt, opensolaris, 1, 1, 1);
1322