]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / cddl / contrib / opensolaris / lib / libdtrace / common / drti.c
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 }