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:
Mark Johnston 2014-09-11 01:04:56 +00:00
parent adbc0311a3
commit 8caf8a8d0e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=271413
2 changed files with 23 additions and 212 deletions

View File

@ -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 {

View File

@ -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);