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
This commit is contained in:
parent
adbc0311a3
commit
8caf8a8d0e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=271413
@ -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 {
|
||||
|
@ -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;
|
||||
@ -440,7 +448,6 @@ prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep)
|
||||
R_SPARC_64);
|
||||
#else
|
||||
#error unknown ISA
|
||||
#endif
|
||||
#endif
|
||||
|
||||
sym->st_name = base + dofr[j].dofr_name - 1;
|
||||
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user