diff options
author | markj <markj@FreeBSD.org> | 2014-09-11 01:04:56 +0000 |
---|---|---|
committer | markj <markj@FreeBSD.org> | 2014-09-11 01:04:56 +0000 |
commit | b4971501637a88d7c7d1f0eeb9b569903ddf1c95 (patch) | |
tree | cdde74689c562f7f07c5a7b738449882e8a5fa87 /cddl | |
parent | 08cd0ead43d723a7735bd3ea281c20c8fb381579 (diff) | |
download | FreeBSD-src-b4971501637a88d7c7d1f0eeb9b569903ddf1c95.zip FreeBSD-src-b4971501637a88d7c7d1f0eeb9b569903ddf1c95.tar.gz |
Use the linker to perform relocations in the SUNW_dof section rather than
doing them in drti during startup. This fixes a number of problems with
using USDT probes in stripped executables and shared libraries, and with
USDT probes in static functions.
Reviewed by: rpaulo
MFC after: 1 month
Sponsored by: EMC / Isilon Storage Division
Phabric: D751
Diffstat (limited to 'cddl')
-rw-r--r-- | cddl/contrib/opensolaris/lib/libdtrace/common/drti.c | 114 | ||||
-rw-r--r-- | cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c | 121 |
2 files changed, 23 insertions, 212 deletions
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c b/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c index bb02d8c..3c64f87 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c @@ -90,36 +90,6 @@ dprintf(int debug, const char *fmt, ...) va_end(ap); } -#if !defined(sun) -static void -fixsymbol(Elf *e, Elf_Data *data, size_t idx, int nprobes, char *buf, - dof_sec_t *sec, int *fixedprobes, char *dofstrtab) -{ - GElf_Sym sym; - char *s; - unsigned char *funcname; - dof_probe_t *prb; - int j = 0; - int ndx; - - while (gelf_getsym(data, j++, &sym) != NULL) { - prb = (dof_probe_t *)(void *)(buf + sec->dofs_offset); - - for (ndx = nprobes; ndx; ndx--, prb += 1) { - funcname = dofstrtab + prb->dofpr_func; - s = elf_strptr(e, idx, sym.st_name); - if (strcmp(s, funcname) == 0) { - dprintf(1, "fixing %s() symbol\n", s); - prb->dofpr_addr = sym.st_value; - (*fixedprobes)++; - } - } - if (*fixedprobes == nprobes) - break; - } -} -#endif - #if defined(sun) #pragma init(dtrace_dof_init) #else @@ -145,9 +115,6 @@ dtrace_dof_init(void) Lmid_t lmid; #else u_long lmid = 0; - dof_sec_t *sec, *secstart, *dofstrtab, *dofprobes; - dof_provider_t *dofprovider; - size_t i; #endif int fd; const char *p; @@ -157,12 +124,9 @@ dtrace_dof_init(void) Elf_Data *symtabdata = NULL, *dynsymdata = NULL, *dofdata = NULL; dof_hdr_t *dof_next = NULL; GElf_Shdr shdr; - int efd, nprobes; + int efd; char *s; - char *dofstrtabraw; size_t shstridx, symtabidx = 0, dynsymidx = 0; - unsigned char *buf; - int fixedprobes; #endif if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL) @@ -183,7 +147,6 @@ dtrace_dof_init(void) } #endif - if ((modname = strrchr(lmp->l_name, '/')) == NULL) modname = lmp->l_name; else @@ -209,9 +172,9 @@ dtrace_dof_init(void) } else if (shdr.sh_type == SHT_DYNSYM) { dynsymidx = shdr.sh_link; dynsymdata = elf_getdata(scn, NULL); - } else if (shdr.sh_type == SHT_PROGBITS) { + } else if (shdr.sh_type == SHT_SUNW_dof) { s = elf_strptr(e, shstridx, shdr.sh_name); - if (s && strcmp(s, ".SUNW_dof") == 0) { + if (s != NULL && strcmp(s, ".SUNW_dof") == 0) { dofdata = elf_getdata(scn, NULL); dof = dofdata->d_buf; } @@ -225,7 +188,6 @@ dtrace_dof_init(void) } while ((char *) dof < (char *) dofdata->d_buf + dofdata->d_size) { - fixedprobes = 0; dof_next = (void *) ((char *) dof + dof->dofh_filesz); #endif @@ -273,76 +235,6 @@ dtrace_dof_init(void) return; #endif } -#if !defined(sun) - /* - * We need to fix the base address of each probe since this wasn't - * done by ld(1). (ld(1) needs to grow support for parsing the - * SUNW_dof section). - * - * The complexity of this is not that great. The first for loop - * iterates over the sections inside the DOF file. There are usually - * 10 sections here. We asume the STRTAB section comes first and the - * PROBES section comes after. Since we are only interested in fixing - * data inside the PROBES section we quit the for loop after processing - * the PROBES section. It's usually the case that the first section - * is the STRTAB section and the second section is the PROBES section, - * so this for loop is not meaningful when doing complexity analysis. - * - * After finding the probes section, we iterate over the symbols - * in the symtab section. When we find a symbol name that matches - * the probe function name, we fix it. If we have fixed all the - * probes, we exit all the loops and we are done. - * The number of probes is given by the variable 'nprobes' and this - * depends entirely on the user, but some optimizations were done. - * - * We are assuming the number of probes is less than the number of - * symbols (libc can have 4k symbols, for example). - */ - secstart = sec = (dof_sec_t *)(dof + 1); - buf = (char *)dof; - for (i = 0; i < dof->dofh_secnum; i++, sec++) { - if (sec->dofs_type != DOF_SECT_PROVIDER) - continue; - - dofprovider = (void *) (buf + sec->dofs_offset); - dofstrtab = secstart + dofprovider->dofpv_strtab; - dofprobes = secstart + dofprovider->dofpv_probes; - - if (dofstrtab->dofs_type != DOF_SECT_STRTAB) { - fprintf(stderr, "WARNING: expected STRTAB section, but got %d\n", - dofstrtab->dofs_type); - break; - } - if (dofprobes->dofs_type != DOF_SECT_PROBES) { - fprintf(stderr, "WARNING: expected PROBES section, but got %d\n", - dofprobes->dofs_type); - break; - } - - dprintf(1, "found provider %p\n", dofprovider); - dofstrtabraw = (char *)(buf + dofstrtab->dofs_offset); - nprobes = dofprobes->dofs_size / dofprobes->dofs_entsize; - fixsymbol(e, symtabdata, symtabidx, nprobes, buf, dofprobes, &fixedprobes, - dofstrtabraw); - if (fixedprobes != nprobes) { - /* - * If we haven't fixed all the probes using the - * symtab section, look inside the dynsym - * section. - */ - fixsymbol(e, dynsymdata, dynsymidx, nprobes, buf, dofprobes, - &fixedprobes, dofstrtabraw); - } - if (fixedprobes != nprobes) { - fprintf(stderr, "WARNING: number of probes " - "fixed does not match the number of " - "defined probes (%d != %d, " - "respectively)\n", fixedprobes, nprobes); - fprintf(stderr, "WARNING: some probes might " - "not fire or your program might crash\n"); - } - } -#endif if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1) dprintf(1, "DTrace ioctl failed for DOF at %p", dof); else { diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c index 13300b0..106878c 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c @@ -322,7 +322,11 @@ prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) char *strtab; int i, j, nrel; size_t strtabsz = 1; +#if defined(sun) uint32_t count = 0; +#else + uint64_t count = 0; +#endif size_t base; Elf64_Sym *sym; Elf64_Rela *rel; @@ -418,7 +422,6 @@ prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) s = &dofs[dofrh->dofr_tgtsec]; for (j = 0; j < nrel; j++) { -#ifdef DOODAD #if defined(__arm__) /* XXX */ #elif defined(__mips__) @@ -431,8 +434,13 @@ prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) #elif defined(__i386) || defined(__amd64) rel->r_offset = s->dofs_offset + dofr[j].dofr_offset; +#if defined(sun) rel->r_info = ELF64_R_INFO(count + dep->de_global, R_AMD64_64); +#else + rel->r_info = ELF64_R_INFO(count + dep->de_global, + R_X86_64_RELATIVE); +#endif #elif defined(__sparc) rel->r_offset = s->dofs_offset + dofr[j].dofr_offset; @@ -441,7 +449,6 @@ prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) #else #error unknown ISA #endif -#endif sym->st_name = base + dofr[j].dofr_name - 1; sym->st_value = 0; @@ -704,7 +711,11 @@ dump_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) shp = &elf_file.shdr[ESHDR_DOF]; shp->sh_name = 11; /* DTRACE_SHSTRTAB64[11] = ".SUNW_dof" */ +#if defined(sun) shp->sh_flags = SHF_ALLOC; +#else + shp->sh_flags = SHF_WRITE | SHF_ALLOC; +#endif shp->sh_type = SHT_SUNW_dof; shp->sh_offset = off; shp->sh_size = dof->dofh_filesz; @@ -1662,19 +1673,6 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, { #if !defined(sun) char tfile[PATH_MAX]; - Elf *e; - Elf_Scn *scn; - Elf_Data *data; - GElf_Shdr shdr; - int efd; - size_t stridx; - unsigned char *buf; - char *s; - int loc; - GElf_Ehdr ehdr; - Elf_Scn *scn0; - GElf_Shdr shdr0; - uint64_t off, rc; #endif char drti[PATH_MAX]; dof_hdr_t *dof; @@ -1810,21 +1808,22 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, (void) unlink(file); #endif -#if defined(sun) if (dtp->dt_oflags & DTRACE_O_LP64) status = dump_elf64(dtp, dof, fd); else status = dump_elf32(dtp, dof, fd); +#if defined(sun) if (status != 0 || lseek(fd, 0, SEEK_SET) != 0) { return (dt_link_error(dtp, NULL, -1, NULL, "failed to write %s: %s", file, strerror(errno))); } #else - /* We don't write the ELF header, just the DOF section */ - if (dt_write(dtp, fd, dof, dof->dofh_filesz) < dof->dofh_filesz) + (void)close(fd); + if (status != 0) return (dt_link_error(dtp, NULL, -1, NULL, - "failed to write %s: %s", tfile, strerror(errno))); + "failed to write %s: %s", tfile, + strerror(dtrace_errno(dtp)))); #endif if (!dtp->dt_lazyload) { @@ -1846,7 +1845,7 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, fd, drti); #else - const char *fmt = "%s -o %s -r %s"; + const char *fmt = "%s -o %s -r %s %s"; #if defined(__amd64__) /* @@ -1868,10 +1867,9 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file, tfile, drti) + 1; - len *= 2; cmd = alloca(len); - (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, + (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, tfile, drti); #endif if ((status = system(cmd)) == -1) { @@ -1894,85 +1892,6 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, file, dtp->dt_ld_path, WEXITSTATUS(status)); goto done; } -#if !defined(sun) - /* - * FreeBSD's ld(1) is not instructed to interpret and add - * correctly the SUNW_dof section present in tfile. - * We use libelf to add this section manually and hope the next - * ld invocation won't remove it. - */ - elf_version(EV_CURRENT); - if ((efd = open(file, O_RDWR, 0)) < 0) { - ret = dt_link_error(dtp, NULL, -1, NULL, - "failed to open file %s: %s", - file, strerror(errno)); - goto done; - } - if ((e = elf_begin(efd, ELF_C_RDWR, NULL)) == NULL) { - close(efd); - ret = dt_link_error(dtp, NULL, -1, NULL, - "failed to open elf file: %s", - elf_errmsg(elf_errno())); - goto done; - } - /* - * Add the string '.SUWN_dof' to the shstrtab section. - */ - elf_getshdrstrndx(e, &stridx); - scn = elf_getscn(e, stridx); - gelf_getshdr(scn, &shdr); - data = elf_newdata(scn); - data->d_off = shdr.sh_size; - data->d_buf = ".SUNW_dof"; - data->d_size = 10; - data->d_type = ELF_T_BYTE; - loc = shdr.sh_size; - shdr.sh_size += data->d_size; - gelf_update_shdr(scn, &shdr); - /* - * Construct the .SUNW_dof section. - */ - scn = elf_newscn(e); - data = elf_newdata(scn); - buf = mmap(NULL, dof->dofh_filesz, PROT_READ, MAP_SHARED, - fd, 0); - if (buf == MAP_FAILED) { - ret = dt_link_error(dtp, NULL, -1, NULL, - "failed to mmap buffer %s", strerror(errno)); - elf_end(e); - close(efd); - goto done; - } - data->d_buf = buf; - data->d_align = 4; - data->d_size = dof->dofh_filesz; - data->d_version = EV_CURRENT; - gelf_getshdr(scn, &shdr); - shdr.sh_name = loc; - shdr.sh_flags = SHF_ALLOC; - /* - * Actually this should be SHT_SUNW_dof, but FreeBSD's ld(1) - * will remove this 'unknown' section when we try to create an - * executable using the object we are modifying, so we stop - * playing by the rules and use SHT_PROGBITS. - * Also, note that our drti has modifications to handle this. - */ - shdr.sh_type = SHT_PROGBITS; - shdr.sh_addralign = 4; - gelf_update_shdr(scn, &shdr); - if (elf_update(e, ELF_C_WRITE) < 0) { - ret = dt_link_error(dtp, NULL, -1, NULL, - "failed to add the SUNW_dof section: %s", - elf_errmsg(elf_errno())); - munmap(buf, dof->dofh_filesz); - elf_end(e); - close(efd); - goto done; - } - munmap(buf, dof->dofh_filesz); - elf_end(e); - close(efd); -#endif (void) close(fd); /* release temporary file */ } else { (void) close(fd); |