[DTrace-devel] [PATCH 2/8] module: add support for user module search paths
Kris Van Hees
kris.van.hees at oracle.com
Mon Sep 28 18:01:56 UTC 2026
User modules can be uerspace libraries or executables.
Introducing this support requires a renaming of the previous dt_lib_path
to dt_dlib_path.
We also introduce a dt_module_sym_iter() function to iterate over all
symbols in a particular module. This can be used in the future to add
function name globbing support to the probe specification for uprobe probes.
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
libdtrace/dt_cc.c | 4 +-
libdtrace/dt_dlibs.c | 2 +-
libdtrace/dt_impl.h | 3 +-
libdtrace/dt_link.c | 2 +-
libdtrace/dt_module.c | 280 +++++++++++++++++++++++++++++++++++++++--
libdtrace/dt_module.h | 3 +
libdtrace/dt_open.c | 6 +-
libdtrace/dt_options.c | 4 +-
8 files changed, 288 insertions(+), 16 deletions(-)
diff --git a/libdtrace/dt_cc.c b/libdtrace/dt_cc.c
index 5860e59d..b49ecb0e 100644
--- a/libdtrace/dt_cc.c
+++ b/libdtrace/dt_cc.c
@@ -620,7 +620,7 @@ dt_compile(dtrace_hdl_t *dtp, int context, dtrace_probespec_t pspec, void *arg,
return NULL;
}
- if (dt_list_next(&dtp->dt_lib_path) != NULL && dt_load_libs(dtp) != 0)
+ if (dt_list_next(&dtp->dt_dlib_path) != NULL && dt_load_libs(dtp) != 0)
return NULL; /* errno is set for us */
ctf_discard(dtp->dt_cdefs->dm_ctfp);
@@ -803,7 +803,7 @@ dt_construct(dtrace_hdl_t *dtp, dt_probe_t *prp, uint_t cflags, dt_ident_t *idp)
return NULL;
}
- if (dt_list_next(&dtp->dt_lib_path) != NULL && dt_load_libs(dtp) != 0)
+ if (dt_list_next(&dtp->dt_dlib_path) != NULL && dt_load_libs(dtp) != 0)
return NULL;
ctf_discard(dtp->dt_cdefs->dm_ctfp);
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index 161e2106..176291f1 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -1232,7 +1232,7 @@ dt_load_libs(dtrace_hdl_t *dtp)
dtp->dt_cflags |= DTRACE_C_NOLIBS;
- for (dirp = dt_list_next(&dtp->dt_lib_path); dirp != NULL;
+ for (dirp = dt_list_next(&dtp->dt_dlib_path); dirp != NULL;
dirp = dt_list_next(dirp)) {
char *kdir_path;
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 9597f823..22733e33 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -361,7 +361,8 @@ 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 */
- dt_list_t dt_lib_path; /* linked-list forming library search path */
+ dt_list_t dt_dlib_path; /* D library search path (list) */
+ dt_list_t dt_user_path; /* userspace module search path (list) */
char *dt_module_path; /* pathname of kernel module root */
dt_version_t dt_kernver;/* kernel version, used in the libpath */
char *dt_dofstash_path; /* Path to the DOF stash. */
diff --git a/libdtrace/dt_link.c b/libdtrace/dt_link.c
index aa5eb735..3d17c07c 100644
--- a/libdtrace/dt_link.c
+++ b/libdtrace/dt_link.c
@@ -513,7 +513,7 @@ dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags,
"failed to write %s: %s", file, strerror(errno));
if (!dtp->dt_lazyload) {
- dt_dirpath_t *libdir = dt_list_next(&dtp->dt_lib_path);
+ dt_dirpath_t *libdir = dt_list_next(&dtp->dt_dlib_path);
const char *fmt = "%s%s -o %s -r --version-script=%s /dev/fd/%d %s";
const char *emu = "";
diff --git a/libdtrace/dt_module.c b/libdtrace/dt_module.c
index 1b16d2cd..acf4baa7 100644
--- a/libdtrace/dt_module.c
+++ b/libdtrace/dt_module.c
@@ -45,6 +45,171 @@ dt_kern_module_find_btf(dtrace_hdl_t *dtp, dt_module_t *dmp);
static void
dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp);
+typedef struct dt_path {
+ dt_list_t list;
+ struct dt_hentry he;
+ const char *str;
+} dt_path_t;
+
+static uint32_t
+dt_path_hval(const dt_path_t *path)
+{
+ return str2hval(path->str, 0);
+}
+
+static int
+dt_path_cmp(const dt_path_t *p, const dt_path_t *q)
+{
+ return strcmp(p->str, q->str);
+}
+
+DEFINE_HE_STD_LINK_FUNCS(dt_path, dt_path_t, he)
+DEFINE_HTAB_STD_OPS(dt_path)
+
+static int
+dt_user_path_add(dtrace_hdl_t *dtp, dt_htab_t *h, char *s)
+{
+ dt_path_t tmpl, *path;
+
+ tmpl.str = s;
+ if (dt_htab_lookup(h, &tmpl) != NULL) {
+ free(s);
+ return 0;
+ }
+
+ path = malloc(sizeof(dt_path_t));
+ if (path == NULL)
+ goto fail;
+
+ memset(path, 0, sizeof(dt_path_t));
+ path->str = s;
+ if (dt_htab_insert(h, path) != 0) {
+ free(path);
+ goto fail;
+ }
+
+ dt_list_append(&dtp->dt_user_path, path);
+
+ return 0;
+
+fail:
+ free(s);
+ return -1;
+}
+
+static int
+dt_user_path_add_env(dtrace_hdl_t *dtp, dt_htab_t *h, const char *env)
+{
+ char *str, *end;
+
+ str = getenv(env);
+ if (str == NULL)
+ return 0;
+
+ for (;;) {
+ end = strchr(str, ':');
+ if (end == NULL) {
+ if (*str == '\0')
+ if (dt_user_path_add(dtp, h, strdup(".")) == -1)
+ return -1;
+
+ if (dt_user_path_add(dtp, h, strdup(str)) == -1)
+ return -1;
+
+ break;
+ }
+
+ if (end == str) {
+ if (dt_user_path_add(dtp, h, strdup(".")) == -1)
+ return -1;
+ } else
+ if (dt_user_path_add(dtp, h, strndup(str, end - str)) == -1)
+ return -1;
+
+ str = end + 1;
+ }
+
+ return 0;
+}
+
+static int
+dt_user_path_init(dtrace_hdl_t *dtp)
+{
+ dt_htab_t *h = dt_htab_create(&dt_path_htab_ops);
+ FILE *fp;
+ char *buf = NULL;
+ size_t len = 0;
+ char *str, *end;
+ int rc = 0;
+
+ /* Add paths from $LD_LIBRARY_PATH. */
+ if (dt_user_path_add_env(dtp, h, "LD_LIBRARY_PATH") != 0)
+ goto fail;
+
+ /* Add paths from ld.so.cache (using ldconfig -p output). */
+ fp = popen("/sbin/ldconfig -p", "r");
+ if (fp == NULL)
+ goto fail;
+
+ while (getline(&buf, &len, fp) != -1) {
+ /* We are interested in lines like: <something> => /path */
+ str = strstr(buf, "=>");
+ if (str == NULL)
+ continue;
+
+ str += 2;
+ while (isspace(*str))
+ str++;
+
+ end = str + strcspn(str, "\n");
+ while (end > str && isspace(end[-1]))
+ end--;
+ *end = '\0';
+
+ end = strrchr(str, '/');
+ if (end == NULL)
+ continue;
+
+ if (dt_user_path_add(dtp, h, strndup(str, end == str ? 1 : end - str)) == -1) {
+ rc = -1;
+ break;
+ }
+ }
+
+ free(buf);
+ pclose(fp);
+
+ if (rc == -1)
+ goto fail;
+
+ /* Add paths from $PATH. */
+ if (dt_user_path_add_env(dtp, h, "PATH") != 0)
+ goto fail;
+
+ /* Always add . in case it wasn't added yet. */
+ if (dt_user_path_add(dtp, h, strdup(".")) == -1)
+ goto fail;
+
+ dt_htab_destroy(h);
+ return 0;
+
+fail:
+ dt_htab_destroy(h);
+ return -1;
+}
+
+void
+dt_user_path_destroy(dtrace_hdl_t *dtp)
+{
+ dt_path_t *path;
+
+ while ((path = dt_list_next(&dtp->dt_user_path)) != NULL) {
+ dt_list_delete(&dtp->dt_user_path, path);
+ free((char *)path->str);
+ free(path);
+ }
+}
+
static uint32_t
dt_module_hval(const dt_module_t *mod)
{
@@ -166,6 +331,75 @@ dt_module_create(dtrace_hdl_t *dtp, const char *name)
return dmp;
}
+/*
+ * Create a userspace module for an ELF object. Userspace module names are
+ * resolved based on the LD_LIBRARY_PATH, ld.so.cache entries, and PATH.
+ * Module loading is lazy, as it is for all other modules.
+ */
+dt_module_t *
+dt_module_create_user(dtrace_hdl_t *dtp, const char *name)
+{
+ dt_module_t *dmp;
+ char *file = NULL;
+
+ if (name == NULL || name[0] == '\0') {
+ dt_set_errno(dtp, EINVAL);
+ return NULL;
+ }
+
+ if (dt_list_empty(&dtp->dt_user_path))
+ dt_user_path_init(dtp);
+ if (!dt_list_empty(&dtp->dt_user_path)) {
+ dt_path_t *path;
+
+ for (path = dt_list_next(&dtp->dt_user_path); path != NULL;
+ path = dt_list_next(path)) {
+ if (asprintf(&file, "%s/%s", path->str, name) == -1) {
+ dt_set_errno(dtp, ENOMEM);
+ return NULL;
+ }
+ if (access(file, R_OK) == 0)
+ break;
+
+ free(file);
+ file = NULL;
+ }
+
+ if (file == NULL) {
+ dt_set_errno(dtp, ENOENT);
+ return NULL;
+ }
+ }
+
+ if (strlen(name) >= DTRACE_MODNAMELEN ||
+ strlen(file) >= sizeof(dmp->dm_file)) {
+ dt_set_errno(dtp, ENAMETOOLONG);
+ goto fail;
+ }
+
+ dmp = dt_module_lookup_by_name(dtp, name);
+ if (dmp != NULL) {
+ /* Do not turn a kernel or built-in type module into a user module. */
+ if (dmp->dm_flags & DT_DM_KERNEL || dmp == dtp->dt_cdefs ||
+ dmp == dtp->dt_ddefs ||
+ (dmp->dm_file[0] != '\0' && strcmp(dmp->dm_file, file) != 0)) {
+ dt_set_errno(dtp, EEXIST);
+ goto fail;
+ }
+ } else if ((dmp = dt_module_create(dtp, name)) == NULL) {
+ dt_set_errno(dtp, EDT_NOMEM);
+ goto fail;
+ }
+
+ strlcpy(dmp->dm_file, file, sizeof(dmp->dm_file));
+ free(file);
+ return dmp;
+
+fail:
+ free(file);
+ return NULL;
+}
+
dt_module_t *
dt_module_lookup_by_name(dtrace_hdl_t *dtp, const char *name)
{
@@ -393,15 +627,21 @@ dt_module_load(dtrace_hdl_t *dtp, dt_module_t *dmp)
if (dmp->dm_flags & DT_DM_LOADED)
return 0; /* module is already loaded */
- /* Load BTF data for the module. */
- dt_kern_module_find_btf(dtp, dmp);
-
/*
- * First find out where the module is, and preliminarily load its CTF.
- * If this fails, we don't care: the problem will be detected in
- * dt_module_init_elf().
+ * A non-kernel module with a known file was explicitly created as a
+ * userspace module. Its CTF, if any, is in that ELF object; do not let
+ * kernel module discovery reclassify it as an unloaded kernel module.
*/
- dt_kern_module_find_ctf(dtp, dmp);
+ if ((dmp->dm_flags & DT_DM_KERNEL) || dmp->dm_file[0] == '\0') {
+ /* Load BTF data for the kernel module. */
+ dt_kern_module_find_btf(dtp, dmp);
+
+ /*
+ * Find where the kernel module is and preliminarily load its CTF.
+ * If this fails, dt_module_init_elf() reports the error below.
+ */
+ dt_kern_module_find_ctf(dtp, dmp);
+ }
/*
* Modules not found in the CTF archive, including non-kernel modules,
@@ -2064,6 +2304,32 @@ out:
return 0;
}
+int
+dt_module_sym_iter(const dt_module_t *dmp, proc_sym_f *func, void *arg)
+{
+ GElf_Sym sym;
+ uint_t i;
+ int rv = 0;
+
+ for (i = 0; i < dmp->dm_aslen; i++) {
+ const char *name;
+
+ if (dmp->dm_ops == &dt_modops_64)
+ dt_module_symgelf64(
+ ((const Elf64_Sym **)dmp->dm_asmap)[i], &sym);
+ else
+ dt_module_symgelf32(
+ ((const Elf32_Sym **)dmp->dm_asmap)[i], &sym);
+
+ name = (const char *)dmp->dm_strtab.cts_data + sym.st_name;
+
+ if ((rv = func(arg, &sym, name)) != 0)
+ break;
+ }
+
+ return rv;
+}
+
static dtrace_objinfo_t *
dt_module_info(const dt_module_t *dmp, dtrace_objinfo_t *dto)
{
diff --git a/libdtrace/dt_module.h b/libdtrace/dt_module.h
index d22cc802..88b4f1a4 100644
--- a/libdtrace/dt_module.h
+++ b/libdtrace/dt_module.h
@@ -15,9 +15,12 @@ extern "C" {
#endif
extern dt_module_t *dt_module_create(dtrace_hdl_t *, const char *);
+extern dt_module_t *dt_module_create_user(dtrace_hdl_t *, const char *);
+extern void dt_user_path_destroy(dtrace_hdl_t *);
extern dt_module_t *dt_module_lookup_by_name(dtrace_hdl_t *, const char *);
extern dt_module_t *dt_module_lookup_by_ctf(dtrace_hdl_t *, ctf_file_t *);
+extern int dt_module_sym_iter(const dt_module_t *, proc_sym_f *, void *);
extern ctf_file_t *dt_module_getctf(dtrace_hdl_t *, dt_module_t *);
extern dt_ident_t *dt_module_extern(dtrace_hdl_t *, dt_module_t *,
diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
index ef6b7599..b5d2b648 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -1348,12 +1348,14 @@ dtrace_close(dtrace_hdl_t *dtp)
for (i = 1; i < dtp->dt_cpp_argc; i++)
free(dtp->dt_cpp_argv[i]);
- while ((dirp = dt_list_next(&dtp->dt_lib_path)) != NULL) {
- dt_list_delete(&dtp->dt_lib_path, dirp);
+ while ((dirp = dt_list_next(&dtp->dt_dlib_path)) != NULL) {
+ dt_list_delete(&dtp->dt_dlib_path, dirp);
free(dirp->dir_path);
free(dirp);
}
+ dt_user_path_destroy(dtp);
+
free(dtp->dt_cpp_argv);
free(dtp->dt_cpp_path);
free(dtp->dt_ld_path);
diff --git a/libdtrace/dt_options.c b/libdtrace/dt_options.c
index 0a35e852..4706589b 100644
--- a/libdtrace/dt_options.c
+++ b/libdtrace/dt_options.c
@@ -359,7 +359,7 @@ dt_opt_libdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
return dt_set_errno(dtp, EDT_NOMEM);
}
- dt_list_append(&dtp->dt_lib_path, dp);
+ dt_list_append(&dtp->dt_dlib_path, dp);
return 0;
}
@@ -541,7 +541,7 @@ dt_opt_stdc(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
static int
dt_opt_syslibdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
- dt_dirpath_t *dp = dt_list_next(&dtp->dt_lib_path);
+ dt_dirpath_t *dp = dt_list_next(&dtp->dt_dlib_path);
char *path;
if (arg == NULL)
--
2.52.0
More information about the DTrace-devel
mailing list