xref: /freebsd-src/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c (revision bc96366c864c07ef352edb92017357917c75b36c)
16ff6d951SJohn Birrell /*
26ff6d951SJohn Birrell  * CDDL HEADER START
36ff6d951SJohn Birrell  *
46ff6d951SJohn Birrell  * The contents of this file are subject to the terms of the
51670a1c2SRui Paulo  * Common Development and Distribution License (the "License").
61670a1c2SRui Paulo  * You may not use this file except in compliance with the License.
76ff6d951SJohn Birrell  *
86ff6d951SJohn Birrell  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96ff6d951SJohn Birrell  * or http://www.opensolaris.org/os/licensing.
106ff6d951SJohn Birrell  * See the License for the specific language governing permissions
116ff6d951SJohn Birrell  * and limitations under the License.
126ff6d951SJohn Birrell  *
136ff6d951SJohn Birrell  * When distributing Covered Code, include this CDDL HEADER in each
146ff6d951SJohn Birrell  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156ff6d951SJohn Birrell  * If applicable, add the following below this CDDL HEADER, with the
166ff6d951SJohn Birrell  * fields enclosed by brackets "[]" replaced with your own identifying
176ff6d951SJohn Birrell  * information: Portions Copyright [yyyy] [name of copyright owner]
186ff6d951SJohn Birrell  *
196ff6d951SJohn Birrell  * CDDL HEADER END
206ff6d951SJohn Birrell  */
216ff6d951SJohn Birrell /*
221670a1c2SRui Paulo  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2344c25a3dSMark Johnston  * Copyright 2013 Voxer Inc. All rights reserved.
246ff6d951SJohn Birrell  * Use is subject to license terms.
256ff6d951SJohn Birrell  */
266ff6d951SJohn Birrell 
276ff6d951SJohn Birrell #include <unistd.h>
286ff6d951SJohn Birrell #include <fcntl.h>
296ff6d951SJohn Birrell #include <dlfcn.h>
306ff6d951SJohn Birrell #include <link.h>
316ff6d951SJohn Birrell #include <sys/dtrace.h>
326ff6d951SJohn Birrell 
336ff6d951SJohn Birrell #include <stdarg.h>
346ff6d951SJohn Birrell #include <stdio.h>
356ff6d951SJohn Birrell #include <stdlib.h>
366ff6d951SJohn Birrell #include <string.h>
376ff6d951SJohn Birrell #include <errno.h>
380f2bd1e8SRui Paulo #include <libelf.h>
390f2bd1e8SRui Paulo #include <gelf.h>
406ff6d951SJohn Birrell 
416ff6d951SJohn Birrell /*
426ff6d951SJohn Birrell  * In Solaris 10 GA, the only mechanism for communicating helper information
436ff6d951SJohn Birrell  * is through the DTrace helper pseudo-device node in /devices; there is
446ff6d951SJohn Birrell  * no /dev link. Because of this, USDT providers and helper actions don't
456ff6d951SJohn Birrell  * work inside of non-global zones. This issue was addressed by adding
466ff6d951SJohn Birrell  * the /dev and having this initialization code use that /dev link. If the
476ff6d951SJohn Birrell  * /dev link doesn't exist it falls back to looking for the /devices node
486ff6d951SJohn Birrell  * as this code may be embedded in a binary which runs on Solaris 10 GA.
496ff6d951SJohn Birrell  *
506ff6d951SJohn Birrell  * Users may set the following environment variable to affect the way
516ff6d951SJohn Birrell  * helper initialization takes place:
526ff6d951SJohn Birrell  *
536ff6d951SJohn Birrell  *	DTRACE_DOF_INIT_DEBUG		enable debugging output
546ff6d951SJohn Birrell  *	DTRACE_DOF_INIT_DISABLE		disable helper loading
556ff6d951SJohn Birrell  *	DTRACE_DOF_INIT_DEVNAME		set the path to the helper node
566ff6d951SJohn Birrell  */
576ff6d951SJohn Birrell 
585fe26f7cSJohn Birrell static const char *devnamep = "/dev/dtrace/helper";
59*bc96366cSSteven Hartland #ifdef illumos
606ff6d951SJohn Birrell static const char *olddevname = "/devices/pseudo/dtrace@0:helper";
610f2bd1e8SRui Paulo #endif
626ff6d951SJohn Birrell 
636ff6d951SJohn Birrell static const char *modname;	/* Name of this load object */
646ff6d951SJohn Birrell static int gen;			/* DOF helper generation */
65*bc96366cSSteven Hartland #ifdef illumos
666ff6d951SJohn Birrell extern dof_hdr_t __SUNW_dof;	/* DOF defined in the .SUNW_dof section */
670f2bd1e8SRui Paulo #endif
68dba0ac63SRui Paulo static boolean_t dof_init_debug = B_FALSE;	/* From DTRACE_DOF_INIT_DEBUG */
696ff6d951SJohn Birrell 
706ff6d951SJohn Birrell static void
716ff6d951SJohn Birrell dprintf(int debug, const char *fmt, ...)
726ff6d951SJohn Birrell {
736ff6d951SJohn Birrell 	va_list ap;
746ff6d951SJohn Birrell 
751670a1c2SRui Paulo 	if (debug && !dof_init_debug)
766ff6d951SJohn Birrell 		return;
776ff6d951SJohn Birrell 
786ff6d951SJohn Birrell 	va_start(ap, fmt);
796ff6d951SJohn Birrell 
806ff6d951SJohn Birrell 	if (modname == NULL)
816ff6d951SJohn Birrell 		(void) fprintf(stderr, "dtrace DOF: ");
826ff6d951SJohn Birrell 	else
836ff6d951SJohn Birrell 		(void) fprintf(stderr, "dtrace DOF %s: ", modname);
846ff6d951SJohn Birrell 
856ff6d951SJohn Birrell 	(void) vfprintf(stderr, fmt, ap);
866ff6d951SJohn Birrell 
876ff6d951SJohn Birrell 	if (fmt[strlen(fmt) - 1] != '\n')
886ff6d951SJohn Birrell 		(void) fprintf(stderr, ": %s\n", strerror(errno));
896ff6d951SJohn Birrell 
906ff6d951SJohn Birrell 	va_end(ap);
916ff6d951SJohn Birrell }
926ff6d951SJohn Birrell 
93*bc96366cSSteven Hartland #ifdef illumos
946ff6d951SJohn Birrell #pragma init(dtrace_dof_init)
955fe26f7cSJohn Birrell #else
965fe26f7cSJohn Birrell static void dtrace_dof_init(void) __attribute__ ((constructor));
975fe26f7cSJohn Birrell #endif
985fe26f7cSJohn Birrell 
996ff6d951SJohn Birrell static void
1006ff6d951SJohn Birrell dtrace_dof_init(void)
1016ff6d951SJohn Birrell {
102*bc96366cSSteven Hartland #ifdef illumos
1036ff6d951SJohn Birrell 	dof_hdr_t *dof = &__SUNW_dof;
1040f2bd1e8SRui Paulo #else
1050f2bd1e8SRui Paulo 	dof_hdr_t *dof = NULL;
1060f2bd1e8SRui Paulo #endif
1076ff6d951SJohn Birrell #ifdef _LP64
1086ff6d951SJohn Birrell 	Elf64_Ehdr *elf;
1096ff6d951SJohn Birrell #else
1106ff6d951SJohn Birrell 	Elf32_Ehdr *elf;
1116ff6d951SJohn Birrell #endif
1126ff6d951SJohn Birrell 	dof_helper_t dh;
1139ad184a9SWill Andrews 	Link_map *lmp = NULL;
114*bc96366cSSteven Hartland #ifdef illumos
1156ff6d951SJohn Birrell 	Lmid_t lmid;
1165fe26f7cSJohn Birrell #else
1175fe26f7cSJohn Birrell 	u_long lmid = 0;
1185fe26f7cSJohn Birrell #endif
1196ff6d951SJohn Birrell 	int fd;
1206ff6d951SJohn Birrell 	const char *p;
121*bc96366cSSteven Hartland #ifndef illumos
1220f2bd1e8SRui Paulo 	Elf *e;
1230f2bd1e8SRui Paulo 	Elf_Scn *scn = NULL;
124f0de4637SMark Johnston 	Elf_Data *dofdata = NULL;
12544c25a3dSMark Johnston 	dof_hdr_t *dof_next = NULL;
1260f2bd1e8SRui Paulo 	GElf_Shdr shdr;
1278caf8a8dSMark Johnston 	int efd;
1280f2bd1e8SRui Paulo 	char *s;
129f0de4637SMark Johnston 	size_t shstridx;
130003e64dfSMark Johnston 	uint64_t aligned_filesz;
1310f2bd1e8SRui Paulo #endif
1326ff6d951SJohn Birrell 
1336ff6d951SJohn Birrell 	if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL)
1346ff6d951SJohn Birrell 		return;
1356ff6d951SJohn Birrell 
1361670a1c2SRui Paulo 	if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL)
1371670a1c2SRui Paulo 		dof_init_debug = B_TRUE;
1381670a1c2SRui Paulo 
1396ff6d951SJohn Birrell 	if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) {
1406ff6d951SJohn Birrell 		dprintf(1, "couldn't discover module name or address\n");
1416ff6d951SJohn Birrell 		return;
1426ff6d951SJohn Birrell 	}
1436ff6d951SJohn Birrell 
144*bc96366cSSteven Hartland #ifdef illumos
1456ff6d951SJohn Birrell 	if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) {
1466ff6d951SJohn Birrell 		dprintf(1, "couldn't discover link map ID\n");
1476ff6d951SJohn Birrell 		return;
1486ff6d951SJohn Birrell 	}
1495fe26f7cSJohn Birrell #endif
1506ff6d951SJohn Birrell 
1516ff6d951SJohn Birrell 	if ((modname = strrchr(lmp->l_name, '/')) == NULL)
1526ff6d951SJohn Birrell 		modname = lmp->l_name;
1536ff6d951SJohn Birrell 	else
1546ff6d951SJohn Birrell 		modname++;
155*bc96366cSSteven Hartland #ifndef illumos
1560f2bd1e8SRui Paulo 	elf_version(EV_CURRENT);
1570f2bd1e8SRui Paulo 	if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) {
1580f2bd1e8SRui Paulo 		dprintf(1, "couldn't open file for reading\n");
1590f2bd1e8SRui Paulo 		return;
1600f2bd1e8SRui Paulo 	}
1610f2bd1e8SRui Paulo 	if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) {
1620f2bd1e8SRui Paulo 		dprintf(1, "elf_begin failed\n");
1630f2bd1e8SRui Paulo 		close(efd);
1640f2bd1e8SRui Paulo 		return;
1650f2bd1e8SRui Paulo 	}
1660f2bd1e8SRui Paulo 	elf_getshdrstrndx(e, &shstridx);
1670f2bd1e8SRui Paulo 	dof = NULL;
1680f2bd1e8SRui Paulo 	while ((scn = elf_nextscn(e, scn)) != NULL) {
1690f2bd1e8SRui Paulo 		gelf_getshdr(scn, &shdr);
170f0de4637SMark Johnston 		if (shdr.sh_type == SHT_SUNW_dof) {
1710f2bd1e8SRui Paulo 			s = elf_strptr(e, shstridx, shdr.sh_name);
1728caf8a8dSMark Johnston 			if (s != NULL && strcmp(s, ".SUNW_dof") == 0) {
17344c25a3dSMark Johnston 				dofdata = elf_getdata(scn, NULL);
17444c25a3dSMark Johnston 				dof = dofdata->d_buf;
1752f6cde8eSMark Johnston 				break;
1760f2bd1e8SRui Paulo 			}
1770f2bd1e8SRui Paulo 		}
1780f2bd1e8SRui Paulo 	}
1790f2bd1e8SRui Paulo 	if (dof == NULL) {
1800f2bd1e8SRui Paulo 		dprintf(1, "SUNW_dof section not found\n");
1810f2bd1e8SRui Paulo 		elf_end(e);
1820f2bd1e8SRui Paulo 		close(efd);
1830f2bd1e8SRui Paulo 		return;
1840f2bd1e8SRui Paulo 	}
18544c25a3dSMark Johnston 
18644c25a3dSMark Johnston 	while ((char *) dof < (char *) dofdata->d_buf + dofdata->d_size) {
187003e64dfSMark Johnston 		aligned_filesz = (shdr.sh_addralign == 0 ? dof->dofh_filesz :
188003e64dfSMark Johnston 		    roundup2(dof->dofh_filesz, shdr.sh_addralign));
189003e64dfSMark Johnston 		dof_next = (void *) ((char *) dof + aligned_filesz);
1900f2bd1e8SRui Paulo #endif
1916ff6d951SJohn Birrell 
1926ff6d951SJohn Birrell 	if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 ||
1936ff6d951SJohn Birrell 	    dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 ||
1946ff6d951SJohn Birrell 	    dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 ||
1956ff6d951SJohn Birrell 	    dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) {
1966ff6d951SJohn Birrell 		dprintf(0, ".SUNW_dof section corrupt\n");
1976ff6d951SJohn Birrell 		return;
1986ff6d951SJohn Birrell 	}
1996ff6d951SJohn Birrell 
2006ff6d951SJohn Birrell 	elf = (void *)lmp->l_addr;
2016ff6d951SJohn Birrell 
2026ff6d951SJohn Birrell 	dh.dofhp_dof = (uintptr_t)dof;
2035fe26f7cSJohn Birrell 	dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0;
2046ff6d951SJohn Birrell 
2056ff6d951SJohn Birrell 	if (lmid == 0) {
2066ff6d951SJohn Birrell 		(void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod),
2076ff6d951SJohn Birrell 		    "%s", modname);
2086ff6d951SJohn Birrell 	} else {
2096ff6d951SJohn Birrell 		(void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod),
2106ff6d951SJohn Birrell 		    "LM%lu`%s", lmid, modname);
2116ff6d951SJohn Birrell 	}
2126ff6d951SJohn Birrell 
2136ff6d951SJohn Birrell 	if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL)
2145fe26f7cSJohn Birrell 		devnamep = p;
2156ff6d951SJohn Birrell 
2165fe26f7cSJohn Birrell 	if ((fd = open64(devnamep, O_RDWR)) < 0) {
2175fe26f7cSJohn Birrell 		dprintf(1, "failed to open helper device %s", devnamep);
218*bc96366cSSteven Hartland #ifdef illumos
2196ff6d951SJohn Birrell 		/*
2206ff6d951SJohn Birrell 		 * If the device path wasn't explicitly set, try again with
2216ff6d951SJohn Birrell 		 * the old device path.
2226ff6d951SJohn Birrell 		 */
2236ff6d951SJohn Birrell 		if (p != NULL)
2246ff6d951SJohn Birrell 			return;
2256ff6d951SJohn Birrell 
2265fe26f7cSJohn Birrell 		devnamep = olddevname;
2276ff6d951SJohn Birrell 
2285fe26f7cSJohn Birrell 		if ((fd = open64(devnamep, O_RDWR)) < 0) {
2295fe26f7cSJohn Birrell 			dprintf(1, "failed to open helper device %s", devnamep);
2306ff6d951SJohn Birrell 			return;
2316ff6d951SJohn Birrell 		}
2320f2bd1e8SRui Paulo #else
2330f2bd1e8SRui Paulo 		return;
2340f2bd1e8SRui Paulo #endif
2356ff6d951SJohn Birrell 	}
2366ff6d951SJohn Birrell 	if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1)
2376ff6d951SJohn Birrell 		dprintf(1, "DTrace ioctl failed for DOF at %p", dof);
2380f2bd1e8SRui Paulo 	else {
2396ff6d951SJohn Birrell 		dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof);
240*bc96366cSSteven Hartland #ifndef illumos
2410f2bd1e8SRui Paulo 		gen = dh.gen;
2420f2bd1e8SRui Paulo #endif
2430f2bd1e8SRui Paulo 	}
2446ff6d951SJohn Birrell 
2456ff6d951SJohn Birrell 	(void) close(fd);
24644c25a3dSMark Johnston 
247*bc96366cSSteven Hartland #ifndef illumos
24844c25a3dSMark Johnston 		/* End of while loop */
24944c25a3dSMark Johnston 		dof = dof_next;
25044c25a3dSMark Johnston 	}
25144c25a3dSMark Johnston 
2520f2bd1e8SRui Paulo 	elf_end(e);
2530f2bd1e8SRui Paulo 	(void) close(efd);
2540f2bd1e8SRui Paulo #endif
2556ff6d951SJohn Birrell }
2566ff6d951SJohn Birrell 
257*bc96366cSSteven Hartland #ifdef illumos
2586ff6d951SJohn Birrell #pragma fini(dtrace_dof_fini)
2595fe26f7cSJohn Birrell #else
2605fe26f7cSJohn Birrell static void dtrace_dof_fini(void) __attribute__ ((destructor));
2615fe26f7cSJohn Birrell #endif
2625fe26f7cSJohn Birrell 
2636ff6d951SJohn Birrell static void
2646ff6d951SJohn Birrell dtrace_dof_fini(void)
2656ff6d951SJohn Birrell {
2666ff6d951SJohn Birrell 	int fd;
2676ff6d951SJohn Birrell 
2685fe26f7cSJohn Birrell 	if ((fd = open64(devnamep, O_RDWR)) < 0) {
2695fe26f7cSJohn Birrell 		dprintf(1, "failed to open helper device %s", devnamep);
2706ff6d951SJohn Birrell 		return;
2716ff6d951SJohn Birrell 	}
2726ff6d951SJohn Birrell 
2730f2bd1e8SRui Paulo 	if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1)
2746ff6d951SJohn Birrell 		dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen);
2756ff6d951SJohn Birrell 	else
2766ff6d951SJohn Birrell 		dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen);
2776ff6d951SJohn Birrell 
2786ff6d951SJohn Birrell 	(void) close(fd);
2796ff6d951SJohn Birrell }
280