* [PATCH 2/8] module: add support for user module search paths
@ 2026-09-28 18:01 Kris Van Hees
2026-10-03 8:39 ` [DTrace-devel] " Elena Zannoni
0 siblings, 1 reply; 2+ messages in thread
From: Kris Van Hees @ 2026-09-28 18:01 UTC (permalink / raw)
To: dtrace, dtrace-devel
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
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [DTrace-devel] [PATCH 2/8] module: add support for user module search paths
2026-09-28 18:01 [PATCH 2/8] module: add support for user module search paths Kris Van Hees
@ 2026-10-03 8:39 ` Elena Zannoni
0 siblings, 0 replies; 2+ messages in thread
From: Elena Zannoni @ 2026-10-03 8:39 UTC (permalink / raw)
To: Kris Van Hees, dtrace, dtrace-devel
Reviewed-by: Elena Zannoni <elena.zannoni@oracle.com>
On 9/28/26 12:01 PM, Kris Van Hees via DTrace-devel wrote:
>
> 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
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel@oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-03 8:39 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-28 18:01 [PATCH 2/8] module: add support for user module search paths Kris Van Hees
2026-10-03 8:39 ` [DTrace-devel] " Elena Zannoni
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.