diff options
author | rpaulo <rpaulo@FreeBSD.org> | 2010-08-21 11:50:53 +0000 |
---|---|---|
committer | rpaulo <rpaulo@FreeBSD.org> | 2010-08-21 11:50:53 +0000 |
commit | b325e69c9282072ea3b203319017abc400db4771 (patch) | |
tree | ae5b6066f0143ecb034e8de394e160841cd4f84f /cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c | |
parent | 232561a60dcdeeffd316d29086bb31f2e12d7591 (diff) | |
download | FreeBSD-src-b325e69c9282072ea3b203319017abc400db4771.zip FreeBSD-src-b325e69c9282072ea3b203319017abc400db4771.tar.gz |
Add libdtrace support for tracing userland programs.
Summary of changes:
* Implement a compatibility shim between Solaris libproc and our
libproc and remove several ifdefs because of this.
* Port the drti to FreeBSD.
* Implement the missing DOODAD sections
* Link with libproc and librtld_db
* Support for ustack, jstack and uregs (by sson@)
* Misc bugfixing
When writing the SUWN_dof section, we had to resort to building the ELF
file layout by "hand". This is the job of libelf, but our libelf doesn't
support this yet. When libelf is fixed, we can remove the code under
#ifdef BROKEN_LIBELF.
Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c')
-rw-r--r-- | cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c | 193 |
1 files changed, 182 insertions, 11 deletions
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c index aa78658..0b8899a 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c @@ -51,6 +51,9 @@ #include <wait.h> #else #include <sys/wait.h> +#include <libelf.h> +#include <gelf.h> +#include <sys/mman.h> #endif #include <assert.h> #include <sys/ipc.h> @@ -412,7 +415,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++) { -printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); #ifdef DOODAD #if defined(__arm__) /* XXX */ @@ -1519,14 +1521,29 @@ process_obj(dtrace_hdl_t *dtp, const char *obj, int *eprobesp) off = rela.r_offset - fsym.st_value; if (dt_modtext(dtp, data_tgt->d_buf, eprobe, - &rela, &off) != 0) { + &rela, &off) != 0) goto err; - } if (dt_probe_define(pvp, prp, s, r, off, eprobe) != 0) { return (dt_link_error(dtp, elf, fd, bufs, "failed to allocate space for probe")); } +#if !defined(sun) + /* + * Our linker doesn't understand the SUNW_IGNORE ndx and + * will try to use this relocation when we build the + * final executable. Since we are done processing this + * relocation, mark it as inexistant and let libelf + * remove it from the file. + * If this wasn't done, we would have garbage added to + * the executable file as the symbol is going to be + * change from UND to ABS. + */ + rela.r_offset = 0; + rela.r_info = 0; + rela.r_addend = 0; + (void) gelf_update_rela(data_rel, i, &rela); +#endif mod = 1; (void) elf_flagdata(data_tgt, ELF_C_SET, ELF_F_DIRTY); @@ -1538,13 +1555,13 @@ process_obj(dtrace_hdl_t *dtp, const char *obj, int *eprobesp) * already been processed by an earlier link * invocation. */ -printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); -#ifdef DOODAD +#if !defined(sun) +#define SHN_SUNW_IGNORE SHN_ABS +#endif if (rsym.st_shndx != SHN_SUNW_IGNORE) { rsym.st_shndx = SHN_SUNW_IGNORE; (void) gelf_update_sym(data_sym, ndx, &rsym); } -#endif } } @@ -1554,6 +1571,9 @@ printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); (void) elf_end(elf); (void) close(fd); +#if !defined(sun) + if (nsym > 0) +#endif while ((pair = bufs) != NULL) { bufs = pair->dlp_next; dt_free(dtp, pair->dlp_str); @@ -1574,6 +1594,19 @@ 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; @@ -1697,12 +1730,17 @@ 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 (status != 0 || lseek(fd, 0, SEEK_SET) != 0) { +#else + /* We don't write the ELF header, just the DOF section */ + if (dt_write(dtp, fd, dof, dof->dofh_filesz) < dof->dofh_filesz) { +#endif return (dt_link_error(dtp, NULL, -1, NULL, "failed to write %s: %s", file, strerror(errno))); } @@ -1726,7 +1764,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 %s"; + const char *fmt = "%s -o %s -r %s"; #if defined(__amd64__) /* @@ -1748,11 +1786,14 @@ 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; +#if !defined(sun) + len *= 2; +#endif cmd = alloca(len); - (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, tfile, drti); + (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, + drti); #endif - if ((status = system(cmd)) == -1) { ret = dt_link_error(dtp, NULL, -1, NULL, "failed to run %s: %s", dtp->dt_ld_path, @@ -1760,8 +1801,6 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, goto done; } - (void) close(fd); /* release temporary file */ - if (WIFSIGNALED(status)) { ret = dt_link_error(dtp, NULL, -1, NULL, "failed to link %s: %s failed due to signal %d", @@ -1775,6 +1814,138 @@ 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) +#define BROKEN_LIBELF + /* + * 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. + */ +#ifdef BROKEN_LIBELF + elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT); +#endif + 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); +#ifdef BROKEN_LIBELF + off = shdr.sh_offset; + rc = shdr.sh_offset + shdr.sh_size; + gelf_getehdr(e, &ehdr); + if (ehdr.e_shoff > off) { + off = ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize; + rc = roundup(rc, 8); + ehdr.e_shoff = rc; + gelf_update_ehdr(e, &ehdr); + rc += ehdr.e_shnum * ehdr.e_shentsize; + } + for (;;) { + scn0 = NULL; + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + if (shdr.sh_type == SHT_NOBITS || + shdr.sh_offset < off) + continue; + /* Find the immediately adjcent section. */ + if (scn0 == NULL || + shdr.sh_offset < shdr0.sh_offset) { + scn0 = scn; + gelf_getshdr(scn0, &shdr0); + } + } + if (scn0 == NULL) + break; + /* Load section data to work around another bug */ + elf_getdata(scn0, NULL); + /* Update section header, assure section alignment */ + off = shdr0.sh_offset + shdr0.sh_size; + rc = roundup(rc, shdr0.sh_addralign); + shdr0.sh_offset = rc; + gelf_update_shdr(scn0, &shdr0); + rc += shdr0.sh_size; + } + if (elf_update(e, ELF_C_WRITE) < 0) { + ret = dt_link_error(dtp, NULL, -1, NULL, + "failed to add append the shstrtab section: %s", + elf_errmsg(elf_errno())); + elf_end(e); + close(efd); + goto done; + } + elf_end(e); + e = elf_begin(efd, ELF_C_RDWR, NULL); +#endif + /* + * 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); } |