[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