1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <dlfcn.h>
29 #include <link.h>
30 #include <sys/dtrace.h>
31 
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <libelf.h>
38 #include <gelf.h>
39 
40 /*
41  * In Solaris 10 GA, the only mechanism for communicating helper information
42  * is through the DTrace helper pseudo-device node in /devices; there is
43  * no /dev link. Because of this, USDT providers and helper actions don't
44  * work inside of non-global zones. This issue was addressed by adding
45  * the /dev and having this initialization code use that /dev link. If the
46  * /dev link doesn't exist it falls back to looking for the /devices node
47  * as this code may be embedded in a binary which runs on Solaris 10 GA.
48  *
49  * Users may set the following environment variable to affect the way
50  * helper initialization takes place:
51  *
52  *	DTRACE_DOF_INIT_DEBUG		enable debugging output
53  *	DTRACE_DOF_INIT_DISABLE		disable helper loading
54  *	DTRACE_DOF_INIT_DEVNAME		set the path to the helper node
55  */
56 
57 static const char *devnamep = "/dev/dtrace/helper";
58 #if defined(sun)
59 static const char *olddevname = "/devices/pseudo/dtrace@0:helper";
60 #endif
61 
62 static const char *modname;	/* Name of this load object */
63 static int gen;			/* DOF helper generation */
64 #if defined(sun)
65 extern dof_hdr_t __SUNW_dof;	/* DOF defined in the .SUNW_dof section */
66 #endif
67 static boolean_t dof_init_debug = B_FALSE;	/* From DTRACE_DOF_INIT_DEBUG */
68 
69 static void
70 dprintf(int debug, const char *fmt, ...)
71 {
72 	va_list ap;
73 
74 	if (debug && !dof_init_debug)
75 		return;
76 
77 	va_start(ap, fmt);
78 
79 	if (modname == NULL)
80 		(void) fprintf(stderr, "dtrace DOF: ");
81 	else
82 		(void) fprintf(stderr, "dtrace DOF %s: ", modname);
83 
84 	(void) vfprintf(stderr, fmt, ap);
85 
86 	if (fmt[strlen(fmt) - 1] != '\n')
87 		(void) fprintf(stderr, ": %s\n", strerror(errno));
88 
89 	va_end(ap);
90 }
91 
92 #if !defined(sun)
93 static void
94 fixsymbol(Elf *e, Elf_Data *data, size_t idx, int nprobes, char *buf,
95     dof_sec_t *sec, int *fixedprobes, char *dofstrtab)
96 {
97 	GElf_Sym sym;
98 	char *s;
99 	unsigned char *funcname;
100 	dof_probe_t *prb;
101 	int j = 0;
102 	int ndx;
103 
104 	while (gelf_getsym(data, j++, &sym) != NULL) {
105 		prb = (dof_probe_t *)(void *)(buf + sec->dofs_offset);
106 
107 		for (ndx = nprobes; ndx; ndx--, prb += 1) {
108 			funcname = dofstrtab + prb->dofpr_func;
109 			s = elf_strptr(e, idx, sym.st_name);
110 			if (strcmp(s, funcname) == 0) {
111 				dprintf(1, "fixing %s() symbol\n", s);
112 				prb->dofpr_addr = sym.st_value;
113 				(*fixedprobes)++;
114 			}
115 		}
116 		if (*fixedprobes == nprobes)
117 			break;
118 	}
119 }
120 #endif
121 
122 #if defined(sun)
123 #pragma init(dtrace_dof_init)
124 #else
125 static void dtrace_dof_init(void) __attribute__ ((constructor));
126 #endif
127 
128 static void
129 dtrace_dof_init(void)
130 {
131 #if defined(sun)
132 	dof_hdr_t *dof = &__SUNW_dof;
133 #else
134 	dof_hdr_t *dof = NULL;
135 #endif
136 #ifdef _LP64
137 	Elf64_Ehdr *elf;
138 #else
139 	Elf32_Ehdr *elf;
140 #endif
141 	dof_helper_t dh;
142 	Link_map *lmp;
143 #if defined(sun)
144 	Lmid_t lmid;
145 #else
146 	u_long lmid = 0;
147 	dof_sec_t *sec;
148 	size_t i;
149 #endif
150 	int fd;
151 	const char *p;
152 #if !defined(sun)
153 	Elf *e;
154 	Elf_Scn *scn = NULL;
155 	Elf_Data *symtabdata = NULL, *dynsymdata = NULL;
156 	GElf_Shdr shdr;
157 	int efd, nprobes;
158 	char *s;
159 	size_t shstridx, symtabidx = 0, dynsymidx = 0;
160 	unsigned char *dofstrtab = NULL;
161 	unsigned char *buf;
162 	int fixedprobes = 0;
163 #endif
164 
165 	if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL)
166 		return;
167 
168 	if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL)
169 		dof_init_debug = B_TRUE;
170 
171 	if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) {
172 		dprintf(1, "couldn't discover module name or address\n");
173 		return;
174 	}
175 
176 #if defined(sun)
177 	if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) {
178 		dprintf(1, "couldn't discover link map ID\n");
179 		return;
180 	}
181 #endif
182 
183 
184 	if ((modname = strrchr(lmp->l_name, '/')) == NULL)
185 		modname = lmp->l_name;
186 	else
187 		modname++;
188 #if !defined(sun)
189 	elf_version(EV_CURRENT);
190 	if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) {
191 		dprintf(1, "couldn't open file for reading\n");
192 		return;
193 	}
194 	if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) {
195 		dprintf(1, "elf_begin failed\n");
196 		close(efd);
197 		return;
198 	}
199 	elf_getshdrstrndx(e, &shstridx);
200 	dof = NULL;
201 	while ((scn = elf_nextscn(e, scn)) != NULL) {
202 		gelf_getshdr(scn, &shdr);
203 		if (shdr.sh_type == SHT_SYMTAB) {
204 			symtabidx = shdr.sh_link;
205 			symtabdata = elf_getdata(scn, NULL);
206 		} else if (shdr.sh_type == SHT_DYNSYM) {
207 			dynsymidx = shdr.sh_link;
208 			dynsymdata = elf_getdata(scn, NULL);
209 		} else if (shdr.sh_type == SHT_PROGBITS) {
210 			s = elf_strptr(e, shstridx, shdr.sh_name);
211 			if  (s && strcmp(s, ".SUNW_dof") == 0) {
212 				dof = elf_getdata(scn, NULL)->d_buf;
213 			}
214 		}
215 	}
216 	if (dof == NULL) {
217 		dprintf(1, "SUNW_dof section not found\n");
218 		elf_end(e);
219 		close(efd);
220 		return;
221 	}
222 #endif
223 
224 	if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 ||
225 	    dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 ||
226 	    dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 ||
227 	    dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) {
228 		dprintf(0, ".SUNW_dof section corrupt\n");
229 		return;
230 	}
231 
232 	elf = (void *)lmp->l_addr;
233 
234 	dh.dofhp_dof = (uintptr_t)dof;
235 	dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0;
236 
237 	if (lmid == 0) {
238 		(void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod),
239 		    "%s", modname);
240 	} else {
241 		(void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod),
242 		    "LM%lu`%s", lmid, modname);
243 	}
244 
245 	if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL)
246 		devnamep = p;
247 
248 	if ((fd = open64(devnamep, O_RDWR)) < 0) {
249 		dprintf(1, "failed to open helper device %s", devnamep);
250 #if defined(sun)
251 		/*
252 		 * If the device path wasn't explicitly set, try again with
253 		 * the old device path.
254 		 */
255 		if (p != NULL)
256 			return;
257 
258 		devnamep = olddevname;
259 
260 		if ((fd = open64(devnamep, O_RDWR)) < 0) {
261 			dprintf(1, "failed to open helper device %s", devnamep);
262 			return;
263 		}
264 #else
265 		return;
266 #endif
267 	}
268 #if !defined(sun)
269 	/*
270 	 * We need to fix the base address of each probe since this wasn't
271 	 * done by ld(1). (ld(1) needs to grow support for parsing the
272 	 * SUNW_dof section).
273 	 *
274 	 * The complexity of this is not that great. The first for loop
275 	 * iterates over the sections inside the DOF file. There are usually
276 	 * 10 sections here. We asume the STRTAB section comes first and the
277 	 * PROBES section comes after. Since we are only interested in fixing
278 	 * data inside the PROBES section we quit the for loop after processing
279 	 * the PROBES section. It's usually the case that the first section
280 	 * is the STRTAB section and the second section is the PROBES section,
281 	 * so this for loop is not meaningful when doing complexity analysis.
282 	 *
283 	 * After finding the probes section, we iterate over the symbols
284 	 * in the symtab section. When we find a symbol name that matches
285 	 * the probe function name, we fix it. If we have fixed all the
286 	 * probes, we exit all the loops and we are done.
287 	 * The number of probes is given by the variable 'nprobes' and this
288 	 * depends entirely on the user, but some optimizations were done.
289 	 *
290 	 * We are assuming the number of probes is less than the number of
291 	 * symbols (libc can have 4k symbols, for example).
292 	 */
293 	sec = (dof_sec_t *)(dof + 1);
294 	buf = (char *)dof;
295 	for (i = 0; i < dof->dofh_secnum; i++, sec++) {
296 		if (sec->dofs_type == DOF_SECT_STRTAB)
297 			dofstrtab = (unsigned char *)(buf + sec->dofs_offset);
298 		else if (sec->dofs_type == DOF_SECT_PROBES && dofstrtab)
299 			break;
300 
301 	}
302 	nprobes = sec->dofs_size / sec->dofs_entsize;
303 	fixsymbol(e, symtabdata, symtabidx, nprobes, buf, sec, &fixedprobes,
304 	    dofstrtab);
305 	if (fixedprobes != nprobes) {
306 		/*
307 		 * If we haven't fixed all the probes using the
308 		 * symtab section, look inside the dynsym
309 		 * section.
310 		 */
311 		fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, sec,
312 		    &fixedprobes, dofstrtab);
313 	}
314 	if (fixedprobes != nprobes) {
315 		fprintf(stderr, "WARNING: number of probes "
316 		    "fixed does not match the number of "
317 		    "defined probes (%d != %d, "
318 		    "respectively)\n", fixedprobes, nprobes);
319 		fprintf(stderr, "WARNING: some probes might "
320 		    "not fire or your program might crash\n");
321 	}
322 #endif
323 	if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1)
324 		dprintf(1, "DTrace ioctl failed for DOF at %p", dof);
325 	else {
326 		dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof);
327 #if !defined(sun)
328 		gen = dh.gen;
329 #endif
330 	}
331 
332 	(void) close(fd);
333 #if !defined(sun)
334 	elf_end(e);
335 	(void) close(efd);
336 #endif
337 }
338 
339 #if defined(sun)
340 #pragma fini(dtrace_dof_fini)
341 #else
342 static void dtrace_dof_fini(void) __attribute__ ((destructor));
343 #endif
344 
345 static void
346 dtrace_dof_fini(void)
347 {
348 	int fd;
349 
350 	if ((fd = open64(devnamep, O_RDWR)) < 0) {
351 		dprintf(1, "failed to open helper device %s", devnamep);
352 		return;
353 	}
354 
355 	if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1)
356 		dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen);
357 	else
358 		dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen);
359 
360 	(void) close(fd);
361 }
362