[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