[DTrace-devel] [PATCH 2/8] module: add support for user module search paths
Elena Zannoni
elena.zannoni at oracle.com
Sat Oct 3 08:39:45 UTC 2026
Reviewed-by: Elena Zannoni <elena.zannoni at 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 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
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
>
More information about the DTrace-devel
mailing list