drti.c revision 297077
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#ifdef illumos 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#ifdef illumos 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#ifndef illumos 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#ifdef illumos 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#ifdef illumos 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#ifdef illumos 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#ifndef illumos 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 uint64_t aligned_filesz; 167#endif 168 169 if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL) 170 return; 171 172 if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL) 173 dof_init_debug = B_TRUE; 174 175 if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) { 176 dprintf(1, "couldn't discover module name or address\n"); 177 return; 178 } 179 180#ifdef illumos 181 if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) { 182 dprintf(1, "couldn't discover link map ID\n"); 183 return; 184 } 185#endif 186 187 188 if ((modname = strrchr(lmp->l_name, '/')) == NULL) 189 modname = lmp->l_name; 190 else 191 modname++; 192#ifndef illumos 193 elf_version(EV_CURRENT); 194 if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) { 195 dprintf(1, "couldn't open file for reading\n"); 196 return; 197 } 198 if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) { 199 dprintf(1, "elf_begin failed\n"); 200 close(efd); 201 return; 202 } 203 elf_getshdrstrndx(e, &shstridx); 204 dof = NULL; 205 while ((scn = elf_nextscn(e, scn)) != NULL) { 206 gelf_getshdr(scn, &shdr); 207 if (shdr.sh_type == SHT_SYMTAB) { 208 symtabidx = shdr.sh_link; 209 symtabdata = elf_getdata(scn, NULL); 210 } else if (shdr.sh_type == SHT_DYNSYM) { 211 dynsymidx = shdr.sh_link; 212 dynsymdata = elf_getdata(scn, NULL); 213 } else if (shdr.sh_type == SHT_PROGBITS) { 214 s = elf_strptr(e, shstridx, shdr.sh_name); 215 if (s && strcmp(s, ".SUNW_dof") == 0) { 216 dofdata = elf_getdata(scn, NULL); 217 dof = dofdata->d_buf; 218 } 219 } 220 } 221 if (dof == NULL) { 222 dprintf(1, "SUNW_dof section not found\n"); 223 elf_end(e); 224 close(efd); 225 return; 226 } 227 228 while ((char *) dof < (char *) dofdata->d_buf + dofdata->d_size) { 229 fixedprobes = 0; 230 aligned_filesz = (shdr.sh_addralign == 0 ? dof->dofh_filesz : 231 roundup2(dof->dofh_filesz, shdr.sh_addralign)); 232 dof_next = (void *) ((char *) dof + aligned_filesz); 233#endif 234 235 if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 || 236 dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 || 237 dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 || 238 dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) { 239 dprintf(0, ".SUNW_dof section corrupt\n"); 240 return; 241 } 242 243 elf = (void *)lmp->l_addr; 244 245 dh.dofhp_dof = (uintptr_t)dof; 246 dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0; 247 248 if (lmid == 0) { 249 (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 250 "%s", modname); 251 } else { 252 (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 253 "LM%lu`%s", lmid, modname); 254 } 255 256 if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL) 257 devnamep = p; 258 259 if ((fd = open64(devnamep, O_RDWR)) < 0) { 260 dprintf(1, "failed to open helper device %s", devnamep); 261#ifdef illumos 262 /* 263 * If the device path wasn't explicitly set, try again with 264 * the old device path. 265 */ 266 if (p != NULL) 267 return; 268 269 devnamep = olddevname; 270 271 if ((fd = open64(devnamep, O_RDWR)) < 0) { 272 dprintf(1, "failed to open helper device %s", devnamep); 273 return; 274 } 275#else 276 return; 277#endif 278 } 279#if !defined(sun) 280 /* 281 * We need to fix the base address of each probe since this wasn't 282 * done by ld(1). (ld(1) needs to grow support for parsing the 283 * SUNW_dof section). 284 * 285 * The complexity of this is not that great. The first for loop 286 * iterates over the sections inside the DOF file. There are usually 287 * 10 sections here. We asume the STRTAB section comes first and the 288 * PROBES section comes after. Since we are only interested in fixing 289 * data inside the PROBES section we quit the for loop after processing 290 * the PROBES section. It's usually the case that the first section 291 * is the STRTAB section and the second section is the PROBES section, 292 * so this for loop is not meaningful when doing complexity analysis. 293 * 294 * After finding the probes section, we iterate over the symbols 295 * in the symtab section. When we find a symbol name that matches 296 * the probe function name, we fix it. If we have fixed all the 297 * probes, we exit all the loops and we are done. 298 * The number of probes is given by the variable 'nprobes' and this 299 * depends entirely on the user, but some optimizations were done. 300 * 301 * We are assuming the number of probes is less than the number of 302 * symbols (libc can have 4k symbols, for example). 303 */ 304 secstart = sec = (dof_sec_t *)(dof + 1); 305 buf = (char *)dof; 306 for (i = 0; i < dof->dofh_secnum; i++, sec++) { 307 if (sec->dofs_type != DOF_SECT_PROVIDER) 308 continue; 309 310 dofprovider = (void *) (buf + sec->dofs_offset); 311 dofstrtab = secstart + dofprovider->dofpv_strtab; 312 dofprobes = secstart + dofprovider->dofpv_probes; 313 314 if (dofstrtab->dofs_type != DOF_SECT_STRTAB) { 315 fprintf(stderr, "WARNING: expected STRTAB section, but got %d\n", 316 dofstrtab->dofs_type); 317 break; 318 } 319 if (dofprobes->dofs_type != DOF_SECT_PROBES) { 320 fprintf(stderr, "WARNING: expected PROBES section, but got %d\n", 321 dofprobes->dofs_type); 322 break; 323 } 324 325 dprintf(1, "found provider %p\n", dofprovider); 326 dofstrtabraw = (char *)(buf + dofstrtab->dofs_offset); 327 nprobes = dofprobes->dofs_size / dofprobes->dofs_entsize; 328 fixsymbol(e, symtabdata, symtabidx, nprobes, buf, dofprobes, &fixedprobes, 329 dofstrtabraw); 330 if (fixedprobes != nprobes) { 331 /* 332 * If we haven't fixed all the probes using the 333 * symtab section, look inside the dynsym 334 * section. 335 */ 336 fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, dofprobes, 337 &fixedprobes, dofstrtabraw); 338 } 339 if (fixedprobes != nprobes) { 340 fprintf(stderr, "WARNING: number of probes " 341 "fixed does not match the number of " 342 "defined probes (%d != %d, " 343 "respectively)\n", fixedprobes, nprobes); 344 fprintf(stderr, "WARNING: some probes might " 345 "not fire or your program might crash\n"); 346 } 347 } 348#endif 349 if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1) 350 dprintf(1, "DTrace ioctl failed for DOF at %p", dof); 351 else { 352 dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof); 353#ifndef illumos 354 gen = dh.gen; 355#endif 356 } 357 358 (void) close(fd); 359 360#ifndef illumos 361 /* End of while loop */ 362 dof = dof_next; 363 } 364 365 elf_end(e); 366 (void) close(efd); 367#endif 368} 369 370#ifdef illumos 371#pragma fini(dtrace_dof_fini) 372#else 373static void dtrace_dof_fini(void) __attribute__ ((destructor)); 374#endif 375 376static void 377dtrace_dof_fini(void) 378{ 379 int fd; 380 381 if ((fd = open64(devnamep, O_RDWR)) < 0) { 382 dprintf(1, "failed to open helper device %s", devnamep); 383 return; 384 } 385 386 if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1) 387 dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen); 388 else 389 dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen); 390 391 (void) close(fd); 392} 393