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