drti.c revision 212462
138032Speter/* 2111823Sgshapiro * CDDL HEADER START 364562Sgshapiro * 438032Speter * The contents of this file are subject to the terms of the 538032Speter * Common Development and Distribution License (the "License"). 638032Speter * You may not use this file except in compliance with the License. 738032Speter * 838032Speter * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 938032Speter * or http://www.opensolaris.org/os/licensing. 1038032Speter * See the License for the specific language governing permissions 1138032Speter * and limitations under the License. 1238032Speter * 1338032Speter * When distributing Covered Code, include this CDDL HEADER in each 1464562Sgshapiro * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1590792Sgshapiro * If applicable, add the following below this CDDL HEADER, with the 1690792Sgshapiro * fields enclosed by brackets "[]" replaced with your own identifying 1790792Sgshapiro * information: Portions Copyright [yyyy] [name of copyright owner] 1864562Sgshapiro * 19111823Sgshapiro * CDDL HEADER END 2064562Sgshapiro */ 2190792Sgshapiro/* 2290792Sgshapiro * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 2390792Sgshapiro * Use is subject to license terms. 2490792Sgshapiro */ 2590792Sgshapiro 2690792Sgshapiro#include <unistd.h> 2764562Sgshapiro#include <fcntl.h> 2890792Sgshapiro#include <dlfcn.h> 2990792Sgshapiro#include <link.h> 3090792Sgshapiro#include <sys/dtrace.h> 3138032Speter 3290792Sgshapiro#include <stdarg.h> 3390792Sgshapiro#include <stdio.h> 3438032Speter#include <stdlib.h> 3590792Sgshapiro#include <string.h> 3690792Sgshapiro#include <errno.h> 3790792Sgshapiro#include <libelf.h> 3890792Sgshapiro#include <gelf.h> 3990792Sgshapiro 4090792Sgshapiro/* 4190792Sgshapiro * In Solaris 10 GA, the only mechanism for communicating helper information 4290792Sgshapiro * is through the DTrace helper pseudo-device node in /devices; there is 4390792Sgshapiro * no /dev link. Because of this, USDT providers and helper actions don't 4490792Sgshapiro * work inside of non-global zones. This issue was addressed by adding 4590792Sgshapiro * the /dev and having this initialization code use that /dev link. If the 4690792Sgshapiro * /dev link doesn't exist it falls back to looking for the /devices node 4790792Sgshapiro * as this code may be embedded in a binary which runs on Solaris 10 GA. 4890792Sgshapiro * 4990792Sgshapiro * Users may set the following environment variable to affect the way 5090792Sgshapiro * helper initialization takes place: 5190792Sgshapiro * 5290792Sgshapiro * DTRACE_DOF_INIT_DEBUG enable debugging output 5390792Sgshapiro * DTRACE_DOF_INIT_DISABLE disable helper loading 5490792Sgshapiro * DTRACE_DOF_INIT_DEVNAME set the path to the helper node 5590792Sgshapiro */ 5690792Sgshapiro 5790792Sgshapirostatic const char *devnamep = "/dev/dtrace/helper"; 5890792Sgshapiro#if defined(sun) 5990792Sgshapirostatic const char *olddevname = "/devices/pseudo/dtrace@0:helper"; 6090792Sgshapiro#endif 6190792Sgshapiro 6290792Sgshapirostatic const char *modname; /* Name of this load object */ 6390792Sgshapirostatic int gen; /* DOF helper generation */ 6464562Sgshapiro#if defined(sun) 6564562Sgshapiroextern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */ 6664562Sgshapiro#endif 6764562Sgshapirostatic boolean_t dof_init_debug = B_FALSE; /* From DTRACE_DOF_INIT_DEBUG */ 6864562Sgshapiro 6990792Sgshapirostatic void 7064562Sgshapirodprintf(int debug, const char *fmt, ...) 7138032Speter{ 7290792Sgshapiro va_list ap; 7390792Sgshapiro 7490792Sgshapiro if (debug && !dof_init_debug) 7538032Speter return; 7638032Speter 7738032Speter va_start(ap, fmt); 7838032Speter 7938032Speter if (modname == NULL) 8090792Sgshapiro (void) fprintf(stderr, "dtrace DOF: "); 8190792Sgshapiro else 8238032Speter (void) fprintf(stderr, "dtrace DOF %s: ", modname); 8338032Speter 8438032Speter (void) vfprintf(stderr, fmt, ap); 8538032Speter 8638032Speter if (fmt[strlen(fmt) - 1] != '\n') 8738032Speter (void) fprintf(stderr, ": %s\n", strerror(errno)); 8890792Sgshapiro 8938032Speter va_end(ap); 9038032Speter} 9190792Sgshapiro 9290792Sgshapiro#if !defined(sun) 9390792Sgshapirostatic void 9490792Sgshapirofixsymbol(Elf *e, Elf_Data *data, size_t idx, int nprobes, char *buf, 9590792Sgshapiro dof_sec_t *sec, int *fixedprobes, char *dofstrtab) 9690792Sgshapiro{ 9790792Sgshapiro GElf_Sym sym; 9838032Speter char *s; 9938032Speter unsigned char *funcname; 10064562Sgshapiro dof_probe_t *prb; 10164562Sgshapiro int j = 0; 10238032Speter int ndx; 10338032Speter 10464562Sgshapiro while (gelf_getsym(data, j++, &sym) != NULL) { 10590792Sgshapiro prb = (dof_probe_t *)(buf + sec->dofs_offset); 10690792Sgshapiro 10790792Sgshapiro for (ndx = nprobes; ndx; ndx--, prb += 1) { 10890792Sgshapiro funcname = dofstrtab + prb->dofpr_func; 10990792Sgshapiro s = elf_strptr(e, idx, sym.st_name); 11090792Sgshapiro if (strcmp(s, funcname) == 0) { 11190792Sgshapiro dprintf(1, "fixing %s() symbol\n", s); 11290792Sgshapiro prb->dofpr_addr = sym.st_value; 11390792Sgshapiro (*fixedprobes)++; 11490792Sgshapiro } 11590792Sgshapiro } 11690792Sgshapiro if (*fixedprobes == nprobes) 11790792Sgshapiro break; 11890792Sgshapiro } 11990792Sgshapiro} 12090792Sgshapiro#endif 12190792Sgshapiro 12290792Sgshapiro#if defined(sun) 12390792Sgshapiro#pragma init(dtrace_dof_init) 12438032Speter#else 12590792Sgshapirostatic void dtrace_dof_init(void) __attribute__ ((constructor)); 12664562Sgshapiro#endif 12790792Sgshapiro 12838032Speterstatic void 12990792Sgshapirodtrace_dof_init(void) 13038032Speter{ 13190792Sgshapiro#if defined(sun) 13290792Sgshapiro dof_hdr_t *dof = &__SUNW_dof; 13338032Speter#else 13466494Sgshapiro dof_hdr_t *dof = NULL; 13590792Sgshapiro#endif 13666494Sgshapiro#ifdef _LP64 13766494Sgshapiro Elf64_Ehdr *elf; 13838032Speter#else 13938032Speter Elf32_Ehdr *elf; 14038032Speter#endif 14138032Speter dof_helper_t dh; 14238032Speter Link_map *lmp; 14338032Speter#if defined(sun) 14438032Speter Lmid_t lmid; 14538032Speter#else 14638032Speter u_long lmid = 0; 14738032Speter dof_sec_t *sec; 14838032Speter size_t i; 14938032Speter#endif 15038032Speter int fd; 15138032Speter const char *p; 15238032Speter#if !defined(sun) 15364562Sgshapiro Elf *e; 15464562Sgshapiro Elf_Scn *scn = NULL; 15564562Sgshapiro Elf_Data *symtabdata = NULL, *dynsymdata = NULL; 15664562Sgshapiro GElf_Shdr shdr; 15790792Sgshapiro int efd, nprobes; 15864562Sgshapiro char *s; 15990792Sgshapiro size_t shstridx, symtabidx = 0, dynsymidx = 0; 16090792Sgshapiro unsigned char *dofstrtab = NULL; 16164562Sgshapiro unsigned char *buf; 16290792Sgshapiro int fixedprobes = 0; 16338032Speter#endif 16438032Speter 16538032Speter if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL) 16638032Speter return; 16738032Speter 16838032Speter if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL) 16938032Speter dof_init_debug = B_TRUE; 17038032Speter 17164562Sgshapiro if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) { 17238032Speter dprintf(1, "couldn't discover module name or address\n"); 17390792Sgshapiro return; 17490792Sgshapiro } 17594334Sgshapiro 17690792Sgshapiro#if defined(sun) 17790792Sgshapiro if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) { 17894334Sgshapiro dprintf(1, "couldn't discover link map ID\n"); 17990792Sgshapiro return; 18090792Sgshapiro } 18194334Sgshapiro#endif 18290792Sgshapiro 18390792Sgshapiro 18494334Sgshapiro if ((modname = strrchr(lmp->l_name, '/')) == NULL) 18590792Sgshapiro modname = lmp->l_name; 18690792Sgshapiro else 18794334Sgshapiro modname++; 18890792Sgshapiro#if !defined(sun) 18990792Sgshapiro elf_version(EV_CURRENT); 19094334Sgshapiro if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) { 19138032Speter dprintf(1, "couldn't open file for reading\n"); 19290792Sgshapiro return; 19390792Sgshapiro } 19490792Sgshapiro if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) { 19590792Sgshapiro dprintf(1, "elf_begin failed\n"); 19638032Speter close(efd); 19790792Sgshapiro return; 19890792Sgshapiro } 19990792Sgshapiro elf_getshdrstrndx(e, &shstridx); 20090792Sgshapiro dof = NULL; 20190792Sgshapiro while ((scn = elf_nextscn(e, scn)) != NULL) { 20290792Sgshapiro gelf_getshdr(scn, &shdr); 20390792Sgshapiro if (shdr.sh_type == SHT_SYMTAB) { 20490792Sgshapiro symtabidx = shdr.sh_link; 20590792Sgshapiro symtabdata = elf_getdata(scn, NULL); 20690792Sgshapiro } else if (shdr.sh_type == SHT_DYNSYM) { 20790792Sgshapiro dynsymidx = shdr.sh_link; 20890792Sgshapiro dynsymdata = elf_getdata(scn, NULL); 20990792Sgshapiro } else if (shdr.sh_type == SHT_PROGBITS) { 21090792Sgshapiro s = elf_strptr(e, shstridx, shdr.sh_name); 21190792Sgshapiro if (s && strcmp(s, ".SUNW_dof") == 0) { 21290792Sgshapiro dof = elf_getdata(scn, NULL)->d_buf; 21390792Sgshapiro } 21490792Sgshapiro } 21590792Sgshapiro } 21690792Sgshapiro if (dof == NULL) { 21790792Sgshapiro dprintf(1, "SUNW_dof section not found\n"); 21890792Sgshapiro elf_end(e); 21990792Sgshapiro close(efd); 22090792Sgshapiro return; 221110560Sgshapiro } 222110560Sgshapiro#endif 223110560Sgshapiro 224110560Sgshapiro if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 || 225110560Sgshapiro dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 || 226110560Sgshapiro dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 || 227110560Sgshapiro dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) { 228110560Sgshapiro dprintf(0, ".SUNW_dof section corrupt\n"); 229110560Sgshapiro return; 230110560Sgshapiro } 231110560Sgshapiro 232110560Sgshapiro elf = (void *)lmp->l_addr; 23390792Sgshapiro 23490792Sgshapiro dh.dofhp_dof = (uintptr_t)dof; 23590792Sgshapiro dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0; 23690792Sgshapiro 23790792Sgshapiro if (lmid == 0) { 23890792Sgshapiro (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 23990792Sgshapiro "%s", modname); 24090792Sgshapiro } else { 24190792Sgshapiro (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 24290792Sgshapiro "LM%lu`%s", lmid, modname); 24390792Sgshapiro } 24490792Sgshapiro 24590792Sgshapiro if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL) 24690792Sgshapiro devnamep = p; 24790792Sgshapiro 248110560Sgshapiro if ((fd = open64(devnamep, O_RDWR)) < 0) { 24990792Sgshapiro dprintf(1, "failed to open helper device %s", devnamep); 25090792Sgshapiro#if defined(sun) 25190792Sgshapiro /* 25290792Sgshapiro * If the device path wasn't explicitly set, try again with 25390792Sgshapiro * the old device path. 25490792Sgshapiro */ 25590792Sgshapiro if (p != NULL) 25690792Sgshapiro return; 25790792Sgshapiro 25890792Sgshapiro devnamep = olddevname; 25990792Sgshapiro 26090792Sgshapiro if ((fd = open64(devnamep, O_RDWR)) < 0) { 26190792Sgshapiro dprintf(1, "failed to open helper device %s", devnamep); 26290792Sgshapiro return; 26390792Sgshapiro } 26490792Sgshapiro#else 26590792Sgshapiro return; 26690792Sgshapiro#endif 26790792Sgshapiro } 26890792Sgshapiro#if !defined(sun) 26990792Sgshapiro /* 27090792Sgshapiro * We need to fix the base address of each probe since this wasn't 27190792Sgshapiro * done by ld(1). (ld(1) needs to grow support for parsing the 27290792Sgshapiro * SUNW_dof section). 27390792Sgshapiro * 27490792Sgshapiro * The complexity of this is not that great. The first for loop 27590792Sgshapiro * iterates over the sections inside the DOF file. There are usually 27690792Sgshapiro * 10 sections here. We asume the STRTAB section comes first and the 27790792Sgshapiro * PROBES section comes after. Since we are only interested in fixing 27890792Sgshapiro * data inside the PROBES section we quit the for loop after processing 27990792Sgshapiro * the PROBES section. It's usually the case that the first section 28090792Sgshapiro * is the STRTAB section and the second section is the PROBES section, 28190792Sgshapiro * so this for loop is not meaningful when doing complexity analysis. 28290792Sgshapiro * 28390792Sgshapiro * After finding the probes section, we iterate over the symbols 28490792Sgshapiro * in the symtab section. When we find a symbol name that matches 28590792Sgshapiro * the probe function name, we fix it. If we have fixed all the 28690792Sgshapiro * probes, we exit all the loops and we are done. 28790792Sgshapiro * The number of probes is given by the variable 'nprobes' and this 28890792Sgshapiro * depends entirely on the user, but some optimizations were done. 28990792Sgshapiro * 29090792Sgshapiro * We are assuming the number of probes is less than the number of 29190792Sgshapiro * symbols (libc can have 4k symbols, for example). 29290792Sgshapiro */ 29390792Sgshapiro sec = (dof_sec_t *)(dof + 1); 29490792Sgshapiro buf = (char *)dof; 29590792Sgshapiro for (i = 0; i < dof->dofh_secnum; i++, sec++) { 29690792Sgshapiro if (sec->dofs_type == DOF_SECT_STRTAB) 29790792Sgshapiro dofstrtab = (unsigned char *)(buf + sec->dofs_offset); 29890792Sgshapiro else if (sec->dofs_type == DOF_SECT_PROBES && dofstrtab) 29990792Sgshapiro break; 30090792Sgshapiro 30190792Sgshapiro } 30290792Sgshapiro nprobes = sec->dofs_size / sec->dofs_entsize; 30390792Sgshapiro fixsymbol(e, symtabdata, symtabidx, nprobes, buf, sec, &fixedprobes, 30490792Sgshapiro dofstrtab); 30590792Sgshapiro if (fixedprobes != nprobes) { 30690792Sgshapiro /* 30790792Sgshapiro * If we haven't fixed all the probes using the 30890792Sgshapiro * symtab section, look inside the dynsym 30990792Sgshapiro * section. 31090792Sgshapiro */ 31190792Sgshapiro fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, sec, 31290792Sgshapiro &fixedprobes, dofstrtab); 31390792Sgshapiro } 31490792Sgshapiro if (fixedprobes != nprobes) { 31590792Sgshapiro fprintf(stderr, "WARNING: number of probes " 31690792Sgshapiro "fixed does not match the number of " 31790792Sgshapiro "defined probes (%d != %d, " 31890792Sgshapiro "respectively)\n", fixedprobes, nprobes); 31990792Sgshapiro fprintf(stderr, "WARNING: some probes might " 32090792Sgshapiro "not fire or your program might crash\n"); 32190792Sgshapiro } 32290792Sgshapiro#endif 32390792Sgshapiro if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1) 32490792Sgshapiro dprintf(1, "DTrace ioctl failed for DOF at %p", dof); 32590792Sgshapiro else { 32690792Sgshapiro dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof); 32790792Sgshapiro#if !defined(sun) 32890792Sgshapiro gen = dh.gen; 32990792Sgshapiro#endif 33090792Sgshapiro } 33190792Sgshapiro 33290792Sgshapiro (void) close(fd); 33390792Sgshapiro#if !defined(sun) 33490792Sgshapiro elf_end(e); 33590792Sgshapiro (void) close(efd); 33690792Sgshapiro#endif 33790792Sgshapiro} 33890792Sgshapiro 33990792Sgshapiro#if defined(sun) 34090792Sgshapiro#pragma fini(dtrace_dof_fini) 34190792Sgshapiro#else 34290792Sgshapirostatic void dtrace_dof_fini(void) __attribute__ ((destructor)); 34390792Sgshapiro#endif 34490792Sgshapiro 34590792Sgshapirostatic void 34638032Speterdtrace_dof_fini(void) 34764562Sgshapiro{ 34864562Sgshapiro int fd; 34964562Sgshapiro 35038032Speter if ((fd = open64(devnamep, O_RDWR)) < 0) { 35138032Speter dprintf(1, "failed to open helper device %s", devnamep); 35238032Speter return; 35364562Sgshapiro } 35438032Speter 35538032Speter if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1) 35638032Speter dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen); 35738032Speter else 35838032Speter dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen); 35938032Speter 36038032Speter (void) close(fd); 36138032Speter} 36238032Speter