diff options
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); } |