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>
52*e453e498SBrooks Davis #include <sys/stdarg.h>
5391eaf3e1SJohn Birrell #include <sys/syscall.h>
5491eaf3e1SJohn Birrell #include <sys/sysent.h>
5591eaf3e1SJohn Birrell #include <sys/sysproto.h>
5691eaf3e1SJohn Birrell #include <sys/uio.h>
5791eaf3e1SJohn Birrell #include <sys/unistd.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 /*
140fdeb273dSMark Johnston * The KMSAN runtime can't be instrumented safely.
141fdeb273dSMark Johnston */
142fdeb273dSMark Johnston if (strncmp(name, "__msan", 6) == 0 ||
143fdeb273dSMark Johnston strncmp(name, "kmsan_", 6) == 0)
144fdeb273dSMark Johnston return (1);
145fdeb273dSMark Johnston
146fdeb273dSMark Johnston /*
1479b9e7f4cSJohn Baldwin * Stack unwinders may be called from probe context on some
1489b9e7f4cSJohn Baldwin * platforms.
1499b9e7f4cSJohn Baldwin */
150ae953968SJohn Baldwin #if defined(__aarch64__) || defined(__riscv)
1519b9e7f4cSJohn Baldwin if (strcmp(name, "unwind_frame") == 0)
1529b9e7f4cSJohn Baldwin return (1);
1539b9e7f4cSJohn Baldwin #endif
1549b9e7f4cSJohn Baldwin
1559b9e7f4cSJohn Baldwin /*
1560ff41755SRuslan Bukin * When DTrace is built into the kernel we need to exclude
1570ff41755SRuslan Bukin * the FBT functions from instrumentation.
1580ff41755SRuslan Bukin */
1590ff41755SRuslan Bukin #ifndef _KLD_MODULE
1600ff41755SRuslan Bukin if (strncmp(name, "fbt_", 4) == 0)
1610ff41755SRuslan Bukin return (1);
1620ff41755SRuslan Bukin #endif
1630ff41755SRuslan Bukin
1640ff41755SRuslan Bukin return (0);
1650ff41755SRuslan Bukin }
1660ff41755SRuslan Bukin
16791eaf3e1SJohn Birrell static void
fbt_doubletrap(void)16891eaf3e1SJohn Birrell fbt_doubletrap(void)
16991eaf3e1SJohn Birrell {
17091eaf3e1SJohn Birrell fbt_probe_t *fbt;
17191eaf3e1SJohn Birrell int i;
17291eaf3e1SJohn Birrell
17391eaf3e1SJohn Birrell for (i = 0; i < fbt_probetab_size; i++) {
17491eaf3e1SJohn Birrell fbt = fbt_probetab[i];
17591eaf3e1SJohn Birrell
176c208cb99SMark Johnston for (; fbt != NULL; fbt = fbt->fbtp_probenext)
17735127d3cSMark Johnston fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
17891eaf3e1SJohn Birrell }
17991eaf3e1SJohn Birrell }
18091eaf3e1SJohn Birrell
18191eaf3e1SJohn Birrell static void
fbt_provide_module(void * arg,modctl_t * lf)18291eaf3e1SJohn Birrell fbt_provide_module(void *arg, modctl_t *lf)
18391eaf3e1SJohn Birrell {
18491eaf3e1SJohn Birrell char modname[MAXPATHLEN];
18591eaf3e1SJohn Birrell int i;
18691eaf3e1SJohn Birrell size_t len;
18791eaf3e1SJohn Birrell
18891eaf3e1SJohn Birrell strlcpy(modname, lf->filename, sizeof(modname));
18991eaf3e1SJohn Birrell len = strlen(modname);
19091eaf3e1SJohn Birrell if (len > 3 && strcmp(modname + len - 3, ".ko") == 0)
19191eaf3e1SJohn Birrell modname[len - 3] = '\0';
19291eaf3e1SJohn Birrell
19391eaf3e1SJohn Birrell /*
19491eaf3e1SJohn Birrell * Employees of dtrace and their families are ineligible. Void
19591eaf3e1SJohn Birrell * where prohibited.
19691eaf3e1SJohn Birrell */
19791eaf3e1SJohn Birrell if (strcmp(modname, "dtrace") == 0)
19891eaf3e1SJohn Birrell return;
19991eaf3e1SJohn Birrell
20091eaf3e1SJohn Birrell /*
20191eaf3e1SJohn Birrell * To register with DTrace, a module must list 'dtrace' as a
20291eaf3e1SJohn Birrell * dependency in order for the kernel linker to resolve
20391eaf3e1SJohn Birrell * symbols like dtrace_register(). All modules with such a
20491eaf3e1SJohn Birrell * dependency are ineligible for FBT tracing.
20591eaf3e1SJohn Birrell */
20691eaf3e1SJohn Birrell for (i = 0; i < lf->ndeps; i++)
20791eaf3e1SJohn Birrell if (strncmp(lf->deps[i]->filename, "dtrace", 6) == 0)
20891eaf3e1SJohn Birrell return;
20991eaf3e1SJohn Birrell
21091eaf3e1SJohn Birrell if (lf->fbt_nentries) {
21191eaf3e1SJohn Birrell /*
21291eaf3e1SJohn Birrell * This module has some FBT entries allocated; we're afraid
21391eaf3e1SJohn Birrell * to screw with it.
21491eaf3e1SJohn Birrell */
21591eaf3e1SJohn Birrell return;
21691eaf3e1SJohn Birrell }
21791eaf3e1SJohn Birrell
21891eaf3e1SJohn Birrell /*
21991eaf3e1SJohn Birrell * List the functions in the module and the symbol values.
22091eaf3e1SJohn Birrell */
22191eaf3e1SJohn Birrell (void) linker_file_function_listall(lf, fbt_provide_module_function, modname);
22291eaf3e1SJohn Birrell }
22391eaf3e1SJohn Birrell
22491eaf3e1SJohn Birrell static void
fbt_destroy_one(fbt_probe_t * fbt)225c208cb99SMark Johnston fbt_destroy_one(fbt_probe_t *fbt)
226c208cb99SMark Johnston {
227c208cb99SMark Johnston fbt_probe_t *hash, *hashprev, *next;
228c208cb99SMark Johnston int ndx;
229c208cb99SMark Johnston
230c208cb99SMark Johnston ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
231c208cb99SMark Johnston for (hash = fbt_probetab[ndx], hashprev = NULL; hash != NULL;
232a9d49f9eSMark Johnston hashprev = hash, hash = hash->fbtp_hashnext) {
233c208cb99SMark Johnston if (hash == fbt) {
234c208cb99SMark Johnston if ((next = fbt->fbtp_tracenext) != NULL)
235c208cb99SMark Johnston next->fbtp_hashnext = hash->fbtp_hashnext;
236c208cb99SMark Johnston else
237c208cb99SMark Johnston next = hash->fbtp_hashnext;
238c208cb99SMark Johnston if (hashprev != NULL)
239c208cb99SMark Johnston hashprev->fbtp_hashnext = next;
240c208cb99SMark Johnston else
241c208cb99SMark Johnston fbt_probetab[ndx] = next;
242c208cb99SMark Johnston goto free;
243c208cb99SMark Johnston } else if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
244c208cb99SMark Johnston for (next = hash; next->fbtp_tracenext != NULL;
245c208cb99SMark Johnston next = next->fbtp_tracenext) {
246c208cb99SMark Johnston if (fbt == next->fbtp_tracenext) {
247c208cb99SMark Johnston next->fbtp_tracenext =
248c208cb99SMark Johnston fbt->fbtp_tracenext;
249c208cb99SMark Johnston goto free;
250c208cb99SMark Johnston }
251c208cb99SMark Johnston }
252c208cb99SMark Johnston }
253c208cb99SMark Johnston }
254c208cb99SMark Johnston panic("probe %p not found in hash table", fbt);
255c208cb99SMark Johnston free:
256c208cb99SMark Johnston free(fbt, M_FBT);
257c208cb99SMark Johnston }
258c208cb99SMark Johnston
259c208cb99SMark Johnston static void
fbt_destroy(void * arg,dtrace_id_t id,void * parg)26091eaf3e1SJohn Birrell fbt_destroy(void *arg, dtrace_id_t id, void *parg)
26191eaf3e1SJohn Birrell {
262c208cb99SMark Johnston fbt_probe_t *fbt = parg, *next;
26391eaf3e1SJohn Birrell modctl_t *ctl;
26491eaf3e1SJohn Birrell
26591eaf3e1SJohn Birrell do {
26691eaf3e1SJohn Birrell ctl = fbt->fbtp_ctl;
26791eaf3e1SJohn Birrell ctl->fbt_nentries--;
26891eaf3e1SJohn Birrell
269c208cb99SMark Johnston next = fbt->fbtp_probenext;
270c208cb99SMark Johnston fbt_destroy_one(fbt);
27191eaf3e1SJohn Birrell fbt = next;
27291eaf3e1SJohn Birrell } while (fbt != NULL);
27391eaf3e1SJohn Birrell }
27491eaf3e1SJohn Birrell
27591eaf3e1SJohn Birrell static void
fbt_enable(void * arg,dtrace_id_t id,void * parg)27691eaf3e1SJohn Birrell fbt_enable(void *arg, dtrace_id_t id, void *parg)
27791eaf3e1SJohn Birrell {
27891eaf3e1SJohn Birrell fbt_probe_t *fbt = parg;
27991eaf3e1SJohn Birrell modctl_t *ctl = fbt->fbtp_ctl;
28091eaf3e1SJohn Birrell
28191eaf3e1SJohn Birrell ctl->nenabled++;
28291eaf3e1SJohn Birrell
28391eaf3e1SJohn Birrell /*
28491eaf3e1SJohn Birrell * Now check that our modctl has the expected load count. If it
28591eaf3e1SJohn Birrell * doesn't, this module must have been unloaded and reloaded -- and
28691eaf3e1SJohn Birrell * we're not going to touch it.
28791eaf3e1SJohn Birrell */
28891eaf3e1SJohn Birrell if (ctl->loadcnt != fbt->fbtp_loadcnt) {
28991eaf3e1SJohn Birrell if (fbt_verbose) {
29091eaf3e1SJohn Birrell printf("fbt is failing for probe %s "
29191eaf3e1SJohn Birrell "(module %s reloaded)",
29291eaf3e1SJohn Birrell fbt->fbtp_name, ctl->filename);
29391eaf3e1SJohn Birrell }
29491eaf3e1SJohn Birrell
29591eaf3e1SJohn Birrell return;
29691eaf3e1SJohn Birrell }
29791eaf3e1SJohn Birrell
298c208cb99SMark Johnston for (; fbt != NULL; fbt = fbt->fbtp_probenext) {
299266b4a78SMark Johnston fbt_patch_tracepoint(fbt, fbt->fbtp_patchval);
300c208cb99SMark Johnston fbt->fbtp_enabled++;
301c208cb99SMark Johnston }
30291eaf3e1SJohn Birrell }
30391eaf3e1SJohn Birrell
30491eaf3e1SJohn Birrell static void
fbt_disable(void * arg,dtrace_id_t id,void * parg)30591eaf3e1SJohn Birrell fbt_disable(void *arg, dtrace_id_t id, void *parg)
30691eaf3e1SJohn Birrell {
307c208cb99SMark Johnston fbt_probe_t *fbt = parg, *hash;
30891eaf3e1SJohn Birrell modctl_t *ctl = fbt->fbtp_ctl;
30991eaf3e1SJohn Birrell
31091eaf3e1SJohn Birrell ASSERT(ctl->nenabled > 0);
31191eaf3e1SJohn Birrell ctl->nenabled--;
31291eaf3e1SJohn Birrell
31391eaf3e1SJohn Birrell if ((ctl->loadcnt != fbt->fbtp_loadcnt))
31491eaf3e1SJohn Birrell return;
31591eaf3e1SJohn Birrell
316c208cb99SMark Johnston for (; fbt != NULL; fbt = fbt->fbtp_probenext) {
317c208cb99SMark Johnston fbt->fbtp_enabled--;
318c208cb99SMark Johnston
319c208cb99SMark Johnston for (hash = fbt_probetab[FBT_ADDR2NDX(fbt->fbtp_patchpoint)];
320c208cb99SMark Johnston hash != NULL; hash = hash->fbtp_hashnext) {
321c208cb99SMark Johnston if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
322c208cb99SMark Johnston for (; hash != NULL; hash = hash->fbtp_tracenext)
323c208cb99SMark Johnston if (hash->fbtp_enabled > 0)
324c208cb99SMark Johnston break;
325c208cb99SMark Johnston break;
326c208cb99SMark Johnston }
327c208cb99SMark Johnston }
328c208cb99SMark Johnston if (hash == NULL)
32935127d3cSMark Johnston fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
33091eaf3e1SJohn Birrell }
331c208cb99SMark Johnston }
33291eaf3e1SJohn Birrell
33391eaf3e1SJohn Birrell static void
fbt_suspend(void * arg,dtrace_id_t id,void * parg)33491eaf3e1SJohn Birrell fbt_suspend(void *arg, dtrace_id_t id, void *parg)
33591eaf3e1SJohn Birrell {
33691eaf3e1SJohn Birrell fbt_probe_t *fbt = parg;
33791eaf3e1SJohn Birrell modctl_t *ctl = fbt->fbtp_ctl;
33891eaf3e1SJohn Birrell
33991eaf3e1SJohn Birrell ASSERT(ctl->nenabled > 0);
34091eaf3e1SJohn Birrell
34191eaf3e1SJohn Birrell if ((ctl->loadcnt != fbt->fbtp_loadcnt))
34291eaf3e1SJohn Birrell return;
34391eaf3e1SJohn Birrell
344c208cb99SMark Johnston for (; fbt != NULL; fbt = fbt->fbtp_probenext)
34535127d3cSMark Johnston fbt_patch_tracepoint(fbt, fbt->fbtp_savedval);
34691eaf3e1SJohn Birrell }
34791eaf3e1SJohn Birrell
34891eaf3e1SJohn Birrell static void
fbt_resume(void * arg,dtrace_id_t id,void * parg)34991eaf3e1SJohn Birrell fbt_resume(void *arg, dtrace_id_t id, void *parg)
35091eaf3e1SJohn Birrell {
35191eaf3e1SJohn Birrell fbt_probe_t *fbt = parg;
35291eaf3e1SJohn Birrell modctl_t *ctl = fbt->fbtp_ctl;
35391eaf3e1SJohn Birrell
35491eaf3e1SJohn Birrell ASSERT(ctl->nenabled > 0);
35591eaf3e1SJohn Birrell
35691eaf3e1SJohn Birrell if ((ctl->loadcnt != fbt->fbtp_loadcnt))
35791eaf3e1SJohn Birrell return;
35891eaf3e1SJohn Birrell
359c208cb99SMark Johnston for (; fbt != NULL; fbt = fbt->fbtp_probenext)
360266b4a78SMark Johnston fbt_patch_tracepoint(fbt, fbt->fbtp_patchval);
36191eaf3e1SJohn Birrell }
36291eaf3e1SJohn Birrell
36391eaf3e1SJohn Birrell static int
fbt_ctfoff_init(modctl_t * lf,linker_ctf_t * lc)36491eaf3e1SJohn Birrell fbt_ctfoff_init(modctl_t *lf, linker_ctf_t *lc)
36591eaf3e1SJohn Birrell {
366b1a217a3SEd Maste const Elf_Sym *symp = lc->symtab;
36791eaf3e1SJohn Birrell const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
36891eaf3e1SJohn Birrell const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
369d9175438SMark Johnston size_t idwidth;
37091eaf3e1SJohn Birrell int i;
37191eaf3e1SJohn Birrell uint32_t *ctfoff;
37291eaf3e1SJohn Birrell uint32_t objtoff = hp->cth_objtoff;
37391eaf3e1SJohn Birrell uint32_t funcoff = hp->cth_funcoff;
374d9175438SMark Johnston uint_t kind, info, vlen;
37591eaf3e1SJohn Birrell
37691eaf3e1SJohn Birrell /* Sanity check. */
37791eaf3e1SJohn Birrell if (hp->cth_magic != CTF_MAGIC) {
37891eaf3e1SJohn Birrell printf("Bad magic value in CTF data of '%s'\n",lf->pathname);
37991eaf3e1SJohn Birrell return (EINVAL);
38091eaf3e1SJohn Birrell }
38191eaf3e1SJohn Birrell
38291eaf3e1SJohn Birrell if (lc->symtab == NULL) {
38391eaf3e1SJohn Birrell printf("No symbol table in '%s'\n",lf->pathname);
38491eaf3e1SJohn Birrell return (EINVAL);
38591eaf3e1SJohn Birrell }
38691eaf3e1SJohn Birrell
387d258fd1dSMark Johnston ctfoff = malloc(sizeof(uint32_t) * lc->nsym, M_LINKER, M_WAITOK);
38891eaf3e1SJohn Birrell *lc->ctfoffp = ctfoff;
38991eaf3e1SJohn Birrell
390d9175438SMark Johnston idwidth = hp->cth_version == CTF_VERSION_2 ? 2 : 4;
391d9175438SMark Johnston
39291eaf3e1SJohn Birrell for (i = 0; i < lc->nsym; i++, ctfoff++, symp++) {
39391eaf3e1SJohn Birrell if (symp->st_name == 0 || symp->st_shndx == SHN_UNDEF) {
39491eaf3e1SJohn Birrell *ctfoff = 0xffffffff;
39591eaf3e1SJohn Birrell continue;
39691eaf3e1SJohn Birrell }
39791eaf3e1SJohn Birrell
39891eaf3e1SJohn Birrell switch (ELF_ST_TYPE(symp->st_info)) {
39991eaf3e1SJohn Birrell case STT_OBJECT:
40091eaf3e1SJohn Birrell if (objtoff >= hp->cth_funcoff ||
40191eaf3e1SJohn Birrell (symp->st_shndx == SHN_ABS && symp->st_value == 0)) {
40291eaf3e1SJohn Birrell *ctfoff = 0xffffffff;
40391eaf3e1SJohn Birrell break;
40491eaf3e1SJohn Birrell }
40591eaf3e1SJohn Birrell
40691eaf3e1SJohn Birrell *ctfoff = objtoff;
407d9175438SMark Johnston objtoff += idwidth;
40891eaf3e1SJohn Birrell break;
40991eaf3e1SJohn Birrell
41091eaf3e1SJohn Birrell case STT_FUNC:
41191eaf3e1SJohn Birrell if (funcoff >= hp->cth_typeoff) {
41291eaf3e1SJohn Birrell *ctfoff = 0xffffffff;
41391eaf3e1SJohn Birrell break;
41491eaf3e1SJohn Birrell }
41591eaf3e1SJohn Birrell
41691eaf3e1SJohn Birrell *ctfoff = funcoff;
41791eaf3e1SJohn Birrell
418d9175438SMark Johnston info = 0;
419d9175438SMark Johnston memcpy(&info, ctfdata + funcoff, idwidth);
420d9175438SMark Johnston if (hp->cth_version == CTF_VERSION_2) {
421d9175438SMark Johnston kind = CTF_V2_INFO_KIND(info);
422d9175438SMark Johnston vlen = CTF_V2_INFO_VLEN(info);
423d9175438SMark Johnston } else {
424d9175438SMark Johnston kind = CTF_V3_INFO_KIND(info);
425d9175438SMark Johnston vlen = CTF_V3_INFO_VLEN(info);
426d9175438SMark Johnston }
42791eaf3e1SJohn Birrell
42891eaf3e1SJohn Birrell /*
42991eaf3e1SJohn Birrell * If we encounter a zero pad at the end, just skip it.
43091eaf3e1SJohn Birrell * Otherwise skip over the function and its return type
43191eaf3e1SJohn Birrell * (+2) and the argument list (vlen).
43291eaf3e1SJohn Birrell */
433d9175438SMark Johnston if (kind == CTF_K_UNKNOWN && vlen == 0)
434d9175438SMark Johnston funcoff += idwidth;
43591eaf3e1SJohn Birrell else
436d9175438SMark Johnston funcoff += idwidth * (vlen + 2);
43791eaf3e1SJohn Birrell break;
43891eaf3e1SJohn Birrell
43991eaf3e1SJohn Birrell default:
44091eaf3e1SJohn Birrell *ctfoff = 0xffffffff;
44191eaf3e1SJohn Birrell break;
44291eaf3e1SJohn Birrell }
44391eaf3e1SJohn Birrell }
44491eaf3e1SJohn Birrell
44591eaf3e1SJohn Birrell return (0);
44691eaf3e1SJohn Birrell }
44791eaf3e1SJohn Birrell
448d9175438SMark Johnston static void
fbt_get_ctt_index(uint8_t version,const void * v,uint_t * indexp,uint_t * typep,int * ischildp)449d9175438SMark Johnston fbt_get_ctt_index(uint8_t version, const void *v, uint_t *indexp,
450d9175438SMark Johnston uint_t *typep, int *ischildp)
451d9175438SMark Johnston {
452d9175438SMark Johnston uint_t index, type;
453d9175438SMark Johnston int ischild;
454d9175438SMark Johnston
455d9175438SMark Johnston if (version == CTF_VERSION_2) {
456d9175438SMark Johnston const struct ctf_type_v2 *ctt = v;
457d9175438SMark Johnston
458d9175438SMark Johnston type = ctt->ctt_type;
459d9175438SMark Johnston index = CTF_V2_TYPE_TO_INDEX(ctt->ctt_type);
460d9175438SMark Johnston ischild = CTF_V2_TYPE_ISCHILD(ctt->ctt_type);
461d9175438SMark Johnston } else {
462d9175438SMark Johnston const struct ctf_type_v3 *ctt = v;
463d9175438SMark Johnston
464d9175438SMark Johnston type = ctt->ctt_type;
465d9175438SMark Johnston index = CTF_V3_TYPE_TO_INDEX(ctt->ctt_type);
466d9175438SMark Johnston ischild = CTF_V3_TYPE_ISCHILD(ctt->ctt_type);
467d9175438SMark Johnston }
468d9175438SMark Johnston
469d9175438SMark Johnston if (indexp != NULL)
470d9175438SMark Johnston *indexp = index;
471d9175438SMark Johnston if (typep != NULL)
472d9175438SMark Johnston *typep = type;
473d9175438SMark Johnston if (ischildp != NULL)
474d9175438SMark Johnston *ischildp = ischild;
475d9175438SMark Johnston }
476d9175438SMark Johnston
47791eaf3e1SJohn Birrell static ssize_t
fbt_get_ctt_size(uint8_t version,const void * tp,ssize_t * sizep,ssize_t * incrementp)478d9175438SMark Johnston fbt_get_ctt_size(uint8_t version, const void *tp, ssize_t *sizep,
47991eaf3e1SJohn Birrell ssize_t *incrementp)
48091eaf3e1SJohn Birrell {
48191eaf3e1SJohn Birrell ssize_t size, increment;
48291eaf3e1SJohn Birrell
483d9175438SMark Johnston if (version == CTF_VERSION_2) {
484d9175438SMark Johnston const struct ctf_type_v2 *ctt = tp;
485d9175438SMark Johnston
486d9175438SMark Johnston if (ctt->ctt_size == CTF_V2_LSIZE_SENT) {
487d9175438SMark Johnston size = CTF_TYPE_LSIZE(ctt);
488d9175438SMark Johnston increment = sizeof (struct ctf_type_v2);
48991eaf3e1SJohn Birrell } else {
490d9175438SMark Johnston size = ctt->ctt_size;
491d9175438SMark Johnston increment = sizeof (struct ctf_stype_v2);
492d9175438SMark Johnston }
493d9175438SMark Johnston } else {
494d9175438SMark Johnston const struct ctf_type_v3 *ctt = tp;
495d9175438SMark Johnston
496d9175438SMark Johnston if (ctt->ctt_size == CTF_V3_LSIZE_SENT) {
497d9175438SMark Johnston size = CTF_TYPE_LSIZE(ctt);
498d9175438SMark Johnston increment = sizeof (struct ctf_type_v3);
499d9175438SMark Johnston } else {
500d9175438SMark Johnston size = ctt->ctt_size;
501d9175438SMark Johnston increment = sizeof (struct ctf_stype_v3);
502d9175438SMark Johnston }
50391eaf3e1SJohn Birrell }
50491eaf3e1SJohn Birrell
50591eaf3e1SJohn Birrell if (sizep)
50691eaf3e1SJohn Birrell *sizep = size;
50791eaf3e1SJohn Birrell if (incrementp)
50891eaf3e1SJohn Birrell *incrementp = increment;
50991eaf3e1SJohn Birrell
51091eaf3e1SJohn Birrell return (size);
51191eaf3e1SJohn Birrell }
51291eaf3e1SJohn Birrell
513d9175438SMark Johnston static void
fbt_get_ctt_info(uint8_t version,const void * tp,uint_t * kindp,uint_t * vlenp,int * isrootp)514d9175438SMark Johnston fbt_get_ctt_info(uint8_t version, const void *tp, uint_t *kindp, uint_t *vlenp,
515d9175438SMark Johnston int *isrootp)
516d9175438SMark Johnston {
517d9175438SMark Johnston uint_t kind, vlen;
518d9175438SMark Johnston int isroot;
519d9175438SMark Johnston
520d9175438SMark Johnston if (version == CTF_VERSION_2) {
521d9175438SMark Johnston const struct ctf_type_v2 *ctt = tp;
522d9175438SMark Johnston
523d9175438SMark Johnston kind = CTF_V2_INFO_KIND(ctt->ctt_info);
524d9175438SMark Johnston vlen = CTF_V2_INFO_VLEN(ctt->ctt_info);
525d9175438SMark Johnston isroot = CTF_V2_INFO_ISROOT(ctt->ctt_info);
526d9175438SMark Johnston } else {
527d9175438SMark Johnston const struct ctf_type_v3 *ctt = tp;
528d9175438SMark Johnston
529d9175438SMark Johnston kind = CTF_V3_INFO_KIND(ctt->ctt_info);
530d9175438SMark Johnston vlen = CTF_V3_INFO_VLEN(ctt->ctt_info);
531d9175438SMark Johnston isroot = CTF_V3_INFO_ISROOT(ctt->ctt_info);
532d9175438SMark Johnston }
533d9175438SMark Johnston
534d9175438SMark Johnston if (kindp != NULL)
535d9175438SMark Johnston *kindp = kind;
536d9175438SMark Johnston if (vlenp != NULL)
537d9175438SMark Johnston *vlenp = vlen;
538d9175438SMark Johnston if (isrootp != NULL)
539d9175438SMark Johnston *isrootp = isroot;
540d9175438SMark Johnston }
541d9175438SMark Johnston
54291eaf3e1SJohn Birrell static int
fbt_typoff_init(linker_ctf_t * lc)54391eaf3e1SJohn Birrell fbt_typoff_init(linker_ctf_t *lc)
54491eaf3e1SJohn Birrell {
54591eaf3e1SJohn Birrell const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
546d9175438SMark Johnston const void *tbuf, *tend, *tp;
54791eaf3e1SJohn Birrell const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
548d9175438SMark Johnston size_t idwidth;
54991eaf3e1SJohn Birrell int ctf_typemax = 0;
55091eaf3e1SJohn Birrell uint32_t *xp;
55191eaf3e1SJohn Birrell ulong_t pop[CTF_K_MAX + 1] = { 0 };
5524d221f59SMark Johnston uint8_t version;
55391eaf3e1SJohn Birrell
55491eaf3e1SJohn Birrell /* Sanity check. */
55591eaf3e1SJohn Birrell if (hp->cth_magic != CTF_MAGIC)
55691eaf3e1SJohn Birrell return (EINVAL);
55791eaf3e1SJohn Birrell
5584d221f59SMark Johnston version = hp->cth_version;
559d9175438SMark Johnston idwidth = version == CTF_VERSION_2 ? 2 : 4;
5604d221f59SMark Johnston
561d9175438SMark Johnston tbuf = (const void *) (ctfdata + hp->cth_typeoff);
562d9175438SMark Johnston tend = (const void *) (ctfdata + hp->cth_stroff);
56391eaf3e1SJohn Birrell
56491eaf3e1SJohn Birrell /*
56591eaf3e1SJohn Birrell * We make two passes through the entire type section. In this first
56691eaf3e1SJohn Birrell * pass, we count the number of each type and the total number of types.
56791eaf3e1SJohn Birrell */
56891eaf3e1SJohn Birrell for (tp = tbuf; tp < tend; ctf_typemax++) {
569d9175438SMark Johnston uint_t kind, type, vlen;
57091eaf3e1SJohn Birrell ssize_t size, increment;
57191eaf3e1SJohn Birrell size_t vbytes;
57291eaf3e1SJohn Birrell
5734d221f59SMark Johnston (void) fbt_get_ctt_size(version, tp, &size, &increment);
574d9175438SMark Johnston fbt_get_ctt_info(version, tp, &kind, &vlen, NULL);
575d9175438SMark Johnston fbt_get_ctt_index(version, tp, NULL, &type, NULL);
57691eaf3e1SJohn Birrell
57791eaf3e1SJohn Birrell switch (kind) {
57891eaf3e1SJohn Birrell case CTF_K_INTEGER:
57991eaf3e1SJohn Birrell case CTF_K_FLOAT:
58091eaf3e1SJohn Birrell vbytes = sizeof (uint_t);
58191eaf3e1SJohn Birrell break;
58291eaf3e1SJohn Birrell case CTF_K_ARRAY:
583d9175438SMark Johnston if (version == CTF_VERSION_2)
584d9175438SMark Johnston vbytes = sizeof (struct ctf_array_v2);
585d9175438SMark Johnston else
586d9175438SMark Johnston vbytes = sizeof (struct ctf_array_v3);
58791eaf3e1SJohn Birrell break;
58891eaf3e1SJohn Birrell case CTF_K_FUNCTION:
589d9175438SMark Johnston vbytes = roundup2(idwidth * vlen, sizeof(uint32_t));
59091eaf3e1SJohn Birrell break;
59191eaf3e1SJohn Birrell case CTF_K_STRUCT:
59291eaf3e1SJohn Birrell case CTF_K_UNION:
593d9175438SMark Johnston if (version == CTF_VERSION_2) {
594d9175438SMark Johnston if (size < CTF_V2_LSTRUCT_THRESH)
595d9175438SMark Johnston vbytes =
596d9175438SMark Johnston sizeof (struct ctf_member_v2) * vlen;
5974d221f59SMark Johnston else
598d9175438SMark Johnston vbytes =
599d9175438SMark Johnston sizeof (struct ctf_lmember_v2) * vlen;
600d9175438SMark Johnston } else {
601d9175438SMark Johnston if (size < CTF_V3_LSTRUCT_THRESH)
602d9175438SMark Johnston vbytes =
603d9175438SMark Johnston sizeof (struct ctf_member_v3) * vlen;
604d9175438SMark Johnston else
605d9175438SMark Johnston vbytes =
606d9175438SMark Johnston sizeof (struct ctf_lmember_v3) * vlen;
607d9175438SMark Johnston }
60891eaf3e1SJohn Birrell break;
60991eaf3e1SJohn Birrell case CTF_K_ENUM:
61091eaf3e1SJohn Birrell vbytes = sizeof (ctf_enum_t) * vlen;
61191eaf3e1SJohn Birrell break;
61291eaf3e1SJohn Birrell case CTF_K_FORWARD:
61391eaf3e1SJohn Birrell /*
61491eaf3e1SJohn Birrell * For forward declarations, ctt_type is the CTF_K_*
61591eaf3e1SJohn Birrell * kind for the tag, so bump that population count too.
61691eaf3e1SJohn Birrell * If ctt_type is unknown, treat the tag as a struct.
61791eaf3e1SJohn Birrell */
618d9175438SMark Johnston if (type == CTF_K_UNKNOWN || type >= CTF_K_MAX)
61991eaf3e1SJohn Birrell pop[CTF_K_STRUCT]++;
62091eaf3e1SJohn Birrell else
621d9175438SMark Johnston pop[type]++;
62291eaf3e1SJohn Birrell /*FALLTHRU*/
62391eaf3e1SJohn Birrell case CTF_K_UNKNOWN:
62491eaf3e1SJohn Birrell vbytes = 0;
62591eaf3e1SJohn Birrell break;
62691eaf3e1SJohn Birrell case CTF_K_POINTER:
62791eaf3e1SJohn Birrell case CTF_K_TYPEDEF:
62891eaf3e1SJohn Birrell case CTF_K_VOLATILE:
62991eaf3e1SJohn Birrell case CTF_K_CONST:
63091eaf3e1SJohn Birrell case CTF_K_RESTRICT:
63191eaf3e1SJohn Birrell vbytes = 0;
63291eaf3e1SJohn Birrell break;
63391eaf3e1SJohn Birrell default:
63491eaf3e1SJohn Birrell printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind);
63591eaf3e1SJohn Birrell return (EIO);
63691eaf3e1SJohn Birrell }
637d9175438SMark Johnston tp = (const void *)((uintptr_t)tp + increment + vbytes);
63891eaf3e1SJohn Birrell pop[kind]++;
63991eaf3e1SJohn Birrell }
64091eaf3e1SJohn Birrell
641a47016e9SAndriy Gapon /* account for a sentinel value below */
642a47016e9SAndriy Gapon ctf_typemax++;
64391eaf3e1SJohn Birrell *lc->typlenp = ctf_typemax;
64491eaf3e1SJohn Birrell
645d258fd1dSMark Johnston xp = malloc(sizeof(uint32_t) * ctf_typemax, M_LINKER,
646d258fd1dSMark Johnston M_ZERO | M_WAITOK);
64791eaf3e1SJohn Birrell
64891eaf3e1SJohn Birrell *lc->typoffp = xp;
64991eaf3e1SJohn Birrell
65091eaf3e1SJohn Birrell /* type id 0 is used as a sentinel value */
65191eaf3e1SJohn Birrell *xp++ = 0;
65291eaf3e1SJohn Birrell
65391eaf3e1SJohn Birrell /*
65491eaf3e1SJohn Birrell * In the second pass, fill in the type offset.
65591eaf3e1SJohn Birrell */
65691eaf3e1SJohn Birrell for (tp = tbuf; tp < tend; xp++) {
65791eaf3e1SJohn Birrell ssize_t size, increment;
658d9175438SMark Johnston uint_t kind, vlen;
65991eaf3e1SJohn Birrell
66091eaf3e1SJohn Birrell size_t vbytes;
66191eaf3e1SJohn Birrell
6624d221f59SMark Johnston (void) fbt_get_ctt_size(version, tp, &size, &increment);
663d9175438SMark Johnston fbt_get_ctt_info(version, tp, &kind, &vlen, NULL);
66491eaf3e1SJohn Birrell
66591eaf3e1SJohn Birrell switch (kind) {
66691eaf3e1SJohn Birrell case CTF_K_INTEGER:
66791eaf3e1SJohn Birrell case CTF_K_FLOAT:
66891eaf3e1SJohn Birrell vbytes = sizeof (uint_t);
66991eaf3e1SJohn Birrell break;
67091eaf3e1SJohn Birrell case CTF_K_ARRAY:
671d9175438SMark Johnston if (version == CTF_VERSION_2)
672d9175438SMark Johnston vbytes = sizeof (struct ctf_array_v2);
673d9175438SMark Johnston else
674d9175438SMark Johnston vbytes = sizeof (struct ctf_array_v3);
67591eaf3e1SJohn Birrell break;
67691eaf3e1SJohn Birrell case CTF_K_FUNCTION:
677d9175438SMark Johnston vbytes = roundup2(idwidth * vlen, sizeof(uint32_t));
67891eaf3e1SJohn Birrell break;
67991eaf3e1SJohn Birrell case CTF_K_STRUCT:
68091eaf3e1SJohn Birrell case CTF_K_UNION:
681d9175438SMark Johnston if (version == CTF_VERSION_2) {
682d9175438SMark Johnston if (size < CTF_V2_LSTRUCT_THRESH)
683d9175438SMark Johnston vbytes =
684d9175438SMark Johnston sizeof (struct ctf_member_v2) * vlen;
6854d221f59SMark Johnston else
686d9175438SMark Johnston vbytes =
687d9175438SMark Johnston sizeof (struct ctf_lmember_v2) * vlen;
688d9175438SMark Johnston } else {
689d9175438SMark Johnston if (size < CTF_V3_LSTRUCT_THRESH)
690d9175438SMark Johnston vbytes =
691d9175438SMark Johnston sizeof (struct ctf_member_v3) * vlen;
692d9175438SMark Johnston else
693d9175438SMark Johnston vbytes =
694d9175438SMark Johnston sizeof (struct ctf_lmember_v3) * vlen;
695d9175438SMark Johnston }
69691eaf3e1SJohn Birrell break;
69791eaf3e1SJohn Birrell case CTF_K_ENUM:
69891eaf3e1SJohn Birrell vbytes = sizeof (ctf_enum_t) * vlen;
69991eaf3e1SJohn Birrell break;
70091eaf3e1SJohn Birrell case CTF_K_FORWARD:
70191eaf3e1SJohn Birrell case CTF_K_UNKNOWN:
70291eaf3e1SJohn Birrell vbytes = 0;
70391eaf3e1SJohn Birrell break;
70491eaf3e1SJohn Birrell case CTF_K_POINTER:
70591eaf3e1SJohn Birrell case CTF_K_TYPEDEF:
70691eaf3e1SJohn Birrell case CTF_K_VOLATILE:
70791eaf3e1SJohn Birrell case CTF_K_CONST:
70891eaf3e1SJohn Birrell case CTF_K_RESTRICT:
70991eaf3e1SJohn Birrell vbytes = 0;
71091eaf3e1SJohn Birrell break;
71191eaf3e1SJohn Birrell default:
71291eaf3e1SJohn Birrell printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind);
71391eaf3e1SJohn Birrell return (EIO);
71491eaf3e1SJohn Birrell }
71591eaf3e1SJohn Birrell *xp = (uint32_t)((uintptr_t) tp - (uintptr_t) ctfdata);
716d9175438SMark Johnston tp = (const void *)((uintptr_t)tp + increment + vbytes);
71791eaf3e1SJohn Birrell }
71891eaf3e1SJohn Birrell
71991eaf3e1SJohn Birrell return (0);
72091eaf3e1SJohn Birrell }
72191eaf3e1SJohn Birrell
72291eaf3e1SJohn Birrell /*
72391eaf3e1SJohn Birrell * CTF Declaration Stack
72491eaf3e1SJohn Birrell *
72591eaf3e1SJohn Birrell * In order to implement ctf_type_name(), we must convert a type graph back
72691eaf3e1SJohn Birrell * into a C type declaration. Unfortunately, a type graph represents a storage
72791eaf3e1SJohn Birrell * class ordering of the type whereas a type declaration must obey the C rules
72891eaf3e1SJohn Birrell * for operator precedence, and the two orderings are frequently in conflict.
72991eaf3e1SJohn Birrell * For example, consider these CTF type graphs and their C declarations:
73091eaf3e1SJohn Birrell *
73191eaf3e1SJohn Birrell * CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER : int (*)()
73291eaf3e1SJohn Birrell * CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER : int (*)[]
73391eaf3e1SJohn Birrell *
73491eaf3e1SJohn Birrell * In each case, parentheses are used to raise operator * to higher lexical
73591eaf3e1SJohn Birrell * precedence, so the string form of the C declaration cannot be constructed by
73691eaf3e1SJohn Birrell * walking the type graph links and forming the string from left to right.
73791eaf3e1SJohn Birrell *
73891eaf3e1SJohn Birrell * The functions in this file build a set of stacks from the type graph nodes
73991eaf3e1SJohn Birrell * corresponding to the C operator precedence levels in the appropriate order.
74091eaf3e1SJohn Birrell * The code in ctf_type_name() can then iterate over the levels and nodes in
74191eaf3e1SJohn Birrell * lexical precedence order and construct the final C declaration string.
74291eaf3e1SJohn Birrell */
74391eaf3e1SJohn Birrell typedef struct ctf_list {
74491eaf3e1SJohn Birrell struct ctf_list *l_prev; /* previous pointer or tail pointer */
74591eaf3e1SJohn Birrell struct ctf_list *l_next; /* next pointer or head pointer */
74691eaf3e1SJohn Birrell } ctf_list_t;
74791eaf3e1SJohn Birrell
74891eaf3e1SJohn Birrell #define ctf_list_prev(elem) ((void *)(((ctf_list_t *)(elem))->l_prev))
74991eaf3e1SJohn Birrell #define ctf_list_next(elem) ((void *)(((ctf_list_t *)(elem))->l_next))
75091eaf3e1SJohn Birrell
75191eaf3e1SJohn Birrell typedef enum {
75291eaf3e1SJohn Birrell CTF_PREC_BASE,
75391eaf3e1SJohn Birrell CTF_PREC_POINTER,
75491eaf3e1SJohn Birrell CTF_PREC_ARRAY,
75591eaf3e1SJohn Birrell CTF_PREC_FUNCTION,
75691eaf3e1SJohn Birrell CTF_PREC_MAX
75791eaf3e1SJohn Birrell } ctf_decl_prec_t;
75891eaf3e1SJohn Birrell
75991eaf3e1SJohn Birrell typedef struct ctf_decl_node {
76091eaf3e1SJohn Birrell ctf_list_t cd_list; /* linked list pointers */
76191eaf3e1SJohn Birrell ctf_id_t cd_type; /* type identifier */
76291eaf3e1SJohn Birrell uint_t cd_kind; /* type kind */
76391eaf3e1SJohn Birrell uint_t cd_n; /* type dimension if array */
76491eaf3e1SJohn Birrell } ctf_decl_node_t;
76591eaf3e1SJohn Birrell
76691eaf3e1SJohn Birrell typedef struct ctf_decl {
76791eaf3e1SJohn Birrell ctf_list_t cd_nodes[CTF_PREC_MAX]; /* declaration node stacks */
76891eaf3e1SJohn Birrell int cd_order[CTF_PREC_MAX]; /* storage order of decls */
76991eaf3e1SJohn Birrell ctf_decl_prec_t cd_qualp; /* qualifier precision */
77091eaf3e1SJohn Birrell ctf_decl_prec_t cd_ordp; /* ordered precision */
77191eaf3e1SJohn Birrell char *cd_buf; /* buffer for output */
77291eaf3e1SJohn Birrell char *cd_ptr; /* buffer location */
77391eaf3e1SJohn Birrell char *cd_end; /* buffer limit */
77491eaf3e1SJohn Birrell size_t cd_len; /* buffer space required */
77591eaf3e1SJohn Birrell int cd_err; /* saved error value */
77691eaf3e1SJohn Birrell } ctf_decl_t;
77791eaf3e1SJohn Birrell
77891eaf3e1SJohn Birrell /*
77991eaf3e1SJohn Birrell * Simple doubly-linked list append routine. This implementation assumes that
78091eaf3e1SJohn Birrell * each list element contains an embedded ctf_list_t as the first member.
78191eaf3e1SJohn Birrell * An additional ctf_list_t is used to store the head (l_next) and tail
78291eaf3e1SJohn Birrell * (l_prev) pointers. The current head and tail list elements have their
78391eaf3e1SJohn Birrell * previous and next pointers set to NULL, respectively.
78491eaf3e1SJohn Birrell */
78591eaf3e1SJohn Birrell static void
ctf_list_append(ctf_list_t * lp,void * new)78691eaf3e1SJohn Birrell ctf_list_append(ctf_list_t *lp, void *new)
78791eaf3e1SJohn Birrell {
78891eaf3e1SJohn Birrell ctf_list_t *p = lp->l_prev; /* p = tail list element */
78991eaf3e1SJohn Birrell ctf_list_t *q = new; /* q = new list element */
79091eaf3e1SJohn Birrell
79191eaf3e1SJohn Birrell lp->l_prev = q;
79291eaf3e1SJohn Birrell q->l_prev = p;
79391eaf3e1SJohn Birrell q->l_next = NULL;
79491eaf3e1SJohn Birrell
79591eaf3e1SJohn Birrell if (p != NULL)
79691eaf3e1SJohn Birrell p->l_next = q;
79791eaf3e1SJohn Birrell else
79891eaf3e1SJohn Birrell lp->l_next = q;
79991eaf3e1SJohn Birrell }
80091eaf3e1SJohn Birrell
80191eaf3e1SJohn Birrell /*
80291eaf3e1SJohn Birrell * Prepend the specified existing element to the given ctf_list_t. The
80391eaf3e1SJohn Birrell * existing pointer should be pointing at a struct with embedded ctf_list_t.
80491eaf3e1SJohn Birrell */
80591eaf3e1SJohn Birrell static void
ctf_list_prepend(ctf_list_t * lp,void * new)80691eaf3e1SJohn Birrell ctf_list_prepend(ctf_list_t *lp, void *new)
80791eaf3e1SJohn Birrell {
80891eaf3e1SJohn Birrell ctf_list_t *p = new; /* p = new list element */
80991eaf3e1SJohn Birrell ctf_list_t *q = lp->l_next; /* q = head list element */
81091eaf3e1SJohn Birrell
81191eaf3e1SJohn Birrell lp->l_next = p;
81291eaf3e1SJohn Birrell p->l_prev = NULL;
81391eaf3e1SJohn Birrell p->l_next = q;
81491eaf3e1SJohn Birrell
81591eaf3e1SJohn Birrell if (q != NULL)
81691eaf3e1SJohn Birrell q->l_prev = p;
81791eaf3e1SJohn Birrell else
81891eaf3e1SJohn Birrell lp->l_prev = p;
81991eaf3e1SJohn Birrell }
82091eaf3e1SJohn Birrell
82191eaf3e1SJohn Birrell static void
ctf_decl_init(ctf_decl_t * cd,char * buf,size_t len)82291eaf3e1SJohn Birrell ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len)
82391eaf3e1SJohn Birrell {
82491eaf3e1SJohn Birrell int i;
82591eaf3e1SJohn Birrell
82691eaf3e1SJohn Birrell bzero(cd, sizeof (ctf_decl_t));
82791eaf3e1SJohn Birrell
82891eaf3e1SJohn Birrell for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++)
82991eaf3e1SJohn Birrell cd->cd_order[i] = CTF_PREC_BASE - 1;
83091eaf3e1SJohn Birrell
83191eaf3e1SJohn Birrell cd->cd_qualp = CTF_PREC_BASE;
83291eaf3e1SJohn Birrell cd->cd_ordp = CTF_PREC_BASE;
83391eaf3e1SJohn Birrell
83491eaf3e1SJohn Birrell cd->cd_buf = buf;
83591eaf3e1SJohn Birrell cd->cd_ptr = buf;
83691eaf3e1SJohn Birrell cd->cd_end = buf + len;
83791eaf3e1SJohn Birrell }
83891eaf3e1SJohn Birrell
83991eaf3e1SJohn Birrell static void
ctf_decl_fini(ctf_decl_t * cd)84091eaf3e1SJohn Birrell ctf_decl_fini(ctf_decl_t *cd)
84191eaf3e1SJohn Birrell {
84291eaf3e1SJohn Birrell ctf_decl_node_t *cdp, *ndp;
84391eaf3e1SJohn Birrell int i;
84491eaf3e1SJohn Birrell
84591eaf3e1SJohn Birrell for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) {
84691eaf3e1SJohn Birrell for (cdp = ctf_list_next(&cd->cd_nodes[i]);
84791eaf3e1SJohn Birrell cdp != NULL; cdp = ndp) {
84891eaf3e1SJohn Birrell ndp = ctf_list_next(cdp);
84991eaf3e1SJohn Birrell free(cdp, M_FBT);
85091eaf3e1SJohn Birrell }
85191eaf3e1SJohn Birrell }
85291eaf3e1SJohn Birrell }
85391eaf3e1SJohn Birrell
854d9175438SMark Johnston static const void *
ctf_lookup_by_id(linker_ctf_t * lc,ctf_id_t type)85591eaf3e1SJohn Birrell ctf_lookup_by_id(linker_ctf_t *lc, ctf_id_t type)
85691eaf3e1SJohn Birrell {
857d9175438SMark Johnston const void *tp;
85891eaf3e1SJohn Birrell uint32_t offset;
85991eaf3e1SJohn Birrell uint32_t *typoff = *lc->typoffp;
86091eaf3e1SJohn Birrell
86191eaf3e1SJohn Birrell if (type >= *lc->typlenp) {
86291eaf3e1SJohn Birrell printf("%s(%d): type %d exceeds max %ld\n",__func__,__LINE__,(int) type,*lc->typlenp);
86391eaf3e1SJohn Birrell return(NULL);
86491eaf3e1SJohn Birrell }
86591eaf3e1SJohn Birrell
86691eaf3e1SJohn Birrell /* Check if the type isn't cross-referenced. */
86791eaf3e1SJohn Birrell if ((offset = typoff[type]) == 0) {
86891eaf3e1SJohn Birrell printf("%s(%d): type %d isn't cross referenced\n",__func__,__LINE__, (int) type);
86991eaf3e1SJohn Birrell return(NULL);
87091eaf3e1SJohn Birrell }
87191eaf3e1SJohn Birrell
872d9175438SMark Johnston tp = (const void *) (lc->ctftab + offset + sizeof(ctf_header_t));
87391eaf3e1SJohn Birrell
87491eaf3e1SJohn Birrell return (tp);
87591eaf3e1SJohn Birrell }
87691eaf3e1SJohn Birrell
87791eaf3e1SJohn Birrell static void
fbt_array_info(linker_ctf_t * lc,ctf_id_t type,ctf_arinfo_t * arp)87891eaf3e1SJohn Birrell fbt_array_info(linker_ctf_t *lc, ctf_id_t type, ctf_arinfo_t *arp)
87991eaf3e1SJohn Birrell {
88091eaf3e1SJohn Birrell const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
881d9175438SMark Johnston const void *tp;
88291eaf3e1SJohn Birrell ssize_t increment;
883d9175438SMark Johnston uint_t kind;
88491eaf3e1SJohn Birrell
88591eaf3e1SJohn Birrell bzero(arp, sizeof(*arp));
88691eaf3e1SJohn Birrell
88791eaf3e1SJohn Birrell if ((tp = ctf_lookup_by_id(lc, type)) == NULL)
88891eaf3e1SJohn Birrell return;
88991eaf3e1SJohn Birrell
890d9175438SMark Johnston fbt_get_ctt_info(hp->cth_version, tp, &kind, NULL, NULL);
891d9175438SMark Johnston if (kind != CTF_K_ARRAY)
89291eaf3e1SJohn Birrell return;
89391eaf3e1SJohn Birrell
89491eaf3e1SJohn Birrell (void) fbt_get_ctt_size(hp->cth_version, tp, NULL, &increment);
89591eaf3e1SJohn Birrell
896d9175438SMark Johnston if (hp->cth_version == CTF_VERSION_2) {
897d9175438SMark Johnston const struct ctf_array_v2 *ap;
898d9175438SMark Johnston
899d9175438SMark Johnston ap = (const struct ctf_array_v2 *)((uintptr_t)tp + increment);
90091eaf3e1SJohn Birrell arp->ctr_contents = ap->cta_contents;
90191eaf3e1SJohn Birrell arp->ctr_index = ap->cta_index;
90291eaf3e1SJohn Birrell arp->ctr_nelems = ap->cta_nelems;
903d9175438SMark Johnston } else {
904d9175438SMark Johnston const struct ctf_array_v3 *ap;
905d9175438SMark Johnston
906d9175438SMark Johnston ap = (const struct ctf_array_v3 *)((uintptr_t)tp + increment);
907d9175438SMark Johnston arp->ctr_contents = ap->cta_contents;
908d9175438SMark Johnston arp->ctr_index = ap->cta_index;
909d9175438SMark Johnston arp->ctr_nelems = ap->cta_nelems;
910d9175438SMark Johnston }
91191eaf3e1SJohn Birrell }
91291eaf3e1SJohn Birrell
91391eaf3e1SJohn Birrell static const char *
ctf_strptr(linker_ctf_t * lc,int name)91491eaf3e1SJohn Birrell ctf_strptr(linker_ctf_t *lc, int name)
91591eaf3e1SJohn Birrell {
916b1a217a3SEd Maste const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
91791eaf3e1SJohn Birrell const char *strp = "";
91891eaf3e1SJohn Birrell
91991eaf3e1SJohn Birrell if (name < 0 || name >= hp->cth_strlen)
92091eaf3e1SJohn Birrell return(strp);
92191eaf3e1SJohn Birrell
92291eaf3e1SJohn Birrell strp = (const char *)(lc->ctftab + hp->cth_stroff + name + sizeof(ctf_header_t));
92391eaf3e1SJohn Birrell
92491eaf3e1SJohn Birrell return (strp);
92591eaf3e1SJohn Birrell }
92691eaf3e1SJohn Birrell
927d9175438SMark Johnston static const char *
ctf_type_rname(linker_ctf_t * lc,const void * v)928d9175438SMark Johnston ctf_type_rname(linker_ctf_t *lc, const void *v)
929d9175438SMark Johnston {
930d9175438SMark Johnston const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
931d9175438SMark Johnston uint_t name;
932d9175438SMark Johnston
933d9175438SMark Johnston if (hp->cth_version == CTF_VERSION_2) {
934d9175438SMark Johnston const struct ctf_type_v2 *ctt = v;
935d9175438SMark Johnston
936d9175438SMark Johnston name = ctt->ctt_name;
937d9175438SMark Johnston } else {
938d9175438SMark Johnston const struct ctf_type_v3 *ctt = v;
939d9175438SMark Johnston
940d9175438SMark Johnston name = ctt->ctt_name;
941d9175438SMark Johnston }
942d9175438SMark Johnston
943d9175438SMark Johnston return (ctf_strptr(lc, name));
944d9175438SMark Johnston }
945d9175438SMark Johnston
94691eaf3e1SJohn Birrell static void
ctf_decl_push(ctf_decl_t * cd,linker_ctf_t * lc,ctf_id_t type)94791eaf3e1SJohn Birrell ctf_decl_push(ctf_decl_t *cd, linker_ctf_t *lc, ctf_id_t type)
94891eaf3e1SJohn Birrell {
949d9175438SMark Johnston const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
95091eaf3e1SJohn Birrell ctf_decl_node_t *cdp;
95191eaf3e1SJohn Birrell ctf_decl_prec_t prec;
952d9175438SMark Johnston uint_t kind, n = 1, t;
95391eaf3e1SJohn Birrell int is_qual = 0;
95491eaf3e1SJohn Birrell
955d9175438SMark Johnston const void *tp;
95691eaf3e1SJohn Birrell ctf_arinfo_t ar;
95791eaf3e1SJohn Birrell
95891eaf3e1SJohn Birrell if ((tp = ctf_lookup_by_id(lc, type)) == NULL) {
95991eaf3e1SJohn Birrell cd->cd_err = ENOENT;
96091eaf3e1SJohn Birrell return;
96191eaf3e1SJohn Birrell }
96291eaf3e1SJohn Birrell
963d9175438SMark Johnston fbt_get_ctt_info(hp->cth_version, tp, &kind, NULL, NULL);
964d9175438SMark Johnston fbt_get_ctt_index(hp->cth_version, tp, NULL, &t, NULL);
965d9175438SMark Johnston
966d9175438SMark Johnston switch (kind) {
96791eaf3e1SJohn Birrell case CTF_K_ARRAY:
96891eaf3e1SJohn Birrell fbt_array_info(lc, type, &ar);
96991eaf3e1SJohn Birrell ctf_decl_push(cd, lc, ar.ctr_contents);
97091eaf3e1SJohn Birrell n = ar.ctr_nelems;
97191eaf3e1SJohn Birrell prec = CTF_PREC_ARRAY;
97291eaf3e1SJohn Birrell break;
97391eaf3e1SJohn Birrell
97491eaf3e1SJohn Birrell case CTF_K_TYPEDEF:
975d9175438SMark Johnston if (ctf_type_rname(lc, tp)[0] == '\0') {
976d9175438SMark Johnston ctf_decl_push(cd, lc, t);
97791eaf3e1SJohn Birrell return;
97891eaf3e1SJohn Birrell }
97991eaf3e1SJohn Birrell prec = CTF_PREC_BASE;
98091eaf3e1SJohn Birrell break;
98191eaf3e1SJohn Birrell
98291eaf3e1SJohn Birrell case CTF_K_FUNCTION:
983d9175438SMark Johnston ctf_decl_push(cd, lc, t);
98491eaf3e1SJohn Birrell prec = CTF_PREC_FUNCTION;
98591eaf3e1SJohn Birrell break;
98691eaf3e1SJohn Birrell
98791eaf3e1SJohn Birrell case CTF_K_POINTER:
988d9175438SMark Johnston ctf_decl_push(cd, lc, t);
98991eaf3e1SJohn Birrell prec = CTF_PREC_POINTER;
99091eaf3e1SJohn Birrell break;
99191eaf3e1SJohn Birrell
99291eaf3e1SJohn Birrell case CTF_K_VOLATILE:
99391eaf3e1SJohn Birrell case CTF_K_CONST:
99491eaf3e1SJohn Birrell case CTF_K_RESTRICT:
995d9175438SMark Johnston ctf_decl_push(cd, lc, t);
99691eaf3e1SJohn Birrell prec = cd->cd_qualp;
99791eaf3e1SJohn Birrell is_qual++;
99891eaf3e1SJohn Birrell break;
99991eaf3e1SJohn Birrell
100091eaf3e1SJohn Birrell default:
100191eaf3e1SJohn Birrell prec = CTF_PREC_BASE;
100291eaf3e1SJohn Birrell }
100391eaf3e1SJohn Birrell
1004d258fd1dSMark Johnston cdp = malloc(sizeof(*cdp), M_FBT, M_WAITOK);
100591eaf3e1SJohn Birrell cdp->cd_type = type;
100691eaf3e1SJohn Birrell cdp->cd_kind = kind;
100791eaf3e1SJohn Birrell cdp->cd_n = n;
100891eaf3e1SJohn Birrell
100991eaf3e1SJohn Birrell if (ctf_list_next(&cd->cd_nodes[prec]) == NULL)
101091eaf3e1SJohn Birrell cd->cd_order[prec] = cd->cd_ordp++;
101191eaf3e1SJohn Birrell
101291eaf3e1SJohn Birrell /*
101391eaf3e1SJohn Birrell * Reset cd_qualp to the highest precedence level that we've seen so
101491eaf3e1SJohn Birrell * far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER).
101591eaf3e1SJohn Birrell */
101691eaf3e1SJohn Birrell if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY)
101791eaf3e1SJohn Birrell cd->cd_qualp = prec;
101891eaf3e1SJohn Birrell
101991eaf3e1SJohn Birrell /*
102091eaf3e1SJohn Birrell * C array declarators are ordered inside out so prepend them. Also by
102191eaf3e1SJohn Birrell * convention qualifiers of base types precede the type specifier (e.g.
102291eaf3e1SJohn Birrell * const int vs. int const) even though the two forms are equivalent.
102391eaf3e1SJohn Birrell */
102491eaf3e1SJohn Birrell if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE))
102591eaf3e1SJohn Birrell ctf_list_prepend(&cd->cd_nodes[prec], cdp);
102691eaf3e1SJohn Birrell else
102791eaf3e1SJohn Birrell ctf_list_append(&cd->cd_nodes[prec], cdp);
102891eaf3e1SJohn Birrell }
102991eaf3e1SJohn Birrell
103091eaf3e1SJohn Birrell static void
ctf_decl_sprintf(ctf_decl_t * cd,const char * format,...)103191eaf3e1SJohn Birrell ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...)
103291eaf3e1SJohn Birrell {
103391eaf3e1SJohn Birrell size_t len = (size_t)(cd->cd_end - cd->cd_ptr);
103491eaf3e1SJohn Birrell va_list ap;
103591eaf3e1SJohn Birrell size_t n;
103691eaf3e1SJohn Birrell
103791eaf3e1SJohn Birrell va_start(ap, format);
103891eaf3e1SJohn Birrell n = vsnprintf(cd->cd_ptr, len, format, ap);
103991eaf3e1SJohn Birrell va_end(ap);
104091eaf3e1SJohn Birrell
104191eaf3e1SJohn Birrell cd->cd_ptr += MIN(n, len);
104291eaf3e1SJohn Birrell cd->cd_len += n;
104391eaf3e1SJohn Birrell }
104491eaf3e1SJohn Birrell
104591eaf3e1SJohn Birrell static ssize_t
fbt_type_name(linker_ctf_t * lc,ctf_id_t type,char * buf,size_t len)104691eaf3e1SJohn Birrell fbt_type_name(linker_ctf_t *lc, ctf_id_t type, char *buf, size_t len)
104791eaf3e1SJohn Birrell {
104891eaf3e1SJohn Birrell ctf_decl_t cd;
104991eaf3e1SJohn Birrell ctf_decl_node_t *cdp;
105091eaf3e1SJohn Birrell ctf_decl_prec_t prec, lp, rp;
105191eaf3e1SJohn Birrell int ptr, arr;
105291eaf3e1SJohn Birrell uint_t k;
105391eaf3e1SJohn Birrell
105491eaf3e1SJohn Birrell if (lc == NULL && type == CTF_ERR)
105591eaf3e1SJohn Birrell return (-1); /* simplify caller code by permitting CTF_ERR */
105691eaf3e1SJohn Birrell
105791eaf3e1SJohn Birrell ctf_decl_init(&cd, buf, len);
105891eaf3e1SJohn Birrell ctf_decl_push(&cd, lc, type);
105991eaf3e1SJohn Birrell
106091eaf3e1SJohn Birrell if (cd.cd_err != 0) {
106191eaf3e1SJohn Birrell ctf_decl_fini(&cd);
106291eaf3e1SJohn Birrell return (-1);
106391eaf3e1SJohn Birrell }
106491eaf3e1SJohn Birrell
106591eaf3e1SJohn Birrell /*
106691eaf3e1SJohn Birrell * If the type graph's order conflicts with lexical precedence order
106791eaf3e1SJohn Birrell * for pointers or arrays, then we need to surround the declarations at
106891eaf3e1SJohn Birrell * the corresponding lexical precedence with parentheses. This can
106991eaf3e1SJohn Birrell * result in either a parenthesized pointer (*) as in int (*)() or
107091eaf3e1SJohn Birrell * int (*)[], or in a parenthesized pointer and array as in int (*[])().
107191eaf3e1SJohn Birrell */
107291eaf3e1SJohn Birrell ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER;
107391eaf3e1SJohn Birrell arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY;
107491eaf3e1SJohn Birrell
107591eaf3e1SJohn Birrell rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1;
107691eaf3e1SJohn Birrell lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1;
107791eaf3e1SJohn Birrell
107891eaf3e1SJohn Birrell k = CTF_K_POINTER; /* avoid leading whitespace (see below) */
107991eaf3e1SJohn Birrell
108091eaf3e1SJohn Birrell for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) {
108191eaf3e1SJohn Birrell for (cdp = ctf_list_next(&cd.cd_nodes[prec]);
108291eaf3e1SJohn Birrell cdp != NULL; cdp = ctf_list_next(cdp)) {
108391eaf3e1SJohn Birrell
1084d9175438SMark Johnston const void *tp = ctf_lookup_by_id(lc, cdp->cd_type);
1085d9175438SMark Johnston const char *name = ctf_type_rname(lc, tp);
108691eaf3e1SJohn Birrell
108791eaf3e1SJohn Birrell if (k != CTF_K_POINTER && k != CTF_K_ARRAY)
108891eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, " ");
108991eaf3e1SJohn Birrell
109091eaf3e1SJohn Birrell if (lp == prec) {
109191eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "(");
109291eaf3e1SJohn Birrell lp = -1;
109391eaf3e1SJohn Birrell }
109491eaf3e1SJohn Birrell
109591eaf3e1SJohn Birrell switch (cdp->cd_kind) {
109691eaf3e1SJohn Birrell case CTF_K_INTEGER:
109791eaf3e1SJohn Birrell case CTF_K_FLOAT:
109891eaf3e1SJohn Birrell case CTF_K_TYPEDEF:
109991eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "%s", name);
110091eaf3e1SJohn Birrell break;
110191eaf3e1SJohn Birrell case CTF_K_POINTER:
110291eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "*");
110391eaf3e1SJohn Birrell break;
110491eaf3e1SJohn Birrell case CTF_K_ARRAY:
110591eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n);
110691eaf3e1SJohn Birrell break;
110791eaf3e1SJohn Birrell case CTF_K_FUNCTION:
110891eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "()");
110991eaf3e1SJohn Birrell break;
111091eaf3e1SJohn Birrell case CTF_K_STRUCT:
111191eaf3e1SJohn Birrell case CTF_K_FORWARD:
111291eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "struct %s", name);
111391eaf3e1SJohn Birrell break;
111491eaf3e1SJohn Birrell case CTF_K_UNION:
111591eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "union %s", name);
111691eaf3e1SJohn Birrell break;
111791eaf3e1SJohn Birrell case CTF_K_ENUM:
111891eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "enum %s", name);
111991eaf3e1SJohn Birrell break;
112091eaf3e1SJohn Birrell case CTF_K_VOLATILE:
112191eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "volatile");
112291eaf3e1SJohn Birrell break;
112391eaf3e1SJohn Birrell case CTF_K_CONST:
112491eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "const");
112591eaf3e1SJohn Birrell break;
112691eaf3e1SJohn Birrell case CTF_K_RESTRICT:
112791eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, "restrict");
112891eaf3e1SJohn Birrell break;
112991eaf3e1SJohn Birrell }
113091eaf3e1SJohn Birrell
113191eaf3e1SJohn Birrell k = cdp->cd_kind;
113291eaf3e1SJohn Birrell }
113391eaf3e1SJohn Birrell
113491eaf3e1SJohn Birrell if (rp == prec)
113591eaf3e1SJohn Birrell ctf_decl_sprintf(&cd, ")");
113691eaf3e1SJohn Birrell }
113791eaf3e1SJohn Birrell
113891eaf3e1SJohn Birrell ctf_decl_fini(&cd);
113991eaf3e1SJohn Birrell return (cd.cd_len);
114091eaf3e1SJohn Birrell }
114191eaf3e1SJohn Birrell
114291eaf3e1SJohn Birrell static void
fbt_getargdesc(void * arg __unused,dtrace_id_t id __unused,void * parg,dtrace_argdesc_t * desc)114391eaf3e1SJohn Birrell fbt_getargdesc(void *arg __unused, dtrace_id_t id __unused, void *parg, dtrace_argdesc_t *desc)
114491eaf3e1SJohn Birrell {
1145d9175438SMark Johnston const ctf_header_t *hp;
1146d9175438SMark Johnston const char *dp;
114791eaf3e1SJohn Birrell fbt_probe_t *fbt = parg;
114891eaf3e1SJohn Birrell linker_ctf_t lc;
114991eaf3e1SJohn Birrell modctl_t *ctl = fbt->fbtp_ctl;
1150d9175438SMark Johnston size_t idwidth;
115191eaf3e1SJohn Birrell int ndx = desc->dtargd_ndx;
115291eaf3e1SJohn Birrell int symindx = fbt->fbtp_symindx;
115391eaf3e1SJohn Birrell uint32_t *ctfoff;
1154d9175438SMark Johnston uint32_t offset, type;
1155d9175438SMark Johnston uint_t info, n;
1156d9175438SMark Johnston ushort_t kind;
115791eaf3e1SJohn Birrell
1158aaf2546bSAndriy Gapon if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
1159aaf2546bSAndriy Gapon (void) strcpy(desc->dtargd_native, "int");
1160aaf2546bSAndriy Gapon return;
1161aaf2546bSAndriy Gapon }
1162aaf2546bSAndriy Gapon
116391eaf3e1SJohn Birrell desc->dtargd_ndx = DTRACE_ARGNONE;
116491eaf3e1SJohn Birrell
116591eaf3e1SJohn Birrell /* Get a pointer to the CTF data and it's length. */
116691eaf3e1SJohn Birrell if (linker_ctf_get(ctl, &lc) != 0)
116791eaf3e1SJohn Birrell /* No CTF data? Something wrong? *shrug* */
116891eaf3e1SJohn Birrell return;
116991eaf3e1SJohn Birrell
117091eaf3e1SJohn Birrell /* Check if this module hasn't been initialised yet. */
117191eaf3e1SJohn Birrell if (*lc.ctfoffp == NULL) {
117291eaf3e1SJohn Birrell /*
117391eaf3e1SJohn Birrell * Initialise the CTF object and function symindx to
117491eaf3e1SJohn Birrell * byte offset array.
117591eaf3e1SJohn Birrell */
117691eaf3e1SJohn Birrell if (fbt_ctfoff_init(ctl, &lc) != 0)
117791eaf3e1SJohn Birrell return;
117891eaf3e1SJohn Birrell
117991eaf3e1SJohn Birrell /* Initialise the CTF type to byte offset array. */
118091eaf3e1SJohn Birrell if (fbt_typoff_init(&lc) != 0)
118191eaf3e1SJohn Birrell return;
118291eaf3e1SJohn Birrell }
118391eaf3e1SJohn Birrell
118491eaf3e1SJohn Birrell ctfoff = *lc.ctfoffp;
118591eaf3e1SJohn Birrell
118691eaf3e1SJohn Birrell if (ctfoff == NULL || *lc.typoffp == NULL)
118791eaf3e1SJohn Birrell return;
118891eaf3e1SJohn Birrell
118991eaf3e1SJohn Birrell /* Check if the symbol index is out of range. */
119091eaf3e1SJohn Birrell if (symindx >= lc.nsym)
119191eaf3e1SJohn Birrell return;
119291eaf3e1SJohn Birrell
119391eaf3e1SJohn Birrell /* Check if the symbol isn't cross-referenced. */
119491eaf3e1SJohn Birrell if ((offset = ctfoff[symindx]) == 0xffffffff)
119591eaf3e1SJohn Birrell return;
119691eaf3e1SJohn Birrell
1197d9175438SMark Johnston hp = (const ctf_header_t *) lc.ctftab;
1198d9175438SMark Johnston idwidth = hp->cth_version == CTF_VERSION_2 ? 2 : 4;
1199d9175438SMark Johnston dp = (const char *)(lc.ctftab + offset + sizeof(ctf_header_t));
120091eaf3e1SJohn Birrell
1201d9175438SMark Johnston info = 0;
1202d9175438SMark Johnston memcpy(&info, dp, idwidth);
1203d9175438SMark Johnston dp += idwidth;
1204d9175438SMark Johnston if (hp->cth_version == CTF_VERSION_2) {
1205d9175438SMark Johnston kind = CTF_V2_INFO_KIND(info);
1206d9175438SMark Johnston n = CTF_V2_INFO_VLEN(info);
1207d9175438SMark Johnston } else {
1208d9175438SMark Johnston kind = CTF_V3_INFO_KIND(info);
1209d9175438SMark Johnston n = CTF_V3_INFO_VLEN(info);
1210d9175438SMark Johnston }
121191eaf3e1SJohn Birrell
121291eaf3e1SJohn Birrell if (kind == CTF_K_UNKNOWN && n == 0) {
121391eaf3e1SJohn Birrell printf("%s(%d): Unknown function!\n",__func__,__LINE__);
121491eaf3e1SJohn Birrell return;
121591eaf3e1SJohn Birrell }
121691eaf3e1SJohn Birrell
121791eaf3e1SJohn Birrell if (kind != CTF_K_FUNCTION) {
121891eaf3e1SJohn Birrell printf("%s(%d): Expected a function!\n",__func__,__LINE__);
121991eaf3e1SJohn Birrell return;
122091eaf3e1SJohn Birrell }
122191eaf3e1SJohn Birrell
1222aaf2546bSAndriy Gapon if (fbt->fbtp_roffset != 0) {
1223aaf2546bSAndriy Gapon /* Only return type is available for args[1] in return probe. */
1224aaf2546bSAndriy Gapon if (ndx > 1)
1225aaf2546bSAndriy Gapon return;
1226aaf2546bSAndriy Gapon ASSERT(ndx == 1);
1227aaf2546bSAndriy Gapon } else {
122891eaf3e1SJohn Birrell /* Check if the requested argument doesn't exist. */
122991eaf3e1SJohn Birrell if (ndx >= n)
123091eaf3e1SJohn Birrell return;
123191eaf3e1SJohn Birrell
123291eaf3e1SJohn Birrell /* Skip the return type and arguments up to the one requested. */
1233d9175438SMark Johnston dp += idwidth * (ndx + 1);
1234aaf2546bSAndriy Gapon }
123591eaf3e1SJohn Birrell
1236d9175438SMark Johnston type = 0;
1237d9175438SMark Johnston memcpy(&type, dp, idwidth);
1238d9175438SMark Johnston if (fbt_type_name(&lc, type, desc->dtargd_native, sizeof(desc->dtargd_native)) > 0)
123991eaf3e1SJohn Birrell desc->dtargd_ndx = ndx;
124091eaf3e1SJohn Birrell }
124191eaf3e1SJohn Birrell
12428776669bSMark Johnston static int
fbt_linker_file_cb(linker_file_t lf,void * arg)12438776669bSMark Johnston fbt_linker_file_cb(linker_file_t lf, void *arg)
12448776669bSMark Johnston {
12458776669bSMark Johnston
12468776669bSMark Johnston fbt_provide_module(arg, lf);
12478776669bSMark Johnston
12488776669bSMark Johnston return (0);
12498776669bSMark Johnston }
12508776669bSMark Johnston
125191eaf3e1SJohn Birrell static void
fbt_load(void * dummy)125291eaf3e1SJohn Birrell fbt_load(void *dummy)
125391eaf3e1SJohn Birrell {
125491eaf3e1SJohn Birrell /* Default the probe table size if not specified. */
125591eaf3e1SJohn Birrell if (fbt_probetab_size == 0)
125691eaf3e1SJohn Birrell fbt_probetab_size = FBT_PROBETAB_SIZE;
125791eaf3e1SJohn Birrell
125891eaf3e1SJohn Birrell /* Choose the hash mask for the probe table. */
125991eaf3e1SJohn Birrell fbt_probetab_mask = fbt_probetab_size - 1;
126091eaf3e1SJohn Birrell
126191eaf3e1SJohn Birrell /* Allocate memory for the probe table. */
126291eaf3e1SJohn Birrell fbt_probetab =
126391eaf3e1SJohn Birrell malloc(fbt_probetab_size * sizeof (fbt_probe_t *), M_FBT, M_WAITOK | M_ZERO);
126491eaf3e1SJohn Birrell
126591eaf3e1SJohn Birrell dtrace_doubletrap_func = fbt_doubletrap;
126691eaf3e1SJohn Birrell dtrace_invop_add(fbt_invop);
126791eaf3e1SJohn Birrell
126891eaf3e1SJohn Birrell if (dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_USER,
126991eaf3e1SJohn Birrell NULL, &fbt_pops, NULL, &fbt_id) != 0)
127091eaf3e1SJohn Birrell return;
127191eaf3e1SJohn Birrell
12728776669bSMark Johnston /* Create probes for the kernel and already-loaded modules. */
12738776669bSMark Johnston linker_file_foreach(fbt_linker_file_cb, NULL);
12748776669bSMark Johnston }
127591eaf3e1SJohn Birrell
127691eaf3e1SJohn Birrell static int
fbt_unload(void)12776339314cSDimitry Andric fbt_unload(void)
127891eaf3e1SJohn Birrell {
127991eaf3e1SJohn Birrell int error = 0;
128091eaf3e1SJohn Birrell
128191eaf3e1SJohn Birrell /* De-register the invalid opcode handler. */
128291eaf3e1SJohn Birrell dtrace_invop_remove(fbt_invop);
128391eaf3e1SJohn Birrell
128491eaf3e1SJohn Birrell dtrace_doubletrap_func = NULL;
128591eaf3e1SJohn Birrell
128691eaf3e1SJohn Birrell /* De-register this DTrace provider. */
128791eaf3e1SJohn Birrell if ((error = dtrace_unregister(fbt_id)) != 0)
128891eaf3e1SJohn Birrell return (error);
128991eaf3e1SJohn Birrell
129091eaf3e1SJohn Birrell /* Free the probe table. */
129191eaf3e1SJohn Birrell free(fbt_probetab, M_FBT);
129291eaf3e1SJohn Birrell fbt_probetab = NULL;
129391eaf3e1SJohn Birrell fbt_probetab_mask = 0;
129491eaf3e1SJohn Birrell
129591eaf3e1SJohn Birrell return (error);
129691eaf3e1SJohn Birrell }
129791eaf3e1SJohn Birrell
129891eaf3e1SJohn Birrell static int
fbt_modevent(module_t mod __unused,int type,void * data __unused)129991eaf3e1SJohn Birrell fbt_modevent(module_t mod __unused, int type, void *data __unused)
130091eaf3e1SJohn Birrell {
130191eaf3e1SJohn Birrell int error = 0;
130291eaf3e1SJohn Birrell
130391eaf3e1SJohn Birrell switch (type) {
130491eaf3e1SJohn Birrell case MOD_LOAD:
130591eaf3e1SJohn Birrell break;
130691eaf3e1SJohn Birrell
130791eaf3e1SJohn Birrell case MOD_UNLOAD:
130891eaf3e1SJohn Birrell break;
130991eaf3e1SJohn Birrell
131091eaf3e1SJohn Birrell case MOD_SHUTDOWN:
131191eaf3e1SJohn Birrell break;
131291eaf3e1SJohn Birrell
131391eaf3e1SJohn Birrell default:
131491eaf3e1SJohn Birrell error = EOPNOTSUPP;
131591eaf3e1SJohn Birrell break;
131691eaf3e1SJohn Birrell
131791eaf3e1SJohn Birrell }
131891eaf3e1SJohn Birrell
131991eaf3e1SJohn Birrell return (error);
132091eaf3e1SJohn Birrell }
132191eaf3e1SJohn Birrell
132291eaf3e1SJohn Birrell SYSINIT(fbt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fbt_load, NULL);
132391eaf3e1SJohn Birrell SYSUNINIT(fbt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fbt_unload, NULL);
132491eaf3e1SJohn Birrell
132591eaf3e1SJohn Birrell DEV_MODULE(fbt, fbt_modevent, NULL);
132691eaf3e1SJohn Birrell MODULE_VERSION(fbt, 1);
132791eaf3e1SJohn Birrell MODULE_DEPEND(fbt, dtrace, 1, 1, 1);
132891eaf3e1SJohn Birrell MODULE_DEPEND(fbt, opensolaris, 1, 1, 1);
1329