From: Kris Van Hees <kris.van.hees@oracle.com>
To: dtrace@lists.linux.dev, dtrace-devel@oss.oracle.com
Subject: [PATCH 2/8] module: add support for user module search paths
Date: Mon, 28 Sep 2026 18:01:56 +0000 [thread overview]
Message-ID: <f7fbd59eed1fa31f615291fdff3f57fb@oracle.com> (raw)
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@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
next reply other threads:[~2026-09-28 18:03 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-28 18:01 Kris Van Hees [this message]
2026-10-03 8:39 ` [DTrace-devel] [PATCH 2/8] module: add support for user module search paths Elena Zannoni
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f7fbd59eed1fa31f615291fdff3f57fb@oracle.com \
--to=kris.van.hees@oracle.com \
--cc=dtrace-devel@oss.oracle.com \
--cc=dtrace@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.