155714Skris/* 255714Skris * CDDL HEADER START 355714Skris * 455714Skris * The contents of this file are subject to the terms of the 555714Skris * Common Development and Distribution License (the "License"). 655714Skris * You may not use this file except in compliance with the License. 755714Skris * 855714Skris * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 955714Skris * or http://www.opensolaris.org/os/licensing. 1055714Skris * See the License for the specific language governing permissions 1155714Skris * and limitations under the License. 1255714Skris * 1355714Skris * When distributing Covered Code, include this CDDL HEADER in each 1455714Skris * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1555714Skris * If applicable, add the following below this CDDL HEADER, with the 1655714Skris * fields enclosed by brackets "[]" replaced with your own identifying 1755714Skris * information: Portions Copyright [yyyy] [name of copyright owner] 1855714Skris * 1955714Skris * CDDL HEADER END 2055714Skris */ 2155714Skris/* 2255714Skris * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 2355714Skris * Copyright 2013 Voxer Inc. All rights reserved. 2455714Skris * Use is subject to license terms. 2555714Skris */ 2655714Skris 2755714Skris#include <unistd.h> 2855714Skris#include <fcntl.h> 2955714Skris#include <dlfcn.h> 3055714Skris#include <link.h> 3155714Skris#include <sys/dtrace.h> 3255714Skris 3355714Skris#include <stdarg.h> 3455714Skris#include <stdio.h> 3555714Skris#include <stdlib.h> 3655714Skris#include <string.h> 3755714Skris#include <errno.h> 3855714Skris#include <libelf.h> 3955714Skris#include <gelf.h> 4055714Skris 4155714Skris/* 4255714Skris * In Solaris 10 GA, the only mechanism for communicating helper information 4355714Skris * is through the DTrace helper pseudo-device node in /devices; there is 4455714Skris * no /dev link. Because of this, USDT providers and helper actions don't 4555714Skris * work inside of non-global zones. This issue was addressed by adding 4655714Skris * the /dev and having this initialization code use that /dev link. If the 4755714Skris * /dev link doesn't exist it falls back to looking for the /devices node 4855714Skris * as this code may be embedded in a binary which runs on Solaris 10 GA. 4955714Skris * 5055714Skris * Users may set the following environment variable to affect the way 5155714Skris * helper initialization takes place: 5255714Skris * 5355714Skris * DTRACE_DOF_INIT_DEBUG enable debugging output 5455714Skris * DTRACE_DOF_INIT_DISABLE disable helper loading 5555714Skris * DTRACE_DOF_INIT_DEVNAME set the path to the helper node 5655714Skris */ 5755714Skris 58162911Ssimonstatic const char *devnamep = "/dev/dtrace/helper"; 59162911Ssimon#if defined(sun) 60162911Ssimonstatic const char *olddevname = "/devices/pseudo/dtrace@0:helper"; 61162911Ssimon#endif 62162911Ssimon 63162911Ssimonstatic const char *modname; /* Name of this load object */ 64162911Ssimonstatic int gen; /* DOF helper generation */ 65162911Ssimon#if defined(sun) 66162911Ssimonextern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */ 67162911Ssimon#endif 68162911Ssimonstatic boolean_t dof_init_debug = B_FALSE; /* From DTRACE_DOF_INIT_DEBUG */ 69162911Ssimon 70162911Ssimonstatic void 71162911Ssimondprintf(int debug, const char *fmt, ...) 72162911Ssimon{ 73162911Ssimon va_list ap; 74162911Ssimon 75162911Ssimon if (debug && !dof_init_debug) 76162911Ssimon return; 77162911Ssimon 78162911Ssimon va_start(ap, fmt); 79162911Ssimon 80162911Ssimon if (modname == NULL) 81162911Ssimon (void) fprintf(stderr, "dtrace DOF: "); 82162911Ssimon else 83162911Ssimon (void) fprintf(stderr, "dtrace DOF %s: ", modname); 84162911Ssimon 85162911Ssimon (void) vfprintf(stderr, fmt, ap); 86162911Ssimon 87162911Ssimon if (fmt[strlen(fmt) - 1] != '\n') 88162911Ssimon (void) fprintf(stderr, ": %s\n", strerror(errno)); 89162911Ssimon 90162911Ssimon va_end(ap); 91162911Ssimon} 92162911Ssimon 93162911Ssimon#if !defined(sun) 94162911Ssimonstatic void 95162911Ssimonfixsymbol(Elf *e, Elf_Data *data, size_t idx, int nprobes, char *buf, 96162911Ssimon dof_sec_t *sec, int *fixedprobes, char *dofstrtab) 97162911Ssimon{ 98162911Ssimon GElf_Sym sym; 99162911Ssimon char *s; 100162911Ssimon unsigned char *funcname; 101162911Ssimon dof_probe_t *prb; 102162911Ssimon int j = 0; 103162911Ssimon int ndx; 104162911Ssimon 105162911Ssimon while (gelf_getsym(data, j++, &sym) != NULL) { 106162911Ssimon prb = (dof_probe_t *)(void *)(buf + sec->dofs_offset); 107162911Ssimon 108162911Ssimon for (ndx = nprobes; ndx; ndx--, prb += 1) { 109162911Ssimon funcname = dofstrtab + prb->dofpr_func; 110162911Ssimon s = elf_strptr(e, idx, sym.st_name); 11155714Skris if (strcmp(s, funcname) == 0) { 11255714Skris dprintf(1, "fixing %s() symbol\n", s); 11359191Skris prb->dofpr_addr = sym.st_value; 11459191Skris (*fixedprobes)++; 11559191Skris } 11659191Skris } 11755714Skris if (*fixedprobes == nprobes) 11855714Skris break; 11955714Skris } 12055714Skris} 12155714Skris#endif 12255714Skris 12359191Skris#if defined(sun) 12455714Skris#pragma init(dtrace_dof_init) 125238405Sjkim#else 126194206Ssimonstatic void dtrace_dof_init(void) __attribute__ ((constructor)); 127194206Ssimon#endif 128194206Ssimon 129109998Smarkmstatic void 13055714Skrisdtrace_dof_init(void) 13155714Skris{ 132109998Smarkm#if defined(sun) 13359191Skris dof_hdr_t *dof = &__SUNW_dof; 134194206Ssimon#else 135194206Ssimon dof_hdr_t *dof = NULL; 13655714Skris#endif 137194206Ssimon#ifdef _LP64 138194206Ssimon Elf64_Ehdr *elf; 139194206Ssimon#else 140194206Ssimon Elf32_Ehdr *elf; 141194206Ssimon#endif 142194206Ssimon dof_helper_t dh; 143194206Ssimon Link_map *lmp; 144194206Ssimon#if defined(sun) 145194206Ssimon Lmid_t lmid; 146194206Ssimon#else 147194206Ssimon u_long lmid = 0; 148194206Ssimon dof_sec_t *sec, *secstart, *dofstrtab, *dofprobes; 149194206Ssimon dof_provider_t *dofprovider; 15059191Skris size_t i; 15159191Skris#endif 152109998Smarkm int fd; 15355714Skris const char *p; 15455714Skris#if !defined(sun) 15555714Skris Elf *e; 15655714Skris Elf_Scn *scn = NULL; 15755714Skris Elf_Data *symtabdata = NULL, *dynsymdata = NULL, *dofdata = NULL; 15855714Skris dof_hdr_t *dof_next = NULL; 15955714Skris GElf_Shdr shdr; 16055714Skris int efd, nprobes; 16155714Skris char *s; 16255714Skris char *dofstrtabraw; 16355714Skris size_t shstridx, symtabidx = 0, dynsymidx = 0; 164238405Sjkim unsigned char *buf; 165194206Ssimon int fixedprobes; 166194206Ssimon#endif 16755714Skris 168194206Ssimon if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL) 169160814Ssimon return; 17059191Skris 17155714Skris if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL) 17259191Skris dof_init_debug = B_TRUE; 17359191Skris 17455714Skris if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) { 17555714Skris dprintf(1, "couldn't discover module name or address\n"); 176238405Sjkim return; 177194206Ssimon } 178194206Ssimon 179194206Ssimon#if defined(sun) 180238405Sjkim if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) { 181238405Sjkim dprintf(1, "couldn't discover link map ID\n"); 182194206Ssimon return; 183194206Ssimon } 184194206Ssimon#endif 185238405Sjkim 186194206Ssimon 187238405Sjkim if ((modname = strrchr(lmp->l_name, '/')) == NULL) 188194206Ssimon modname = lmp->l_name; 189194206Ssimon else 190194206Ssimon modname++; 191194206Ssimon#if !defined(sun) 192194206Ssimon elf_version(EV_CURRENT); 193194206Ssimon if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) { 194194206Ssimon dprintf(1, "couldn't open file for reading\n"); 195238405Sjkim return; 196194206Ssimon } 197238405Sjkim if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) { 198238405Sjkim dprintf(1, "elf_begin failed\n"); 199238405Sjkim close(efd); 200238405Sjkim return; 201194206Ssimon } 202194206Ssimon elf_getshdrstrndx(e, &shstridx); 203194206Ssimon dof = NULL; 204194206Ssimon while ((scn = elf_nextscn(e, scn)) != NULL) { 205194206Ssimon gelf_getshdr(scn, &shdr); 206194206Ssimon if (shdr.sh_type == SHT_SYMTAB) { 207194206Ssimon symtabidx = shdr.sh_link; 208238405Sjkim symtabdata = elf_getdata(scn, NULL); 209194206Ssimon } else if (shdr.sh_type == SHT_DYNSYM) { 210194206Ssimon dynsymidx = shdr.sh_link; 211194206Ssimon dynsymdata = elf_getdata(scn, NULL); 212194206Ssimon } else if (shdr.sh_type == SHT_PROGBITS) { 213194206Ssimon s = elf_strptr(e, shstridx, shdr.sh_name); 214194206Ssimon if (s && strcmp(s, ".SUNW_dof") == 0) { 215194206Ssimon dofdata = elf_getdata(scn, NULL); 216194206Ssimon dof = dofdata->d_buf; 217194206Ssimon } 218194206Ssimon } 219194206Ssimon } 220194206Ssimon if (dof == NULL) { 221194206Ssimon dprintf(1, "SUNW_dof section not found\n"); 222194206Ssimon elf_end(e); 223194206Ssimon close(efd); 224194206Ssimon return; 225194206Ssimon } 226238405Sjkim 227238405Sjkim while ((char *) dof < (char *) dofdata->d_buf + dofdata->d_size) { 228238405Sjkim fixedprobes = 0; 229238405Sjkim dof_next = (void *) ((char *) dof + dof->dofh_filesz); 230194206Ssimon#endif 231194206Ssimon 232238405Sjkim if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 || 233238405Sjkim dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 || 234194206Ssimon dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 || 235194206Ssimon dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) { 236194206Ssimon dprintf(0, ".SUNW_dof section corrupt\n"); 237238405Sjkim return; 238194206Ssimon } 239238405Sjkim 240238405Sjkim elf = (void *)lmp->l_addr; 241194206Ssimon 242238405Sjkim dh.dofhp_dof = (uintptr_t)dof; 243238405Sjkim dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0; 244194206Ssimon 245238405Sjkim if (lmid == 0) { 246238405Sjkim (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 247194206Ssimon "%s", modname); 248194206Ssimon } else { 249238405Sjkim (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), 250238405Sjkim "LM%lu`%s", lmid, modname); 251194206Ssimon } 252238405Sjkim 253194206Ssimon if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL) 254194206Ssimon devnamep = p; 255194206Ssimon 256194206Ssimon if ((fd = open64(devnamep, O_RDWR)) < 0) { 257194206Ssimon dprintf(1, "failed to open helper device %s", devnamep); 258194206Ssimon#if defined(sun) 259194206Ssimon /* 260194206Ssimon * If the device path wasn't explicitly set, try again with 261194206Ssimon * the old device path. 262194206Ssimon */ 263194206Ssimon if (p != NULL) 264194206Ssimon return; 265238405Sjkim 266194206Ssimon devnamep = olddevname; 267238405Sjkim 268238405Sjkim if ((fd = open64(devnamep, O_RDWR)) < 0) { 269238405Sjkim dprintf(1, "failed to open helper device %s", devnamep); 270238405Sjkim return; 271238405Sjkim } 272194206Ssimon#else 273194206Ssimon return; 274194206Ssimon#endif 275194206Ssimon } 276194206Ssimon#if !defined(sun) 277194206Ssimon /* 278238405Sjkim * We need to fix the base address of each probe since this wasn't 279194206Ssimon * done by ld(1). (ld(1) needs to grow support for parsing the 280109998Smarkm * SUNW_dof section). 28155714Skris * 28255714Skris * The complexity of this is not that great. The first for loop 28359191Skris * iterates over the sections inside the DOF file. There are usually 284238405Sjkim * 10 sections here. We asume the STRTAB section comes first and the 285194206Ssimon * PROBES section comes after. Since we are only interested in fixing 28655714Skris * data inside the PROBES section we quit the for loop after processing 287194206Ssimon * the PROBES section. It's usually the case that the first section 288194206Ssimon * is the STRTAB section and the second section is the PROBES section, 289194206Ssimon * so this for loop is not meaningful when doing complexity analysis. 290194206Ssimon * 291238405Sjkim * After finding the probes section, we iterate over the symbols 29259191Skris * in the symtab section. When we find a symbol name that matches 29355714Skris * the probe function name, we fix it. If we have fixed all the 29459191Skris * probes, we exit all the loops and we are done. 29559191Skris * The number of probes is given by the variable 'nprobes' and this 29659191Skris * depends entirely on the user, but some optimizations were done. 29759191Skris * 29859191Skris * We are assuming the number of probes is less than the number of 29959191Skris * symbols (libc can have 4k symbols, for example). 30059191Skris */ 30155714Skris secstart = sec = (dof_sec_t *)(dof + 1); 30259191Skris buf = (char *)dof; 30359191Skris for (i = 0; i < dof->dofh_secnum; i++, sec++) { 30455714Skris if (sec->dofs_type != DOF_SECT_PROVIDER) 30559191Skris continue; 30659191Skris 307100936Snectar dofprovider = (void *) (buf + sec->dofs_offset); 30855714Skris dofstrtab = secstart + dofprovider->dofpv_strtab; 30959191Skris dofprobes = secstart + dofprovider->dofpv_probes; 31059191Skris 311109998Smarkm if (dofstrtab->dofs_type != DOF_SECT_STRTAB) { 31255714Skris fprintf(stderr, "WARNING: expected STRTAB section, but got %d\n", 31359191Skris dofstrtab->dofs_type); 314160814Ssimon break; 31559191Skris } 31659191Skris if (dofprobes->dofs_type != DOF_SECT_PROBES) { 317238405Sjkim fprintf(stderr, "WARNING: expected PROBES section, but got %d\n", 31859191Skris dofprobes->dofs_type); 31955714Skris break; 32055714Skris } 32155714Skris 32255714Skris dprintf(1, "found provider %p\n", dofprovider); 32355714Skris dofstrtabraw = (char *)(buf + dofstrtab->dofs_offset); 32455714Skris nprobes = dofprobes->dofs_size / dofprobes->dofs_entsize; 32568651Skris fixsymbol(e, symtabdata, symtabidx, nprobes, buf, dofprobes, &fixedprobes, 32655714Skris dofstrtabraw); 32755714Skris if (fixedprobes != nprobes) { 32855714Skris /* 32955714Skris * If we haven't fixed all the probes using the 33055714Skris * symtab section, look inside the dynsym 33155714Skris * section. 33255714Skris */ 33355714Skris fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, dofprobes, 33455714Skris &fixedprobes, dofstrtabraw); 33555714Skris } 33655714Skris if (fixedprobes != nprobes) { 33755714Skris fprintf(stderr, "WARNING: number of probes " 33855714Skris "fixed does not match the number of " 339194206Ssimon "defined probes (%d != %d, " 34055714Skris "respectively)\n", fixedprobes, nprobes); 34155714Skris fprintf(stderr, "WARNING: some probes might " 34255714Skris "not fire or your program might crash\n"); 34355714Skris } 34455714Skris } 34555714Skris#endif 34655714Skris if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1) 34755714Skris dprintf(1, "DTrace ioctl failed for DOF at %p", dof); 34855714Skris else { 34955714Skris dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof); 35055714Skris#if !defined(sun) 35155714Skris gen = dh.gen; 35268651Skris#endif 35355714Skris } 35455714Skris 35555714Skris (void) close(fd); 35655714Skris 357160814Ssimon#if !defined(sun) 358160814Ssimon /* End of while loop */ 35955714Skris dof = dof_next; 360160814Ssimon } 361160814Ssimon 36255714Skris elf_end(e); 363160814Ssimon (void) close(efd); 364109998Smarkm#endif 36555714Skris} 36659191Skris 36755714Skris#if defined(sun) 36855714Skris#pragma fini(dtrace_dof_fini) 36955714Skris#else 37055714Skrisstatic void dtrace_dof_fini(void) __attribute__ ((destructor)); 371194206Ssimon#endif 37255714Skris 37368651Skrisstatic void 374109998Smarkmdtrace_dof_fini(void) 375194206Ssimon{ 376238405Sjkim int fd; 377238405Sjkim 378238405Sjkim if ((fd = open64(devnamep, O_RDWR)) < 0) { 379238405Sjkim dprintf(1, "failed to open helper device %s", devnamep); 380238405Sjkim return; 381238405Sjkim } 382238405Sjkim 383238405Sjkim if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, &gen)) == -1) 384238405Sjkim dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen); 385238405Sjkim else 386238405Sjkim dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen); 387194206Ssimon 388194206Ssimon (void) close(fd); 389194206Ssimon} 390194206Ssimon