Remove drti.o's dependency on libelf. This makes it possible to add DTrace

probes to userland programs and libraries without also needing to link
libelf.

dtrace -G places the __SUNW_dof symbol at the beginning of the DOF (DTrace
probe and provider metdata) section in the generated object file; drti.o
now just uses this symbol to locate the section. A complication occurs
when multiple dtrace-generated object files are linked together, since the
__SUNW_dof symbol defined in each file is global. This is handled by
using objcopy(1) to convert __SUNW_dof to a local symbol once drti.o has
been linked with the generated object file. Upstream, this is done using a
linker feature not present in GNU ld.

Differential Revision:	https://reviews.freebsd.org/D1757
Reviewed by:	rpaulo
MFC after:	1 month
Relnotes:	yes
This commit is contained in:
Mark Johnston 2015-02-18 03:54:54 +00:00
parent 70f82314c2
commit 03a5f9f0fd
6 changed files with 86 additions and 75 deletions

View File

@ -36,7 +36,6 @@
#include <string.h>
#include <errno.h>
#include <libelf.h>
#include <gelf.h>
/*
* In Solaris 10 GA, the only mechanism for communicating helper information
@ -62,9 +61,7 @@ static const char *olddevname = "/devices/pseudo/dtrace@0:helper";
static const char *modname; /* Name of this load object */
static int gen; /* DOF helper generation */
#ifdef illumos
extern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */
#endif
static boolean_t dof_init_debug = B_FALSE; /* From DTRACE_DOF_INIT_DEBUG */
static void
@ -99,11 +96,7 @@ static void dtrace_dof_init(void) __attribute__ ((constructor));
static void
dtrace_dof_init(void)
{
#ifdef illumos
dof_hdr_t *dof = &__SUNW_dof;
#else
dof_hdr_t *dof = NULL;
#endif
#ifdef _LP64
Elf64_Ehdr *elf;
#else
@ -118,17 +111,6 @@ dtrace_dof_init(void)
#endif
int fd;
const char *p;
#ifndef illumos
Elf *e;
Elf_Scn *scn = NULL;
Elf_Data *dofdata = NULL;
dof_hdr_t *dof_next = NULL;
GElf_Shdr shdr;
int efd;
char *s;
size_t shstridx;
uint64_t aligned_filesz;
#endif
if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL)
return;
@ -152,42 +134,6 @@ dtrace_dof_init(void)
modname = lmp->l_name;
else
modname++;
#ifndef illumos
elf_version(EV_CURRENT);
if ((efd = open(lmp->l_name, O_RDONLY, 0)) < 0) {
dprintf(1, "couldn't open file for reading\n");
return;
}
if ((e = elf_begin(efd, ELF_C_READ, NULL)) == NULL) {
dprintf(1, "elf_begin failed\n");
close(efd);
return;
}
elf_getshdrstrndx(e, &shstridx);
dof = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
if (shdr.sh_type == SHT_SUNW_dof) {
s = elf_strptr(e, shstridx, shdr.sh_name);
if (s != NULL && strcmp(s, ".SUNW_dof") == 0) {
dofdata = elf_getdata(scn, NULL);
dof = dofdata->d_buf;
break;
}
}
}
if (dof == NULL) {
dprintf(1, "SUNW_dof section not found\n");
elf_end(e);
close(efd);
return;
}
while ((char *) dof < (char *) dofdata->d_buf + dofdata->d_size) {
aligned_filesz = (shdr.sh_addralign == 0 ? dof->dofh_filesz :
roundup2(dof->dofh_filesz, shdr.sh_addralign));
dof_next = (void *) ((char *) dof + aligned_filesz);
#endif
if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 ||
dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 ||
@ -237,21 +183,12 @@ dtrace_dof_init(void)
dprintf(1, "DTrace ioctl failed for DOF at %p", dof);
else {
dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof);
#ifndef illumos
#ifdef __FreeBSD__
gen = dh.gen;
#endif
}
(void) close(fd);
#ifndef illumos
/* End of while loop */
dof = dof_next;
}
elf_end(e);
(void) close(efd);
#endif
}
#ifdef illumos

View File

@ -275,6 +275,9 @@ struct dtrace_hdl {
int dt_cpp_argc; /* count of initialized cpp(1) arguments */
int dt_cpp_args; /* size of dt_cpp_argv[] array */
char *dt_ld_path; /* pathname of ld(1) to invoke if needed */
#ifdef __FreeBSD__
char *dt_objcopy_path; /* pathname of objcopy(1) to invoke if needed */
#endif
dt_list_t dt_lib_path; /* linked-list forming library search path */
uint_t dt_lazyload; /* boolean: set via -xlazyload */
uint_t dt_droptags; /* boolean: set via -xdroptags */

View File

@ -281,7 +281,11 @@ printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__);
sym->st_value = 0;
sym->st_size = dof->dofh_filesz;
sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT);
#ifdef illumos
sym->st_other = 0;
#else
sym->st_other = ELF32_ST_VISIBILITY(STV_HIDDEN);
#endif
sym->st_shndx = ESHDR_DOF;
sym++;
@ -471,7 +475,11 @@ prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep)
sym->st_value = 0;
sym->st_size = dof->dofh_filesz;
sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
#ifdef illumos
sym->st_other = 0;
#else
sym->st_other = ELF64_ST_VISIBILITY(STV_HIDDEN);
#endif
sym->st_shndx = ESHDR_DOF;
sym++;
@ -711,11 +719,7 @@ 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" */
#ifdef illumos
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;
@ -1874,7 +1878,7 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags,
#endif
(void) snprintf(drti, sizeof (drti), "/usr/lib%s/dtrace/drti.o",
use_32 ? "32":"");
use_32 ? "32" : "");
len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file, tfile,
drti) + 1;
@ -1885,26 +1889,61 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags,
drti);
#endif
if ((status = system(cmd)) == -1) {
ret = dt_link_error(dtp, NULL, -1, NULL,
ret = dt_link_error(dtp, NULL, fd, NULL,
"failed to run %s: %s", dtp->dt_ld_path,
strerror(errno));
goto done;
}
if (WIFSIGNALED(status)) {
ret = dt_link_error(dtp, NULL, -1, NULL,
ret = dt_link_error(dtp, NULL, fd, NULL,
"failed to link %s: %s failed due to signal %d",
file, dtp->dt_ld_path, WTERMSIG(status));
goto done;
}
if (WEXITSTATUS(status) != 0) {
ret = dt_link_error(dtp, NULL, -1, NULL,
ret = dt_link_error(dtp, NULL, fd, NULL,
"failed to link %s: %s exited with status %d\n",
file, dtp->dt_ld_path, WEXITSTATUS(status));
goto done;
}
(void) close(fd); /* release temporary file */
#ifdef __FreeBSD__
/*
* Now that we've linked drti.o, reduce the global __SUNW_dof
* symbol to a local symbol. This is needed to so that multiple
* generated object files (for different providers, for
* instance) can be linked together. This is accomplished using
* the -Blocal flag with Sun's linker, but GNU ld doesn't appear
* to have an equivalent option.
*/
asprintf(&cmd, "%s --localize-hidden %s", dtp->dt_objcopy_path,
file);
if ((status = system(cmd)) == -1) {
ret = dt_link_error(dtp, NULL, -1, NULL,
"failed to run %s: %s", dtp->dt_objcopy_path,
strerror(errno));
free(cmd);
goto done;
}
free(cmd);
if (WIFSIGNALED(status)) {
ret = dt_link_error(dtp, NULL, -1, NULL,
"failed to link %s: %s failed due to signal %d",
file, dtp->dt_objcopy_path, WTERMSIG(status));
goto done;
}
if (WEXITSTATUS(status) != 0) {
ret = dt_link_error(dtp, NULL, -1, NULL,
"failed to link %s: %s exited with status %d\n",
file, dtp->dt_objcopy_path, WEXITSTATUS(status));
goto done;
}
#endif
} else {
(void) close(fd);
}

View File

@ -785,6 +785,7 @@ const char *_dtrace_defld = "/usr/ccs/bin/ld"; /* default ld(1) to invoke */
#else
const char *_dtrace_defcpp = "cpp"; /* default cpp(1) to invoke */
const char *_dtrace_defld = "ld"; /* default ld(1) to invoke */
const char *_dtrace_defobjcopy = "objcopy"; /* default objcopy(1) to invoke */
#endif
const char *_dtrace_libdir = "/usr/lib/dtrace"; /* default library directory */
@ -1185,6 +1186,9 @@ dt_vopen(int version, int flags, int *errp,
dtp->dt_cpp_argc = 1;
dtp->dt_cpp_args = 1;
dtp->dt_ld_path = strdup(_dtrace_defld);
#ifdef __FreeBSD__
dtp->dt_objcopy_path = strdup(_dtrace_defobjcopy);
#endif
dtp->dt_provmod = provmod;
dtp->dt_vector = vector;
dtp->dt_varg = arg;
@ -1193,6 +1197,9 @@ dt_vopen(int version, int flags, int *errp,
if (dtp->dt_mods == NULL || dtp->dt_provs == NULL ||
dtp->dt_procs == NULL || dtp->dt_ld_path == NULL ||
#ifdef __FreeBSD__
dtp->dt_objcopy_path == NULL ||
#endif
dtp->dt_cpp_path == NULL || dtp->dt_cpp_argv == NULL)
return (set_open_errno(dtp, errp, EDT_NOMEM));
@ -1673,6 +1680,9 @@ dtrace_close(dtrace_hdl_t *dtp)
free(dtp->dt_cpp_argv);
free(dtp->dt_cpp_path);
free(dtp->dt_ld_path);
#ifdef __FreeBSD__
free(dtp->dt_objcopy_path);
#endif
free(dtp->dt_mods);
free(dtp->dt_provs);

View File

@ -280,6 +280,28 @@ dt_opt_ld_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
return (0);
}
#ifdef __FreeBSD__
static int
dt_opt_objcopy_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
char *objcopy;
if (arg == NULL)
return (dt_set_errno(dtp, EDT_BADOPTVAL));
if (dtp->dt_pcb != NULL)
return (dt_set_errno(dtp, EDT_BADOPTCTX));
if ((objcopy = strdup(arg)) == NULL)
return (dt_set_errno(dtp, EDT_NOMEM));
free(dtp->dt_objcopy_path);
dtp->dt_objcopy_path = objcopy;
return (0);
}
#endif
/*ARGSUSED*/
static int
dt_opt_libdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
@ -960,6 +982,9 @@ static const dt_option_t _dtrace_ctoptions[] = {
{ "linkmode", dt_opt_linkmode },
{ "linktype", dt_opt_linktype },
{ "nolibs", dt_opt_cflags, DTRACE_C_NOLIBS },
#ifdef __FreeBSD__
{ "objcopypath", dt_opt_objcopy_path },
#endif
{ "pgmax", dt_opt_pgmax },
{ "pspec", dt_opt_cflags, DTRACE_C_PSPEC },
{ "setenv", dt_opt_setenv, 1 },

View File

@ -121,10 +121,7 @@ ${_YC:R}.o: ${_YC}
.endfor
# DTrace probe definitions
# libelf is currently needed for drti.o
.if ${SRCS:M*.d}
LDADD+= -lelf
DPADD+= ${LIBELF}
CFLAGS+= -I${.OBJDIR}
.endif
.for _DSRC in ${SRCS:M*.d:N*/*}