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. 23178479Sjb * Use is subject to license terms. 24178479Sjb */ 25178479Sjb 26178479Sjb#include <unistd.h> 27178479Sjb#include <fcntl.h> 28178479Sjb#include <dlfcn.h> 29178479Sjb#include <link.h> 30178479Sjb#include <sys/dtrace.h> 31178479Sjb 32178479Sjb#include <stdarg.h> 33178479Sjb#include <stdio.h> 34178479Sjb#include <stdlib.h> 35178479Sjb#include <string.h> 36178479Sjb#include <errno.h> 37211554Srpaulo#include <libelf.h> 38211554Srpaulo#include <gelf.h> 39178479Sjb 40178479Sjb/* 41178479Sjb * In Solaris 10 GA, the only mechanism for communicating helper information 42178479Sjb * is through the DTrace helper pseudo-device node in /devices; there is 43178479Sjb * no /dev link. Because of this, USDT providers and helper actions don't 44178479Sjb * work inside of non-global zones. This issue was addressed by adding 45178479Sjb * the /dev and having this initialization code use that /dev link. If the 46178479Sjb * /dev link doesn't exist it falls back to looking for the /devices node 47178479Sjb * as this code may be embedded in a binary which runs on Solaris 10 GA. 48178479Sjb * 49178479Sjb * Users may set the following environment variable to affect the way 50178479Sjb * helper initialization takes place: 51178479Sjb * 52178479Sjb * DTRACE_DOF_INIT_DEBUG enable debugging output 53178479Sjb * DTRACE_DOF_INIT_DISABLE disable helper loading 54178479Sjb * DTRACE_DOF_INIT_DEVNAME set the path to the helper node 55178479Sjb */ 56178479Sjb 57178572Sjbstatic const char *devnamep = "/dev/dtrace/helper"; 58211554Srpaulo#if defined(sun) 59178479Sjbstatic const char *olddevname = "/devices/pseudo/dtrace@0:helper"; 60211554Srpaulo#endif 61178479Sjb 62178479Sjbstatic const char *modname; /* Name of this load object */ 63178479Sjbstatic int gen; /* DOF helper generation */ 64211554Srpaulo#if defined(sun) 65178479Sjbextern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */ 66211554Srpaulo#endif 67212462Srpaulostatic boolean_t dof_init_debug = B_FALSE; /* From DTRACE_DOF_INIT_DEBUG */ 68178479Sjb 69178479Sjbstatic void 70178479Sjbdprintf(int debug, const char *fmt, ...) 71178479Sjb{ 72178479Sjb va_list ap; 73178479Sjb 74210767Srpaulo if (debug && !dof_init_debug) 75178479Sjb return; 76178479Sjb 77178479Sjb va_start(ap, fmt); 78178479Sjb 79178479Sjb if (modname == NULL) 80178479Sjb (void) fprintf(stderr, "dtrace DOF: "); 81178479Sjb else 82178479Sjb (void) fprintf(stderr, "dtrace DOF %s: ", modname); 83178479Sjb 84178479Sjb (void) vfprintf(stderr, fmt, ap); 85178479Sjb 86178479Sjb if (fmt[strlen(fmt) - 1] != '\n') 87178479Sjb (void) fprintf(stderr, ": %s\n", strerror(errno)); 88178479Sjb 89178479Sjb va_end(ap); 90178479Sjb} 91178479Sjb 92211554Srpaulo#if !defined(sun) 93211554Srpaulostatic void 94211554Srpaulofixsymbol(Elf *e, Elf_Data *data, size_t idx, int nprobes, char *buf, 95211554Srpaulo dof_sec_t *sec, int *fixedprobes, char *dofstrtab) 96211554Srpaulo{ 97211554Srpaulo GElf_Sym sym; 98211554Srpaulo char *s; 99211554Srpaulo unsigned char *funcname; 100211554Srpaulo dof_probe_t *prb; 101211554Srpaulo int j = 0; 102211554Srpaulo int ndx; 103211554Srpaulo 104211554Srpaulo while (gelf_getsym(data, j++, &sym) != NULL) { 105229088Sdim prb = (dof_probe_t *)(void *)(buf + sec->dofs_offset); 106211554Srpaulo 107211554Srpaulo for (ndx = nprobes; ndx; ndx--, prb += 1) { 108211554Srpaulo funcname = dofstrtab + prb->dofpr_func; 109211554Srpaulo s = elf_strptr(e, idx, sym.st_name); 110211554Srpaulo if (strcmp(s, funcname) == 0) { 111211554Srpaulo dprintf(1, "fixing %s() symbol\n", s); 112211554Srpaulo prb->dofpr_addr = sym.st_value; 113211554Srpaulo (*fixedprobes)++; 114211554Srpaulo } 115211554Srpaulo } 116211554Srpaulo if (*fixedprobes == nprobes) 117211554Srpaulo break; 118211554Srpaulo } 119211554Srpaulo} 120211554Srpaulo#endif 121211554Srpaulo 122178572Sjb#if defined(sun) 123178479Sjb#pragma init(dtrace_dof_init) 124178572Sjb#else 125178572Sjbstatic void dtrace_dof_init(void) __attribute__ ((constructor)); 126178572Sjb#endif 127178572Sjb 128178479Sjbstatic void 129178479Sjbdtrace_dof_init(void) 130178479Sjb{ 131211554Srpaulo#if defined(sun) 132178479Sjb dof_hdr_t *dof = &__SUNW_dof; 133211554Srpaulo#else 134211554Srpaulo dof_hdr_t *dof = NULL; 135211554Srpaulo#endif 136178479Sjb#ifdef _LP64 137178479Sjb Elf64_Ehdr *elf; 138178479Sjb#else 139178479Sjb Elf32_Ehdr *elf; 140178479Sjb#endif 141178479Sjb dof_helper_t dh; 142211554Srpaulo Link_map *lmp; 143178572Sjb#if defined(sun) 144178479Sjb Lmid_t lmid; 145178572Sjb#else 146178572Sjb u_long lmid = 0; 147211554Srpaulo dof_sec_t *sec; 148211554Srpaulo size_t i; 149178572Sjb#endif 150178479Sjb int fd; 151178479Sjb const char *p; 152211554Srpaulo#if !defined(sun) 153211554Srpaulo Elf *e; 154211554Srpaulo Elf_Scn *scn = NULL; 155211554Srpaulo Elf_Data *symtabdata = NULL, *dynsymdata = NULL; 156211554Srpaulo GElf_Shdr shdr; 157211554Srpaulo int efd, nprobes; 158211554Srpaulo char *s; 159211554Srpaulo size_t shstridx, symtabidx = 0, dynsymidx = 0; 160211554Srpaulo unsigned char *dofstrtab = NULL; 161211554Srpaulo unsigned char *buf; 162211554Srpaulo int fixedprobes = 0; 163211554Srpaulo#endif 164178479Sjb 165178479Sjb if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL) 166178479Sjb return; 167178479Sjb 168210767Srpaulo if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL) 169210767Srpaulo dof_init_debug = B_TRUE; 170210767Srpaulo 171178479Sjb if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) { 172178479Sjb dprintf(1, "couldn't discover module name or address\n"); 173178479Sjb return; 174178479Sjb } 175178479Sjb 176178572Sjb#if defined(sun) 177178479Sjb if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) { 178178479Sjb dprintf(1, "couldn't discover link map ID\n"); 179178479Sjb return; 180178479Sjb } 181178572Sjb#endif 182178479Sjb 183211554Srpaulo 184178479Sjb if ((modname = strrchr(lmp->l_name, '/')) == NULL) 185178479Sjb modname = lmp->l_name; 186178479Sjb else 187178479Sjb modname++; 188211554Srpaulo#if !defined(sun) 189211554Srpaulo elf_version(EV_CURRENT); 190211554Srpaulo if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) { 191211554Srpaulo dprintf(1, "couldn't open file for reading\n"); 192211554Srpaulo return; 193211554Srpaulo } 194211554Srpaulo if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) { 195211554Srpaulo dprintf(1, "elf_begin failed\n"); 196211554Srpaulo close(efd); 197211554Srpaulo return; 198211554Srpaulo } 199211554Srpaulo elf_getshdrstrndx(e, &shstridx); 200211554Srpaulo dof = NULL; 201211554Srpaulo while ((scn = elf_nextscn(e, scn)) != NULL) { 202211554Srpaulo gelf_getshdr(scn, &shdr); 203211554Srpaulo if (shdr.sh_type == SHT_SYMTAB) { 204211554Srpaulo symtabidx = shdr.sh_link; 205211554Srpaulo symtabdata = elf_getdata(scn, NULL); 206211554Srpaulo } else if (shdr.sh_type == SHT_DYNSYM) { 207211554Srpaulo dynsymidx = shdr.sh_link; 208211554Srpaulo dynsymdata = elf_getdata(scn, NULL); 209211554Srpaulo } else if (shdr.sh_type == SHT_PROGBITS) { 210211554Srpaulo s = elf_strptr(e, shstridx, shdr.sh_name); 211211554Srpaulo if (s && strcmp(s, ".SUNW_dof") == 0) { 212211554Srpaulo dof = elf_getdata(scn, NULL)->d_buf; 213211554Srpaulo } 214211554Srpaulo } 215211554Srpaulo } 216211554Srpaulo if (dof == NULL) { 217211554Srpaulo dprintf(1, "SUNW_dof section not found\n"); 218211554Srpaulo elf_end(e); 219211554Srpaulo close(efd); 220211554Srpaulo return; 221211554Srpaulo } 222211554Srpaulo#endif 223178479Sjb 224178479Sjb if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 || 225178479Sjb dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 || 226178479Sjb dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 || 227178479Sjb dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) { 228178479Sjb dprintf(0, ".SUNW_dof section corrupt\n"); 229178479Sjb return; 230178479Sjb } 231178479Sjb 232178479Sjb elf = (void *)lmp->l_addr; 233178479Sjb 234178479Sjb dh.dofhp_dof = (uintptr_t)dof; 235178572Sjb dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0; 236178479Sjb 237178479Sjb if (lmid == 0) { 238178479Sjb (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 239178479Sjb "%s", modname); 240178479Sjb } else { 241178479Sjb (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 242178479Sjb "LM%lu`%s", lmid, modname); 243178479Sjb } 244178479Sjb 245178479Sjb if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL) 246178572Sjb devnamep = p; 247178479Sjb 248178572Sjb if ((fd = open64(devnamep, O_RDWR)) < 0) { 249178572Sjb dprintf(1, "failed to open helper device %s", devnamep); 250211554Srpaulo#if defined(sun) 251178479Sjb /* 252178479Sjb * If the device path wasn't explicitly set, try again with 253178479Sjb * the old device path. 254178479Sjb */ 255178479Sjb if (p != NULL) 256178479Sjb return; 257178479Sjb 258178572Sjb devnamep = olddevname; 259178479Sjb 260178572Sjb if ((fd = open64(devnamep, O_RDWR)) < 0) { 261178572Sjb dprintf(1, "failed to open helper device %s", devnamep); 262178479Sjb return; 263178479Sjb } 264211554Srpaulo#else 265211554Srpaulo return; 266211554Srpaulo#endif 267178479Sjb } 268211554Srpaulo#if !defined(sun) 269211554Srpaulo /* 270211554Srpaulo * We need to fix the base address of each probe since this wasn't 271211554Srpaulo * done by ld(1). (ld(1) needs to grow support for parsing the 272211554Srpaulo * SUNW_dof section). 273211554Srpaulo * 274211554Srpaulo * The complexity of this is not that great. The first for loop 275211554Srpaulo * iterates over the sections inside the DOF file. There are usually 276211554Srpaulo * 10 sections here. We asume the STRTAB section comes first and the 277211554Srpaulo * PROBES section comes after. Since we are only interested in fixing 278211554Srpaulo * data inside the PROBES section we quit the for loop after processing 279211554Srpaulo * the PROBES section. It's usually the case that the first section 280211554Srpaulo * is the STRTAB section and the second section is the PROBES section, 281211554Srpaulo * so this for loop is not meaningful when doing complexity analysis. 282211554Srpaulo * 283211554Srpaulo * After finding the probes section, we iterate over the symbols 284211554Srpaulo * in the symtab section. When we find a symbol name that matches 285211554Srpaulo * the probe function name, we fix it. If we have fixed all the 286211554Srpaulo * probes, we exit all the loops and we are done. 287211554Srpaulo * The number of probes is given by the variable 'nprobes' and this 288211554Srpaulo * depends entirely on the user, but some optimizations were done. 289211554Srpaulo * 290211554Srpaulo * We are assuming the number of probes is less than the number of 291211554Srpaulo * symbols (libc can have 4k symbols, for example). 292211554Srpaulo */ 293211554Srpaulo sec = (dof_sec_t *)(dof + 1); 294211554Srpaulo buf = (char *)dof; 295211554Srpaulo for (i = 0; i < dof->dofh_secnum; i++, sec++) { 296211554Srpaulo if (sec->dofs_type == DOF_SECT_STRTAB) 297211554Srpaulo dofstrtab = (unsigned char *)(buf + sec->dofs_offset); 298211554Srpaulo else if (sec->dofs_type == DOF_SECT_PROBES && dofstrtab) 299211554Srpaulo break; 300211554Srpaulo 301211554Srpaulo } 302211554Srpaulo nprobes = sec->dofs_size / sec->dofs_entsize; 303211554Srpaulo fixsymbol(e, symtabdata, symtabidx, nprobes, buf, sec, &fixedprobes, 304211554Srpaulo dofstrtab); 305211554Srpaulo if (fixedprobes != nprobes) { 306211554Srpaulo /* 307211554Srpaulo * If we haven't fixed all the probes using the 308211554Srpaulo * symtab section, look inside the dynsym 309211554Srpaulo * section. 310211554Srpaulo */ 311211554Srpaulo fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, sec, 312211554Srpaulo &fixedprobes, dofstrtab); 313211554Srpaulo } 314211554Srpaulo if (fixedprobes != nprobes) { 315211554Srpaulo fprintf(stderr, "WARNING: number of probes " 316211554Srpaulo "fixed does not match the number of " 317211554Srpaulo "defined probes (%d != %d, " 318211554Srpaulo "respectively)\n", fixedprobes, nprobes); 319211554Srpaulo fprintf(stderr, "WARNING: some probes might " 320211554Srpaulo "not fire or your program might crash\n"); 321211554Srpaulo } 322211554Srpaulo#endif 323178479Sjb if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1) 324178479Sjb dprintf(1, "DTrace ioctl failed for DOF at %p", dof); 325211554Srpaulo else { 326178479Sjb dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof); 327211554Srpaulo#if !defined(sun) 328211554Srpaulo gen = dh.gen; 329211554Srpaulo#endif 330211554Srpaulo } 331178479Sjb 332178479Sjb (void) close(fd); 333211554Srpaulo#if !defined(sun) 334211554Srpaulo elf_end(e); 335211554Srpaulo (void) close(efd); 336211554Srpaulo#endif 337178479Sjb} 338178479Sjb 339178572Sjb#if defined(sun) 340178479Sjb#pragma fini(dtrace_dof_fini) 341178572Sjb#else 342178572Sjbstatic void dtrace_dof_fini(void) __attribute__ ((destructor)); 343178572Sjb#endif 344178572Sjb 345178479Sjbstatic void 346178479Sjbdtrace_dof_fini(void) 347178479Sjb{ 348178479Sjb int fd; 349178479Sjb 350178572Sjb if ((fd = open64(devnamep, O_RDWR)) < 0) { 351178572Sjb dprintf(1, "failed to open helper device %s", devnamep); 352178479Sjb return; 353178479Sjb } 354178479Sjb 355211554Srpaulo if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1) 356178479Sjb dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen); 357178479Sjb else 358178479Sjb dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen); 359178479Sjb 360178479Sjb (void) close(fd); 361178479Sjb} 362