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