drti.c revision 212462
138032Speter/*
2111823Sgshapiro * CDDL HEADER START
364562Sgshapiro *
438032Speter * The contents of this file are subject to the terms of the
538032Speter * Common Development and Distribution License (the "License").
638032Speter * You may not use this file except in compliance with the License.
738032Speter *
838032Speter * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
938032Speter * or http://www.opensolaris.org/os/licensing.
1038032Speter * See the License for the specific language governing permissions
1138032Speter * and limitations under the License.
1238032Speter *
1338032Speter * When distributing Covered Code, include this CDDL HEADER in each
1464562Sgshapiro * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1590792Sgshapiro * If applicable, add the following below this CDDL HEADER, with the
1690792Sgshapiro * fields enclosed by brackets "[]" replaced with your own identifying
1790792Sgshapiro * information: Portions Copyright [yyyy] [name of copyright owner]
1864562Sgshapiro *
19111823Sgshapiro * CDDL HEADER END
2064562Sgshapiro */
2190792Sgshapiro/*
2290792Sgshapiro * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2390792Sgshapiro * Use is subject to license terms.
2490792Sgshapiro */
2590792Sgshapiro
2690792Sgshapiro#include <unistd.h>
2764562Sgshapiro#include <fcntl.h>
2890792Sgshapiro#include <dlfcn.h>
2990792Sgshapiro#include <link.h>
3090792Sgshapiro#include <sys/dtrace.h>
3138032Speter
3290792Sgshapiro#include <stdarg.h>
3390792Sgshapiro#include <stdio.h>
3438032Speter#include <stdlib.h>
3590792Sgshapiro#include <string.h>
3690792Sgshapiro#include <errno.h>
3790792Sgshapiro#include <libelf.h>
3890792Sgshapiro#include <gelf.h>
3990792Sgshapiro
4090792Sgshapiro/*
4190792Sgshapiro * In Solaris 10 GA, the only mechanism for communicating helper information
4290792Sgshapiro * is through the DTrace helper pseudo-device node in /devices; there is
4390792Sgshapiro * no /dev link. Because of this, USDT providers and helper actions don't
4490792Sgshapiro * work inside of non-global zones. This issue was addressed by adding
4590792Sgshapiro * the /dev and having this initialization code use that /dev link. If the
4690792Sgshapiro * /dev link doesn't exist it falls back to looking for the /devices node
4790792Sgshapiro * as this code may be embedded in a binary which runs on Solaris 10 GA.
4890792Sgshapiro *
4990792Sgshapiro * Users may set the following environment variable to affect the way
5090792Sgshapiro * helper initialization takes place:
5190792Sgshapiro *
5290792Sgshapiro *	DTRACE_DOF_INIT_DEBUG		enable debugging output
5390792Sgshapiro *	DTRACE_DOF_INIT_DISABLE		disable helper loading
5490792Sgshapiro *	DTRACE_DOF_INIT_DEVNAME		set the path to the helper node
5590792Sgshapiro */
5690792Sgshapiro
5790792Sgshapirostatic const char *devnamep = "/dev/dtrace/helper";
5890792Sgshapiro#if defined(sun)
5990792Sgshapirostatic const char *olddevname = "/devices/pseudo/dtrace@0:helper";
6090792Sgshapiro#endif
6190792Sgshapiro
6290792Sgshapirostatic const char *modname;	/* Name of this load object */
6390792Sgshapirostatic int gen;			/* DOF helper generation */
6464562Sgshapiro#if defined(sun)
6564562Sgshapiroextern dof_hdr_t __SUNW_dof;	/* DOF defined in the .SUNW_dof section */
6664562Sgshapiro#endif
6764562Sgshapirostatic boolean_t dof_init_debug = B_FALSE;	/* From DTRACE_DOF_INIT_DEBUG */
6864562Sgshapiro
6990792Sgshapirostatic void
7064562Sgshapirodprintf(int debug, const char *fmt, ...)
7138032Speter{
7290792Sgshapiro	va_list ap;
7390792Sgshapiro
7490792Sgshapiro	if (debug && !dof_init_debug)
7538032Speter		return;
7638032Speter
7738032Speter	va_start(ap, fmt);
7838032Speter
7938032Speter	if (modname == NULL)
8090792Sgshapiro		(void) fprintf(stderr, "dtrace DOF: ");
8190792Sgshapiro	else
8238032Speter		(void) fprintf(stderr, "dtrace DOF %s: ", modname);
8338032Speter
8438032Speter	(void) vfprintf(stderr, fmt, ap);
8538032Speter
8638032Speter	if (fmt[strlen(fmt) - 1] != '\n')
8738032Speter		(void) fprintf(stderr, ": %s\n", strerror(errno));
8890792Sgshapiro
8938032Speter	va_end(ap);
9038032Speter}
9190792Sgshapiro
9290792Sgshapiro#if !defined(sun)
9390792Sgshapirostatic void
9490792Sgshapirofixsymbol(Elf *e, Elf_Data *data, size_t idx, int nprobes, char *buf,
9590792Sgshapiro    dof_sec_t *sec, int *fixedprobes, char *dofstrtab)
9690792Sgshapiro{
9790792Sgshapiro	GElf_Sym sym;
9838032Speter	char *s;
9938032Speter	unsigned char *funcname;
10064562Sgshapiro	dof_probe_t *prb;
10164562Sgshapiro	int j = 0;
10238032Speter	int ndx;
10338032Speter
10464562Sgshapiro	while (gelf_getsym(data, j++, &sym) != NULL) {
10590792Sgshapiro		prb = (dof_probe_t *)(buf + sec->dofs_offset);
10690792Sgshapiro
10790792Sgshapiro		for (ndx = nprobes; ndx; ndx--, prb += 1) {
10890792Sgshapiro			funcname = dofstrtab + prb->dofpr_func;
10990792Sgshapiro			s = elf_strptr(e, idx, sym.st_name);
11090792Sgshapiro			if (strcmp(s, funcname) == 0) {
11190792Sgshapiro				dprintf(1, "fixing %s() symbol\n", s);
11290792Sgshapiro				prb->dofpr_addr = sym.st_value;
11390792Sgshapiro				(*fixedprobes)++;
11490792Sgshapiro			}
11590792Sgshapiro		}
11690792Sgshapiro		if (*fixedprobes == nprobes)
11790792Sgshapiro			break;
11890792Sgshapiro	}
11990792Sgshapiro}
12090792Sgshapiro#endif
12190792Sgshapiro
12290792Sgshapiro#if defined(sun)
12390792Sgshapiro#pragma init(dtrace_dof_init)
12438032Speter#else
12590792Sgshapirostatic void dtrace_dof_init(void) __attribute__ ((constructor));
12664562Sgshapiro#endif
12790792Sgshapiro
12838032Speterstatic void
12990792Sgshapirodtrace_dof_init(void)
13038032Speter{
13190792Sgshapiro#if defined(sun)
13290792Sgshapiro	dof_hdr_t *dof = &__SUNW_dof;
13338032Speter#else
13466494Sgshapiro	dof_hdr_t *dof = NULL;
13590792Sgshapiro#endif
13666494Sgshapiro#ifdef _LP64
13766494Sgshapiro	Elf64_Ehdr *elf;
13838032Speter#else
13938032Speter	Elf32_Ehdr *elf;
14038032Speter#endif
14138032Speter	dof_helper_t dh;
14238032Speter	Link_map *lmp;
14338032Speter#if defined(sun)
14438032Speter	Lmid_t lmid;
14538032Speter#else
14638032Speter	u_long lmid = 0;
14738032Speter	dof_sec_t *sec;
14838032Speter	size_t i;
14938032Speter#endif
15038032Speter	int fd;
15138032Speter	const char *p;
15238032Speter#if !defined(sun)
15364562Sgshapiro	Elf *e;
15464562Sgshapiro	Elf_Scn *scn = NULL;
15564562Sgshapiro	Elf_Data *symtabdata = NULL, *dynsymdata = NULL;
15664562Sgshapiro	GElf_Shdr shdr;
15790792Sgshapiro	int efd, nprobes;
15864562Sgshapiro	char *s;
15990792Sgshapiro	size_t shstridx, symtabidx = 0, dynsymidx = 0;
16090792Sgshapiro	unsigned char *dofstrtab = NULL;
16164562Sgshapiro	unsigned char *buf;
16290792Sgshapiro	int fixedprobes = 0;
16338032Speter#endif
16438032Speter
16538032Speter	if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL)
16638032Speter		return;
16738032Speter
16838032Speter	if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL)
16938032Speter		dof_init_debug = B_TRUE;
17038032Speter
17164562Sgshapiro	if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) {
17238032Speter		dprintf(1, "couldn't discover module name or address\n");
17390792Sgshapiro		return;
17490792Sgshapiro	}
17594334Sgshapiro
17690792Sgshapiro#if defined(sun)
17790792Sgshapiro	if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) {
17894334Sgshapiro		dprintf(1, "couldn't discover link map ID\n");
17990792Sgshapiro		return;
18090792Sgshapiro	}
18194334Sgshapiro#endif
18290792Sgshapiro
18390792Sgshapiro
18494334Sgshapiro	if ((modname = strrchr(lmp->l_name, '/')) == NULL)
18590792Sgshapiro		modname = lmp->l_name;
18690792Sgshapiro	else
18794334Sgshapiro		modname++;
18890792Sgshapiro#if !defined(sun)
18990792Sgshapiro	elf_version(EV_CURRENT);
19094334Sgshapiro	if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) {
19138032Speter		dprintf(1, "couldn't open file for reading\n");
19290792Sgshapiro		return;
19390792Sgshapiro	}
19490792Sgshapiro	if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) {
19590792Sgshapiro		dprintf(1, "elf_begin failed\n");
19638032Speter		close(efd);
19790792Sgshapiro		return;
19890792Sgshapiro	}
19990792Sgshapiro	elf_getshdrstrndx(e, &shstridx);
20090792Sgshapiro	dof = NULL;
20190792Sgshapiro	while ((scn = elf_nextscn(e, scn)) != NULL) {
20290792Sgshapiro		gelf_getshdr(scn, &shdr);
20390792Sgshapiro		if (shdr.sh_type == SHT_SYMTAB) {
20490792Sgshapiro			symtabidx = shdr.sh_link;
20590792Sgshapiro			symtabdata = elf_getdata(scn, NULL);
20690792Sgshapiro		} else if (shdr.sh_type == SHT_DYNSYM) {
20790792Sgshapiro			dynsymidx = shdr.sh_link;
20890792Sgshapiro			dynsymdata = elf_getdata(scn, NULL);
20990792Sgshapiro		} else if (shdr.sh_type == SHT_PROGBITS) {
21090792Sgshapiro			s = elf_strptr(e, shstridx, shdr.sh_name);
21190792Sgshapiro			if  (s && strcmp(s, ".SUNW_dof") == 0) {
21290792Sgshapiro				dof = elf_getdata(scn, NULL)->d_buf;
21390792Sgshapiro			}
21490792Sgshapiro		}
21590792Sgshapiro	}
21690792Sgshapiro	if (dof == NULL) {
21790792Sgshapiro		dprintf(1, "SUNW_dof section not found\n");
21890792Sgshapiro		elf_end(e);
21990792Sgshapiro		close(efd);
22090792Sgshapiro		return;
221110560Sgshapiro	}
222110560Sgshapiro#endif
223110560Sgshapiro
224110560Sgshapiro	if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 ||
225110560Sgshapiro	    dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 ||
226110560Sgshapiro	    dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 ||
227110560Sgshapiro	    dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) {
228110560Sgshapiro		dprintf(0, ".SUNW_dof section corrupt\n");
229110560Sgshapiro		return;
230110560Sgshapiro	}
231110560Sgshapiro
232110560Sgshapiro	elf = (void *)lmp->l_addr;
23390792Sgshapiro
23490792Sgshapiro	dh.dofhp_dof = (uintptr_t)dof;
23590792Sgshapiro	dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0;
23690792Sgshapiro
23790792Sgshapiro	if (lmid == 0) {
23890792Sgshapiro		(void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod),
23990792Sgshapiro		    "%s", modname);
24090792Sgshapiro	} else {
24190792Sgshapiro		(void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod),
24290792Sgshapiro		    "LM%lu`%s", lmid, modname);
24390792Sgshapiro	}
24490792Sgshapiro
24590792Sgshapiro	if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL)
24690792Sgshapiro		devnamep = p;
24790792Sgshapiro
248110560Sgshapiro	if ((fd = open64(devnamep, O_RDWR)) < 0) {
24990792Sgshapiro		dprintf(1, "failed to open helper device %s", devnamep);
25090792Sgshapiro#if defined(sun)
25190792Sgshapiro		/*
25290792Sgshapiro		 * If the device path wasn't explicitly set, try again with
25390792Sgshapiro		 * the old device path.
25490792Sgshapiro		 */
25590792Sgshapiro		if (p != NULL)
25690792Sgshapiro			return;
25790792Sgshapiro
25890792Sgshapiro		devnamep = olddevname;
25990792Sgshapiro
26090792Sgshapiro		if ((fd = open64(devnamep, O_RDWR)) < 0) {
26190792Sgshapiro			dprintf(1, "failed to open helper device %s", devnamep);
26290792Sgshapiro			return;
26390792Sgshapiro		}
26490792Sgshapiro#else
26590792Sgshapiro		return;
26690792Sgshapiro#endif
26790792Sgshapiro	}
26890792Sgshapiro#if !defined(sun)
26990792Sgshapiro	/*
27090792Sgshapiro	 * We need to fix the base address of each probe since this wasn't
27190792Sgshapiro	 * done by ld(1). (ld(1) needs to grow support for parsing the
27290792Sgshapiro	 * SUNW_dof section).
27390792Sgshapiro	 *
27490792Sgshapiro	 * The complexity of this is not that great. The first for loop
27590792Sgshapiro	 * iterates over the sections inside the DOF file. There are usually
27690792Sgshapiro	 * 10 sections here. We asume the STRTAB section comes first and the
27790792Sgshapiro	 * PROBES section comes after. Since we are only interested in fixing
27890792Sgshapiro	 * data inside the PROBES section we quit the for loop after processing
27990792Sgshapiro	 * the PROBES section. It's usually the case that the first section
28090792Sgshapiro	 * is the STRTAB section and the second section is the PROBES section,
28190792Sgshapiro	 * so this for loop is not meaningful when doing complexity analysis.
28290792Sgshapiro	 *
28390792Sgshapiro	 * After finding the probes section, we iterate over the symbols
28490792Sgshapiro	 * in the symtab section. When we find a symbol name that matches
28590792Sgshapiro	 * the probe function name, we fix it. If we have fixed all the
28690792Sgshapiro	 * probes, we exit all the loops and we are done.
28790792Sgshapiro	 * The number of probes is given by the variable 'nprobes' and this
28890792Sgshapiro	 * depends entirely on the user, but some optimizations were done.
28990792Sgshapiro	 *
29090792Sgshapiro	 * We are assuming the number of probes is less than the number of
29190792Sgshapiro	 * symbols (libc can have 4k symbols, for example).
29290792Sgshapiro	 */
29390792Sgshapiro	sec = (dof_sec_t *)(dof + 1);
29490792Sgshapiro	buf = (char *)dof;
29590792Sgshapiro	for (i = 0; i < dof->dofh_secnum; i++, sec++) {
29690792Sgshapiro		if (sec->dofs_type == DOF_SECT_STRTAB)
29790792Sgshapiro			dofstrtab = (unsigned char *)(buf + sec->dofs_offset);
29890792Sgshapiro		else if (sec->dofs_type == DOF_SECT_PROBES && dofstrtab)
29990792Sgshapiro			break;
30090792Sgshapiro
30190792Sgshapiro	}
30290792Sgshapiro	nprobes = sec->dofs_size / sec->dofs_entsize;
30390792Sgshapiro	fixsymbol(e, symtabdata, symtabidx, nprobes, buf, sec, &fixedprobes,
30490792Sgshapiro	    dofstrtab);
30590792Sgshapiro	if (fixedprobes != nprobes) {
30690792Sgshapiro		/*
30790792Sgshapiro		 * If we haven't fixed all the probes using the
30890792Sgshapiro		 * symtab section, look inside the dynsym
30990792Sgshapiro		 * section.
31090792Sgshapiro		 */
31190792Sgshapiro		fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, sec,
31290792Sgshapiro		    &fixedprobes, dofstrtab);
31390792Sgshapiro	}
31490792Sgshapiro	if (fixedprobes != nprobes) {
31590792Sgshapiro		fprintf(stderr, "WARNING: number of probes "
31690792Sgshapiro		    "fixed does not match the number of "
31790792Sgshapiro		    "defined probes (%d != %d, "
31890792Sgshapiro		    "respectively)\n", fixedprobes, nprobes);
31990792Sgshapiro		fprintf(stderr, "WARNING: some probes might "
32090792Sgshapiro		    "not fire or your program might crash\n");
32190792Sgshapiro	}
32290792Sgshapiro#endif
32390792Sgshapiro	if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1)
32490792Sgshapiro		dprintf(1, "DTrace ioctl failed for DOF at %p", dof);
32590792Sgshapiro	else {
32690792Sgshapiro		dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof);
32790792Sgshapiro#if !defined(sun)
32890792Sgshapiro		gen = dh.gen;
32990792Sgshapiro#endif
33090792Sgshapiro	}
33190792Sgshapiro
33290792Sgshapiro	(void) close(fd);
33390792Sgshapiro#if !defined(sun)
33490792Sgshapiro	elf_end(e);
33590792Sgshapiro	(void) close(efd);
33690792Sgshapiro#endif
33790792Sgshapiro}
33890792Sgshapiro
33990792Sgshapiro#if defined(sun)
34090792Sgshapiro#pragma fini(dtrace_dof_fini)
34190792Sgshapiro#else
34290792Sgshapirostatic void dtrace_dof_fini(void) __attribute__ ((destructor));
34390792Sgshapiro#endif
34490792Sgshapiro
34590792Sgshapirostatic void
34638032Speterdtrace_dof_fini(void)
34764562Sgshapiro{
34864562Sgshapiro	int fd;
34964562Sgshapiro
35038032Speter	if ((fd = open64(devnamep, O_RDWR)) < 0) {
35138032Speter		dprintf(1, "failed to open helper device %s", devnamep);
35238032Speter		return;
35364562Sgshapiro	}
35438032Speter
35538032Speter	if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1)
35638032Speter		dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen);
35738032Speter	else
35838032Speter		dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen);
35938032Speter
36038032Speter	(void) close(fd);
36138032Speter}
36238032Speter