1178479Sjb/* 2178479Sjb * CDDL HEADER START 3178479Sjb * 4178479Sjb * The contents of this file are subject to the terms of the 5210767Srpaulo * Common Development and Distribution License (the "License"). 6210767Srpaulo * You may not use this file except in compliance with the License. 7178479Sjb * 8178479Sjb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9178479Sjb * or http://www.opensolaris.org/os/licensing. 10178479Sjb * See the License for the specific language governing permissions 11178479Sjb * and limitations under the License. 12178479Sjb * 13178479Sjb * When distributing Covered Code, include this CDDL HEADER in each 14178479Sjb * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15178479Sjb * If applicable, add the following below this CDDL HEADER, with the 16178479Sjb * fields enclosed by brackets "[]" replaced with your own identifying 17178479Sjb * information: Portions Copyright [yyyy] [name of copyright owner] 18178479Sjb * 19178479Sjb * CDDL HEADER END 20178479Sjb */ 21178479Sjb/* 22210767Srpaulo * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23269245Smarkj * Copyright 2013 Voxer Inc. All rights reserved. 24178479Sjb * Use is subject to license terms. 25178479Sjb */ 26178479Sjb 27178479Sjb#include <unistd.h> 28178479Sjb#include <fcntl.h> 29178479Sjb#include <dlfcn.h> 30178479Sjb#include <link.h> 31178479Sjb#include <sys/dtrace.h> 32178479Sjb 33178479Sjb#include <stdarg.h> 34178479Sjb#include <stdio.h> 35178479Sjb#include <stdlib.h> 36178479Sjb#include <string.h> 37178479Sjb#include <errno.h> 38211554Srpaulo#include <libelf.h> 39211554Srpaulo#include <gelf.h> 40178479Sjb 41178479Sjb/* 42178479Sjb * In Solaris 10 GA, the only mechanism for communicating helper information 43178479Sjb * is through the DTrace helper pseudo-device node in /devices; there is 44178479Sjb * no /dev link. Because of this, USDT providers and helper actions don't 45178479Sjb * work inside of non-global zones. This issue was addressed by adding 46178479Sjb * the /dev and having this initialization code use that /dev link. If the 47178479Sjb * /dev link doesn't exist it falls back to looking for the /devices node 48178479Sjb * as this code may be embedded in a binary which runs on Solaris 10 GA. 49178479Sjb * 50178479Sjb * Users may set the following environment variable to affect the way 51178479Sjb * helper initialization takes place: 52178479Sjb * 53178479Sjb * DTRACE_DOF_INIT_DEBUG enable debugging output 54178479Sjb * DTRACE_DOF_INIT_DISABLE disable helper loading 55178479Sjb * DTRACE_DOF_INIT_DEVNAME set the path to the helper node 56178479Sjb */ 57178479Sjb 58178572Sjbstatic const char *devnamep = "/dev/dtrace/helper"; 59211554Srpaulo#if defined(sun) 60178479Sjbstatic const char *olddevname = "/devices/pseudo/dtrace@0:helper"; 61211554Srpaulo#endif 62178479Sjb 63178479Sjbstatic const char *modname; /* Name of this load object */ 64178479Sjbstatic int gen; /* DOF helper generation */ 65211554Srpaulo#if defined(sun) 66178479Sjbextern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */ 67211554Srpaulo#endif 68212462Srpaulostatic boolean_t dof_init_debug = B_FALSE; /* From DTRACE_DOF_INIT_DEBUG */ 69178479Sjb 70178479Sjbstatic void 71178479Sjbdprintf(int debug, const char *fmt, ...) 72178479Sjb{ 73178479Sjb va_list ap; 74178479Sjb 75210767Srpaulo if (debug && !dof_init_debug) 76178479Sjb return; 77178479Sjb 78178479Sjb va_start(ap, fmt); 79178479Sjb 80178479Sjb if (modname == NULL) 81178479Sjb (void) fprintf(stderr, "dtrace DOF: "); 82178479Sjb else 83178479Sjb (void) fprintf(stderr, "dtrace DOF %s: ", modname); 84178479Sjb 85178479Sjb (void) vfprintf(stderr, fmt, ap); 86178479Sjb 87178479Sjb if (fmt[strlen(fmt) - 1] != '\n') 88178479Sjb (void) fprintf(stderr, ": %s\n", strerror(errno)); 89178479Sjb 90178479Sjb va_end(ap); 91178479Sjb} 92178479Sjb 93211554Srpaulo#if !defined(sun) 94211554Srpaulostatic void 95211554Srpaulofixsymbol(Elf *e, Elf_Data *data, size_t idx, int nprobes, char *buf, 96211554Srpaulo dof_sec_t *sec, int *fixedprobes, char *dofstrtab) 97211554Srpaulo{ 98211554Srpaulo GElf_Sym sym; 99211554Srpaulo char *s; 100211554Srpaulo unsigned char *funcname; 101211554Srpaulo dof_probe_t *prb; 102211554Srpaulo int j = 0; 103211554Srpaulo int ndx; 104211554Srpaulo 105211554Srpaulo while (gelf_getsym(data, j++, &sym) != NULL) { 106228547Sdim prb = (dof_probe_t *)(void *)(buf + sec->dofs_offset); 107211554Srpaulo 108211554Srpaulo for (ndx = nprobes; ndx; ndx--, prb += 1) { 109211554Srpaulo funcname = dofstrtab + prb->dofpr_func; 110211554Srpaulo s = elf_strptr(e, idx, sym.st_name); 111211554Srpaulo if (strcmp(s, funcname) == 0) { 112211554Srpaulo dprintf(1, "fixing %s() symbol\n", s); 113211554Srpaulo prb->dofpr_addr = sym.st_value; 114211554Srpaulo (*fixedprobes)++; 115211554Srpaulo } 116211554Srpaulo } 117211554Srpaulo if (*fixedprobes == nprobes) 118211554Srpaulo break; 119211554Srpaulo } 120211554Srpaulo} 121211554Srpaulo#endif 122211554Srpaulo 123178572Sjb#if defined(sun) 124178479Sjb#pragma init(dtrace_dof_init) 125178572Sjb#else 126178572Sjbstatic void dtrace_dof_init(void) __attribute__ ((constructor)); 127178572Sjb#endif 128178572Sjb 129178479Sjbstatic void 130178479Sjbdtrace_dof_init(void) 131178479Sjb{ 132211554Srpaulo#if defined(sun) 133178479Sjb dof_hdr_t *dof = &__SUNW_dof; 134211554Srpaulo#else 135211554Srpaulo dof_hdr_t *dof = NULL; 136211554Srpaulo#endif 137178479Sjb#ifdef _LP64 138178479Sjb Elf64_Ehdr *elf; 139178479Sjb#else 140178479Sjb Elf32_Ehdr *elf; 141178479Sjb#endif 142178479Sjb dof_helper_t dh; 143211554Srpaulo Link_map *lmp; 144178572Sjb#if defined(sun) 145178479Sjb Lmid_t lmid; 146178572Sjb#else 147178572Sjb u_long lmid = 0; 148269245Smarkj dof_sec_t *sec, *secstart, *dofstrtab, *dofprobes; 149269245Smarkj dof_provider_t *dofprovider; 150211554Srpaulo size_t i; 151178572Sjb#endif 152178479Sjb int fd; 153178479Sjb const char *p; 154211554Srpaulo#if !defined(sun) 155211554Srpaulo Elf *e; 156211554Srpaulo Elf_Scn *scn = NULL; 157269245Smarkj Elf_Data *symtabdata = NULL, *dynsymdata = NULL, *dofdata = NULL; 158269245Smarkj dof_hdr_t *dof_next = NULL; 159211554Srpaulo GElf_Shdr shdr; 160211554Srpaulo int efd, nprobes; 161211554Srpaulo char *s; 162269245Smarkj char *dofstrtabraw; 163211554Srpaulo size_t shstridx, symtabidx = 0, dynsymidx = 0; 164211554Srpaulo unsigned char *buf; 165269245Smarkj int fixedprobes; 166211554Srpaulo#endif 167178479Sjb 168178479Sjb if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL) 169178479Sjb return; 170178479Sjb 171210767Srpaulo if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL) 172210767Srpaulo dof_init_debug = B_TRUE; 173210767Srpaulo 174178479Sjb if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) { 175178479Sjb dprintf(1, "couldn't discover module name or address\n"); 176178479Sjb return; 177178479Sjb } 178178479Sjb 179178572Sjb#if defined(sun) 180178479Sjb if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) { 181178479Sjb dprintf(1, "couldn't discover link map ID\n"); 182178479Sjb return; 183178479Sjb } 184178572Sjb#endif 185178479Sjb 186211554Srpaulo 187178479Sjb if ((modname = strrchr(lmp->l_name, '/')) == NULL) 188178479Sjb modname = lmp->l_name; 189178479Sjb else 190178479Sjb modname++; 191211554Srpaulo#if !defined(sun) 192211554Srpaulo elf_version(EV_CURRENT); 193211554Srpaulo if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) { 194211554Srpaulo dprintf(1, "couldn't open file for reading\n"); 195211554Srpaulo return; 196211554Srpaulo } 197211554Srpaulo if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) { 198211554Srpaulo dprintf(1, "elf_begin failed\n"); 199211554Srpaulo close(efd); 200211554Srpaulo return; 201211554Srpaulo } 202211554Srpaulo elf_getshdrstrndx(e, &shstridx); 203211554Srpaulo dof = NULL; 204211554Srpaulo while ((scn = elf_nextscn(e, scn)) != NULL) { 205211554Srpaulo gelf_getshdr(scn, &shdr); 206211554Srpaulo if (shdr.sh_type == SHT_SYMTAB) { 207211554Srpaulo symtabidx = shdr.sh_link; 208211554Srpaulo symtabdata = elf_getdata(scn, NULL); 209211554Srpaulo } else if (shdr.sh_type == SHT_DYNSYM) { 210211554Srpaulo dynsymidx = shdr.sh_link; 211211554Srpaulo dynsymdata = elf_getdata(scn, NULL); 212211554Srpaulo } else if (shdr.sh_type == SHT_PROGBITS) { 213211554Srpaulo s = elf_strptr(e, shstridx, shdr.sh_name); 214211554Srpaulo if (s && strcmp(s, ".SUNW_dof") == 0) { 215269245Smarkj dofdata = elf_getdata(scn, NULL); 216269245Smarkj dof = dofdata->d_buf; 217211554Srpaulo } 218211554Srpaulo } 219211554Srpaulo } 220211554Srpaulo if (dof == NULL) { 221211554Srpaulo dprintf(1, "SUNW_dof section not found\n"); 222211554Srpaulo elf_end(e); 223211554Srpaulo close(efd); 224211554Srpaulo return; 225211554Srpaulo } 226269245Smarkj 227269245Smarkj while ((char *) dof < (char *) dofdata->d_buf + dofdata->d_size) { 228269245Smarkj fixedprobes = 0; 229269245Smarkj dof_next = (void *) ((char *) dof + dof->dofh_filesz); 230211554Srpaulo#endif 231178479Sjb 232178479Sjb if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 || 233178479Sjb dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 || 234178479Sjb dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 || 235178479Sjb dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) { 236178479Sjb dprintf(0, ".SUNW_dof section corrupt\n"); 237178479Sjb return; 238178479Sjb } 239178479Sjb 240178479Sjb elf = (void *)lmp->l_addr; 241178479Sjb 242178479Sjb dh.dofhp_dof = (uintptr_t)dof; 243178572Sjb dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0; 244178479Sjb 245178479Sjb if (lmid == 0) { 246178479Sjb (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 247178479Sjb "%s", modname); 248178479Sjb } else { 249178479Sjb (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 250178479Sjb "LM%lu`%s", lmid, modname); 251178479Sjb } 252178479Sjb 253178479Sjb if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL) 254178572Sjb devnamep = p; 255178479Sjb 256178572Sjb if ((fd = open64(devnamep, O_RDWR)) < 0) { 257178572Sjb dprintf(1, "failed to open helper device %s", devnamep); 258211554Srpaulo#if defined(sun) 259178479Sjb /* 260178479Sjb * If the device path wasn't explicitly set, try again with 261178479Sjb * the old device path. 262178479Sjb */ 263178479Sjb if (p != NULL) 264178479Sjb return; 265178479Sjb 266178572Sjb devnamep = olddevname; 267178479Sjb 268178572Sjb if ((fd = open64(devnamep, O_RDWR)) < 0) { 269178572Sjb dprintf(1, "failed to open helper device %s", devnamep); 270178479Sjb return; 271178479Sjb } 272211554Srpaulo#else 273211554Srpaulo return; 274211554Srpaulo#endif 275178479Sjb } 276211554Srpaulo#if !defined(sun) 277211554Srpaulo /* 278211554Srpaulo * We need to fix the base address of each probe since this wasn't 279211554Srpaulo * done by ld(1). (ld(1) needs to grow support for parsing the 280211554Srpaulo * SUNW_dof section). 281211554Srpaulo * 282211554Srpaulo * The complexity of this is not that great. The first for loop 283211554Srpaulo * iterates over the sections inside the DOF file. There are usually 284211554Srpaulo * 10 sections here. We asume the STRTAB section comes first and the 285211554Srpaulo * PROBES section comes after. Since we are only interested in fixing 286211554Srpaulo * data inside the PROBES section we quit the for loop after processing 287211554Srpaulo * the PROBES section. It's usually the case that the first section 288211554Srpaulo * is the STRTAB section and the second section is the PROBES section, 289211554Srpaulo * so this for loop is not meaningful when doing complexity analysis. 290211554Srpaulo * 291211554Srpaulo * After finding the probes section, we iterate over the symbols 292211554Srpaulo * in the symtab section. When we find a symbol name that matches 293211554Srpaulo * the probe function name, we fix it. If we have fixed all the 294211554Srpaulo * probes, we exit all the loops and we are done. 295211554Srpaulo * The number of probes is given by the variable 'nprobes' and this 296211554Srpaulo * depends entirely on the user, but some optimizations were done. 297211554Srpaulo * 298211554Srpaulo * We are assuming the number of probes is less than the number of 299211554Srpaulo * symbols (libc can have 4k symbols, for example). 300211554Srpaulo */ 301269245Smarkj secstart = sec = (dof_sec_t *)(dof + 1); 302211554Srpaulo buf = (char *)dof; 303211554Srpaulo for (i = 0; i < dof->dofh_secnum; i++, sec++) { 304269245Smarkj if (sec->dofs_type != DOF_SECT_PROVIDER) 305269245Smarkj continue; 306269245Smarkj 307269245Smarkj dofprovider = (void *) (buf + sec->dofs_offset); 308269245Smarkj dofstrtab = secstart + dofprovider->dofpv_strtab; 309269245Smarkj dofprobes = secstart + dofprovider->dofpv_probes; 310269245Smarkj 311269245Smarkj if (dofstrtab->dofs_type != DOF_SECT_STRTAB) { 312269245Smarkj fprintf(stderr, "WARNING: expected STRTAB section, but got %d\n", 313269245Smarkj dofstrtab->dofs_type); 314211554Srpaulo break; 315269245Smarkj } 316269245Smarkj if (dofprobes->dofs_type != DOF_SECT_PROBES) { 317269245Smarkj fprintf(stderr, "WARNING: expected PROBES section, but got %d\n", 318269245Smarkj dofprobes->dofs_type); 319269245Smarkj break; 320269245Smarkj } 321269245Smarkj 322269245Smarkj dprintf(1, "found provider %p\n", dofprovider); 323269245Smarkj dofstrtabraw = (char *)(buf + dofstrtab->dofs_offset); 324269245Smarkj nprobes = dofprobes->dofs_size / dofprobes->dofs_entsize; 325269245Smarkj fixsymbol(e, symtabdata, symtabidx, nprobes, buf, dofprobes, &fixedprobes, 326269245Smarkj dofstrtabraw); 327269245Smarkj if (fixedprobes != nprobes) { 328269245Smarkj /* 329269245Smarkj * If we haven't fixed all the probes using the 330269245Smarkj * symtab section, look inside the dynsym 331269245Smarkj * section. 332269245Smarkj */ 333269245Smarkj fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, dofprobes, 334269245Smarkj &fixedprobes, dofstrtabraw); 335269245Smarkj } 336269245Smarkj if (fixedprobes != nprobes) { 337269245Smarkj fprintf(stderr, "WARNING: number of probes " 338269245Smarkj "fixed does not match the number of " 339269245Smarkj "defined probes (%d != %d, " 340269245Smarkj "respectively)\n", fixedprobes, nprobes); 341269245Smarkj fprintf(stderr, "WARNING: some probes might " 342269245Smarkj "not fire or your program might crash\n"); 343269245Smarkj } 344211554Srpaulo } 345211554Srpaulo#endif 346178479Sjb if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1) 347178479Sjb dprintf(1, "DTrace ioctl failed for DOF at %p", dof); 348211554Srpaulo else { 349178479Sjb dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof); 350211554Srpaulo#if !defined(sun) 351211554Srpaulo gen = dh.gen; 352211554Srpaulo#endif 353211554Srpaulo } 354178479Sjb 355178479Sjb (void) close(fd); 356269245Smarkj 357211554Srpaulo#if !defined(sun) 358269245Smarkj /* End of while loop */ 359269245Smarkj dof = dof_next; 360269245Smarkj } 361269245Smarkj 362211554Srpaulo elf_end(e); 363211554Srpaulo (void) close(efd); 364211554Srpaulo#endif 365178479Sjb} 366178479Sjb 367178572Sjb#if defined(sun) 368178479Sjb#pragma fini(dtrace_dof_fini) 369178572Sjb#else 370178572Sjbstatic void dtrace_dof_fini(void) __attribute__ ((destructor)); 371178572Sjb#endif 372178572Sjb 373178479Sjbstatic void 374178479Sjbdtrace_dof_fini(void) 375178479Sjb{ 376178479Sjb int fd; 377178479Sjb 378178572Sjb if ((fd = open64(devnamep, O_RDWR)) < 0) { 379178572Sjb dprintf(1, "failed to open helper device %s", devnamep); 380178479Sjb return; 381178479Sjb } 382178479Sjb 383211554Srpaulo if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1) 384178479Sjb dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen); 385178479Sjb else 386178479Sjb dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen); 387178479Sjb 388178479Sjb (void) close(fd); 389178479Sjb} 390