From: Kris Van Hees <kris.van.hees@oracle.com>
To: dtrace@lists.linux.dev, dtrace-devel@oss.oracle.com
Subject: [PATCH 03/20] ctf: give shared kernel types a synthetic module owner
Date: Fri, 30 Oct 2026 19:36:31 +0000 [thread overview]
Message-ID: <307cf8d7cbf63295d5448222112686f5@oracle.com> (raw)
Store the shared dictionary in shared_ctf->dm_ctfp and remove dt_shared_ctf
from the DTrace handle. Archive, embedded, and BTF sources use the same
owner; BTF-backed vmlinux imports the parent instead of aliasing it.
Register each dictionary owning module and use a module lookup helper for
shared type consumers.
Release the shared dictionary through module destruction and reserve its
name against userspace module creation. Retain handle-owned shared BTF until
explicit BTF parents are introduced, and release the BTF path string on
handle teardown.
Add regression coverage for archive and BTF parent imports, embedded CTF,
missing data, and persistence across module discovery. Exercise shared-first
and vmlinux-first loading on separate handles.
Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
---
libdtrace/dt_btf.c | 47 +++--
libdtrace/dt_cc.c | 11 +-
libdtrace/dt_cg.c | 2 +-
libdtrace/dt_impl.h | 10 +-
libdtrace/dt_module.c | 74 ++++++--
libdtrace/dt_module.h | 1 +
libdtrace/dt_open.c | 3 +-
libdtrace/dt_prov_proc.c | 5 +-
libdtrace/dt_provider_tp.c | 3 +-
test/internals/btf/tst.shared-ctf.sh | 14 ++
test/triggers/Build | 7 +-
test/triggers/shared-ctf.c | 265 +++++++++++++++++++++++++++
12 files changed, 392 insertions(+), 50 deletions(-)
create mode 100755 test/internals/btf/tst.shared-ctf.sh
create mode 100644 test/triggers/shared-ctf.c
diff --git a/libdtrace/dt_btf.c b/libdtrace/dt_btf.c
index 54011e4d..dce44987 100644
--- a/libdtrace/dt_btf.c
+++ b/libdtrace/dt_btf.c
@@ -383,7 +383,7 @@ dt_btf_add_to_ctf(dtrace_hdl_t *dtp, dt_btf_t *btf, ctf_dict_t *ctf,
* If we already have shared_ctf, we may be looking for a type in that
* shared_ctf.
*/
- if (dtp->dt_shared_ctf && btf != dtp->dt_shared_btf) {
+ if (dt_module_shared_ctf(dtp) && btf != dtp->dt_shared_btf) {
if (type_id < dtp->dt_shared_btf->type_cnt)
return dtp->dt_shared_btf->ctfids[type_id];
@@ -724,9 +724,13 @@ dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
if (dmp) {
int err;
- err = ctf_import(ctf, dtp->dt_shared_ctf);
- if (err == CTF_ERR)
+ err = ctf_import(ctf, dt_module_shared_ctf(dtp));
+ if (err == CTF_ERR ||
+ ctf_parent_name_set(ctf, "shared_ctf") == CTF_ERR) {
+ dt_ctf_error(dtp, ctf);
+ ctf_close(ctf);
return NULL;
+ }
ctf_setspecific(ctf, dmp);
@@ -775,8 +779,11 @@ dt_btf_to_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp, dt_btf_t *btf)
}
out:
- if (ctf_update(ctf) == CTF_ERR)
+ if (ctf_update(ctf) == CTF_ERR) {
+ dt_ctf_error(dtp, ctf);
+ ctf_close(ctf);
return NULL;
+ }
return ctf;
}
@@ -830,23 +837,25 @@ dt_btf_module_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
#ifdef HAVE_LIBCTF
/*
- * If the module is 'vmlinux', we are creating the shared CTF.
- * If the module uses the shared BTF, we create CTF for a NULL BTF.
- * Otherwise, convert the module BTF.
+ * The shared module owns the dictionary generated from vmlinux BTF.
+ * vmlinux itself, like every other module, imports this dictionary.
*/
- if (strcmp(dmp->dm_name, "vmlinux") == 0)
- ctf = dt_btf_to_ctf(dtp, NULL, btf);
- else if (btf == dtp->dt_shared_btf)
- ctf = dt_btf_to_ctf(dtp, dmp, NULL);
- else
- ctf = dt_btf_to_ctf(dtp, dmp, btf);
+ if (dmp == dtp->dt_exec && dt_module_shared_ctf(dtp) == NULL) {
+ dt_module_t *shared = dt_module_create(dtp, "shared_ctf");
- /*
- * If generating CTF data for the main kernel, store it as the shared
- * CTF.
- */
- if (!dtp->dt_shared_ctf && dtp->dt_shared_btf && btf == dtp->dt_shared_btf)
- dtp->dt_shared_ctf = ctf;
+ if (shared == NULL) {
+ dt_set_errno(dtp, EDT_NOMEM);
+ return NULL;
+ }
+ shared->dm_ctfp = dt_btf_to_ctf(dtp, NULL, btf);
+ if (shared->dm_ctfp == NULL)
+ return NULL;
+ ctf_setspecific(shared->dm_ctfp, shared);
+ shared->dm_flags |= DT_DM_LOADED;
+ }
+
+ ctf = dt_btf_to_ctf(dtp, dmp,
+ btf == dtp->dt_shared_btf ? NULL : btf);
dt_dprintf("Generated %s CTF from BTF (%d types).\n", dmp->dm_name,
btf->type_cnt);
diff --git a/libdtrace/dt_cc.c b/libdtrace/dt_cc.c
index b49ecb0e..4b639bdd 100644
--- a/libdtrace/dt_cc.c
+++ b/libdtrace/dt_cc.c
@@ -79,6 +79,7 @@
#include <dirent.h>
#include <port.h>
#include <dt_pcap.h>
+#include <dt_module.h>
#include <dt_program.h>
#include <dt_provider.h>
#include <dt_probe.h>
@@ -1084,7 +1085,7 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
case DT_CONST_TASK_REAL_PARENT:
case DT_CONST_TASK_COMM:
case DT_CONST_TASK_MM: {
- ctf_file_t *cfp = dtp->dt_shared_ctf;
+ ctf_file_t *cfp = dt_module_shared_ctf(dtp);
ctf_id_t type;
ctf_membinfo_t ctm;
int rc = 0;
@@ -1120,7 +1121,7 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
}
case DT_CONST_TASK_MM_ARG_START:
case DT_CONST_TASK_MM_ARG_END: {
- ctf_file_t *cfp = dtp->dt_shared_ctf;
+ ctf_file_t *cfp = dt_module_shared_ctf(dtp);
ctf_id_t type;
ctf_membinfo_t ctm;
int rc = 0;
@@ -1146,7 +1147,7 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
continue;
}
case DT_CONST_MUTEX_OWNER: {
- ctf_file_t *cfp = dtp->dt_shared_ctf;
+ ctf_file_t *cfp = dt_module_shared_ctf(dtp);
ctf_id_t type;
ctf_membinfo_t ctm;
int rc;
@@ -1175,7 +1176,7 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
*
* FIXME: what about CONFIG_PREEMPT_RT?
*/
- ctf_file_t *cfp = dtp->dt_shared_ctf;
+ ctf_file_t *cfp = dt_module_shared_ctf(dtp);
ctf_id_t type;
ctf_membinfo_t ctm;
int rc;
@@ -1256,7 +1257,7 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
return pc;
err_ctf:
- dtp->dt_ctferr = ctf_errno(dtp->dt_shared_ctf);
+ dtp->dt_ctferr = ctf_errno(dt_module_shared_ctf(dtp));
return dt_set_errno(dtp, EDT_CTF);
}
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 275a311d..03d114d4 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -5329,7 +5329,7 @@ dt_cg_uregs(unsigned int idx, dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp
* since it can be smaller, at least on aarch64.
*/
{
- ctf_file_t *cfp = yypcb->pcb_hdl->dt_shared_ctf;
+ ctf_file_t *cfp = dt_module_shared_ctf(yypcb->pcb_hdl);
ctf_id_t type;
type = ctf_lookup_by_name(cfp, "struct pt_regs");
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 0290e0b4..682dda82 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -198,10 +198,11 @@ typedef struct dt_kern_path {
char *dkp_path; /* full name including path */
} dt_kern_path_t;
-#define DT_DM_LOADED 0x1 /* module symbol and type data is loaded */
-#define DT_DM_KERNEL 0x2 /* module is associated with a kernel object */
-#define DT_DM_CTF_ARCHIVED 0x4 /* module found in a CTF archive */
-#define DT_DM_KERN_UNLOADED 0x8 /* module not loaded into the kernel */
+#define DT_DM_LOADED 0x01 /* module symbol and type data is loaded */
+#define DT_DM_KERNEL 0x02 /* module is associated with a kernel object */
+#define DT_DM_CTF_ARCHIVED 0x04 /* module found in a CTF archive */
+#define DT_DM_KERN_UNLOADED 0x08 /* module not loaded into the kernel */
+#define DT_DM_SHARED 0x20 /* module owns shared kernel CTF */
#define DT_DM_KERN_SYMS 0x40 /* kernel symbols discovered and finalized */
/*
@@ -311,7 +312,6 @@ struct dtrace_hdl {
void *dt_ctfa_data; /* backing data for dt_ctfa */
char *dt_btf_path; /* path to vmlinux.btf */
struct dt_btf *dt_shared_btf; /* BTF data for the kernel (shared) */
- ctf_file_t *dt_shared_ctf; /* Handle to the shared CTF */
dt_htab_t *dt_kernpaths; /* hash table of dt_kern_path_t's */
dt_module_t *dt_exec; /* pointer to executable module */
dt_module_t *dt_cdefs; /* pointer to C dynamic type module */
diff --git a/libdtrace/dt_module.c b/libdtrace/dt_module.c
index c98297e7..10e54412 100644
--- a/libdtrace/dt_module.c
+++ b/libdtrace/dt_module.c
@@ -342,6 +342,10 @@ dt_module_create(dtrace_hdl_t *dtp, const char *name)
else
dmp->dm_ops = &dt_modops_32;
dmp->dm_dtp = dtp;
+ if (strcmp(name, "shared_ctf") == 0) {
+ dmp->dm_flags |= DT_DM_KERNEL | DT_DM_SHARED;
+ dmp->dm_ops = NULL;
+ }
dt_dprintf("created module %s\n", dmp->dm_name);
@@ -398,6 +402,11 @@ dt_module_create_user(dtrace_hdl_t *dtp, const char *name)
if (p != NULL)
name = p + 1;
+ if (strcmp(name, "shared_ctf") == 0) {
+ dt_set_errno(dtp, EEXIST);
+ goto fail;
+ }
+
if (strlen(name) >= DTRACE_MODNAMELEN ||
strlen(file) >= sizeof(dmp->dm_file)) {
dt_set_errno(dtp, ENAMETOOLONG);
@@ -455,6 +464,15 @@ dt_module_lookup_by_ctf(dtrace_hdl_t *dtp, ctf_file_t *ctfp)
return ctfp ? ctf_getspecific(ctfp) : NULL;
}
+/* Return the shared dictionary without loading or creating a module. */
+ctf_file_t *
+dt_module_shared_ctf(dtrace_hdl_t *dtp)
+{
+ dt_module_t *dmp = dt_module_lookup_by_name(dtp, "shared_ctf");
+
+ return dmp == NULL ? NULL : dmp->dm_ctfp;
+}
+
static void
dt_module_delete_registered(dtrace_hdl_t *dtp, dt_module_t *dmp)
{
@@ -762,7 +780,8 @@ dt_module_load(dtrace_hdl_t *dtp, dt_module_t *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);
+ if (!(dmp->dm_flags & DT_DM_SHARED))
+ dt_kern_module_find_btf(dtp, dmp);
/*
* Find where the kernel module is and preliminarily load its CTF.
@@ -771,13 +790,23 @@ dt_module_load(dtrace_hdl_t *dtp, dt_module_t *dmp)
dt_kern_module_find_ctf(dtp, dmp);
}
+ /* An archive- or BTF-backed shared dictionary has no ELF symbols. */
+ if (dmp->dm_flags & DT_DM_SHARED) {
+ if (dmp->dm_ctfp != NULL) {
+ dmp->dm_flags |= DT_DM_LOADED;
+ return 0;
+ }
+ if (dmp->dm_file[0] == '\0')
+ return dt_set_errno(dtp, EDT_NOCTF);
+ }
+
/*
* Modules not found in the CTF archive, including non-kernel modules,
* must pull in the symbol and string tables (in addition to, perhaps,
* the CTF section.)
*/
- if (!(dmp->dm_flags & DT_DM_CTF_ARCHIVED)) {
+ if (dmp->dm_ctfp == NULL) {
if ((dmp->dm_elf == NULL) && (dt_module_init_elf(dtp, dmp) != 0))
return -1; /* dt_errno is set for us */
@@ -960,7 +989,7 @@ dt_module_getctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
if ((parent = ctf_parent_name(dmp->dm_ctfp)) != NULL) {
assert(strcmp(parent, "shared_ctf") == 0);
- if (ctf_import(dmp->dm_ctfp, dtp->dt_shared_ctf) < 0) {
+ if (ctf_import(dmp->dm_ctfp, dt_module_shared_ctf(dtp)) < 0) {
dt_dprintf("Importing of CTF for %s failed: %s\n",
dmp->dm_name, ctf_errmsg(ctf_errno(dmp->dm_ctfp)));
dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp);
@@ -1018,8 +1047,7 @@ dt_module_destroy(dtrace_hdl_t *dtp, dt_module_t *dmp)
{
if (dmp->dm_btf != dtp->dt_shared_btf)
dt_btf_destroy(dtp, dmp->dm_btf);
- if (dmp->dm_ctfp != dtp->dt_shared_ctf)
- ctf_close(dmp->dm_ctfp);
+ ctf_close(dmp->dm_ctfp);
dt_module_clear_elf(dmp);
dt_symtab_destroy(dtp, dmp->dm_kernsyms);
free(dmp->dm_text_addrs);
@@ -1215,8 +1243,6 @@ dt_kern_module_ctf_from_btf(dtrace_hdl_t *dtp, dt_module_t *dmp)
dt_dprintf("Cannot generate CTF for module %s from BTF: %s; "
"looking for out-of-tree module.\n",
dmp->dm_name, ctf_errmsg(dtp->dt_ctferr));
- else
- dmp->dm_flags |= DT_DM_CTF_ARCHIVED;
}
#endif
@@ -1254,6 +1280,13 @@ 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)
{
+ dt_module_t *shared = dt_module_create(dtp, "shared_ctf");
+
+ if (shared == NULL) {
+ dt_set_errno(dtp, EDT_NOMEM);
+ return;
+ }
+
/*
* Module not already known as a kernel module? It must be an unloaded
* one, since we do not support userspace modules yet.
@@ -1279,7 +1312,7 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
*
* Check for a CTF archive containing the specified module.
*/
- if (dtp->dt_ctfa == NULL && dtp->dt_shared_ctf == NULL) {
+ if (dtp->dt_ctfa == NULL && shared->dm_ctfp == NULL) {
char *ctfa_name;
char *to;
@@ -1301,10 +1334,10 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
/*
* Load in the shared CTF immediately.
*/
- dtp->dt_shared_ctf = ctf_arc_open_by_name(dtp->dt_ctfa,
+ shared->dm_ctfp = ctf_arc_open_by_name(dtp->dt_ctfa,
"shared_ctf", &dtp->dt_ctferr);
- if (dtp->dt_shared_ctf == NULL) {
+ if (shared->dm_ctfp == NULL) {
dt_dprintf("Cannot get shared CTF from archive %s: %s.",
ctfa_name, ctf_errmsg(dtp->dt_ctferr));
ctf_arc_close(dtp->dt_ctfa);
@@ -1312,6 +1345,8 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
free(dtp->dt_ctfa_data);
dtp->dt_ctfa_data = NULL;
} else {
+ shared->dm_flags |= DT_DM_LOADED | DT_DM_CTF_ARCHIVED;
+ ctf_setspecific(shared->dm_ctfp, shared);
dt_dprintf("Loaded shared CTF from archive %s.\n",
ctfa_name);
}
@@ -1328,9 +1363,10 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
* data for shared_ctf from.
*/
mod = dt_module_lookup_by_name(dtp, "vmlinux");
+ if (mod != NULL && mod->dm_btf == NULL)
+ dt_btf_load_module(dtp, mod);
if (mod != NULL && mod->dm_btf != NULL) {
dt_kern_module_ctf_from_btf(dtp, mod);
- dtp->dt_shared_ctf = mod->dm_ctfp;
} else
dt_dprintf("No BTF data for vmlinux; "
"looking for in-module CTF instead.\n");
@@ -1345,6 +1381,14 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
free(ctfa_name);
}
+ /* The shared module already owns the archive/BTF parent dictionary. */
+ if (dmp == shared && shared->dm_ctfp != NULL)
+ return;
+
+ /* Older kernels carry the shared dictionary in the ctf module. */
+ if (dmp != shared && shared->dm_ctfp == NULL)
+ dt_module_getctf(dtp, shared);
+
if (dtp->dt_ctfa != NULL) {
dt_dprintf("Loading CTF for module %s from archive.\n",
dmp->dm_name);
@@ -1378,7 +1422,7 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
*/
if ((parent = ctf_parent_name(dmp->dm_ctfp)) != NULL) {
assert(strcmp(parent, "shared_ctf") == 0);
- if (ctf_import(dmp->dm_ctfp, dtp->dt_shared_ctf) < 0) {
+ if (ctf_import(dmp->dm_ctfp, dt_module_shared_ctf(dtp)) < 0) {
dt_dprintf("Importing of CTF for %s "
"failed: %s\n", dmp->dm_name,
ctf_errmsg(ctf_errno(dmp->dm_ctfp)));
@@ -1391,7 +1435,8 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
dmp->dm_name, (void *)dmp->dm_ctfp);
}
- dmp->dm_flags |= DT_DM_CTF_ARCHIVED;
+ if (dtp->dt_ctfa != NULL)
+ dmp->dm_flags |= DT_DM_CTF_ARCHIVED;
ctf_setspecific(dmp->dm_ctfp, dmp);
#ifdef HAVE_LIBCTF
} else if (dmp->dm_btf != NULL) {
@@ -1412,7 +1457,8 @@ dt_kern_module_find_ctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
if ((dmp->dm_ctfp == NULL) || (dmp->dm_flags & DT_DM_KERN_UNLOADED)) {
dt_kern_path_t *dkpp = NULL;
- dkpp = dt_kern_path_lookup_by_name(dtp, dmp->dm_name);
+ dkpp = dt_kern_path_lookup_by_name(dtp,
+ dmp == shared ? "ctf" : dmp->dm_name);
/*
* Not found, or not a kernel module at all? That's quite
diff --git a/libdtrace/dt_module.h b/libdtrace/dt_module.h
index a5327d37..c94cf7b5 100644
--- a/libdtrace/dt_module.h
+++ b/libdtrace/dt_module.h
@@ -22,6 +22,7 @@ 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_shared_ctf(dtrace_hdl_t *);
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 *,
const char *, const dtrace_typeinfo_t *);
diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
index 9dd15483..6182a5da 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -1316,8 +1316,6 @@ dtrace_close(dtrace_hdl_t *dtp)
if (dtp->dt_shared_btf != NULL)
dt_btf_destroy(dtp, dtp->dt_shared_btf);
- if (dtp->dt_shared_ctf != NULL)
- ctf_close(dtp->dt_shared_ctf);
if (dtp->dt_ctfa != NULL)
ctf_arc_close(dtp->dt_ctfa);
free(dtp->dt_ctfa_data);
@@ -1369,6 +1367,7 @@ dtrace_close(dtrace_hdl_t *dtp)
free(dtp->dt_drops);
free(dtp->dt_module_path);
free(dtp->dt_ctfa_path);
+ free(dtp->dt_btf_path);
free(dtp);
dt_debug_dump(0);
diff --git a/libdtrace/dt_prov_proc.c b/libdtrace/dt_prov_proc.c
index f7e46a5a..af3105e4 100644
--- a/libdtrace/dt_prov_proc.c
+++ b/libdtrace/dt_prov_proc.c
@@ -1,11 +1,12 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*
* The 'proc' SDT provider for DTrace specific probes.
*/
+#include <dt_module.h>
#include <assert.h>
#include <errno.h>
@@ -131,7 +132,7 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(0), BPF_REG_0));
}
} else if (strcmp(prp->desc->prb, "exit") == 0) {
- ctf_file_t *cfp = dtp->dt_shared_ctf;
+ ctf_file_t *cfp = dt_module_shared_ctf(dtp);
ctf_id_t type;
ctf_membinfo_t ctm;
int rc = 0;
diff --git a/libdtrace/dt_provider_tp.c b/libdtrace/dt_provider_tp.c
index 564cb66c..93fa8417 100644
--- a/libdtrace/dt_provider_tp.c
+++ b/libdtrace/dt_provider_tp.c
@@ -6,6 +6,7 @@
*
* Provider support code for tracepoint-based probes.
*/
+#include <dt_module.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
@@ -322,7 +323,7 @@ dt_tp_event_info(dtrace_hdl_t *dtp, FILE *f, dt_valid_arg_f *valid_arg,
strp = tstr;
if (alpha) {
- ctf_file_t *ctfp = dtp->dt_shared_ctf;
+ ctf_file_t *ctfp = dt_module_shared_ctf(dtp);
ctf_id_t type;
size_t esize = 1;
diff --git a/test/internals/btf/tst.shared-ctf.sh b/test/internals/btf/tst.shared-ctf.sh
new file mode 100755
index 00000000..ade9cc6a
--- /dev/null
+++ b/test/internals/btf/tst.shared-ctf.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+#
+# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+# ASSERTION: A shared CTF module owns archive, BTF, and embedded dictionaries,
+# survives module discovery, and reports missing type data.
+#
+
+dir=$tmpdir/shared-ctf.$$
+mkdir -p "$dir" || exit 1
+
+test/triggers/shared-ctf "$dir"
diff --git a/test/triggers/Build b/test/triggers/Build
index 20763860..947341c6 100644
--- a/test/triggers/Build
+++ b/test/triggers/Build
@@ -25,7 +25,7 @@ EXTERNAL_TRIGGERS = $(EXTERNAL_64BIT_TRIGGERS) $(if $(NATIVE_BITNESS_ONLY),,$(EX
INTERNAL_64BIT_TRIGGERS = libproc-pldd libproc-consistency libproc-sleeper \
libproc-sleeper-pie libproc-dlmadopen libproc-lookup-by-name \
libproc-lookup-victim libproc-execing-bkpts libproc-execing-bkpts-victim \
- module-discovery
+ module-discovery shared-ctf
INTERNAL_32BIT_TRIGGERS := libproc-sleeper-32 libproc-sleeper-pie-32
INTERNAL_TRIGGERS = $(INTERNAL_64BIT_TRIGGERS) $(if $(NATIVE_BITNESS_ONLY),,$(INTERNAL_32BIT_TRIGGERS))
@@ -253,3 +253,8 @@ module-discovery_LDFLAGS := -Wl,--wrap=fopen64 \
module-discovery_DEPS := build-libdtrace.a build-libproc.a libcommon.a libport.a
module-discovery_LIBS := $(objdir)/build-libdtrace.a $(objdir)/build-libproc.a \
$(objdir)/build-libcommon.a $(objdir)/build-libport.a $(libdtrace_LIBS)
+
+# Exercise shared dictionary ownership using libdtrace's private module APIs.
+shared-ctf_CFLAGS := -Ilibproc -Ilibdtrace -Ilibcommon -Wno-pedantic
+shared-ctf_DEPS := build-libdtrace.a build-libproc.a libcommon.a libport.a
+shared-ctf_LIBS := $(objdir)/build-libdtrace.a $(objdir)/build-libproc.a $(objdir)/build-libcommon.a $(objdir)/build-libport.a $(libdtrace_LIBS)
diff --git a/test/triggers/shared-ctf.c b/test/triggers/shared-ctf.c
new file mode 100644
index 00000000..8d06e9da
--- /dev/null
+++ b/test/triggers/shared-ctf.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/btf.h>
+#include <dt_module.h>
+#include <dt_kernel_module.h>
+
+#ifdef HAVE_LIBCTF
+#define CHECK(expr) do { \
+ if (!(expr)) { \
+ fprintf(stderr, "Check failed at line %d: %s\n", __LINE__, #expr); \
+ return 1; \
+ } \
+} while (0)
+
+static int
+write_elf(const char *path, void *contents, size_t size)
+{
+ Elf *elf;
+ Elf64_Ehdr *ehdr;
+ Elf_Scn *scn;
+ Elf64_Shdr *shdr;
+ Elf_Data *data;
+ char strings[] = "\0.shstrtab\0.ctf";
+ int fd;
+
+ /* Kernel archives are ELF files containing a .ctf section. */
+ CHECK(elf_version(EV_CURRENT) != EV_NONE);
+ CHECK((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0);
+ CHECK((elf = elf_begin(fd, ELF_C_WRITE, NULL)) != NULL);
+ CHECK((ehdr = elf64_newehdr(elf)) != NULL);
+ ehdr->e_type = ET_REL;
+ ehdr->e_shstrndx = 1;
+ CHECK((scn = elf_newscn(elf)) != NULL);
+ CHECK((shdr = elf64_getshdr(scn)) != NULL);
+ shdr->sh_name = 1;
+ shdr->sh_type = SHT_STRTAB;
+ CHECK((data = elf_newdata(scn)) != NULL);
+ data->d_buf = strings;
+ data->d_size = sizeof(strings);
+ data->d_align = 1;
+ CHECK((scn = elf_newscn(elf)) != NULL);
+ CHECK((shdr = elf64_getshdr(scn)) != NULL);
+ shdr->sh_name = sizeof("\0.shstrtab");
+ shdr->sh_type = SHT_PROGBITS;
+ CHECK((data = elf_newdata(scn)) != NULL);
+ data->d_buf = contents;
+ data->d_size = size;
+ data->d_align = 1;
+ CHECK(elf_update(elf, ELF_C_WRITE) > 0);
+ CHECK(elf_end(elf) == 0);
+ CHECK(close(fd) == 0);
+ return 0;
+}
+
+static int
+write_archive(const char *path, const char *embedded)
+{
+ ctf_dict_t *dicts[2];
+ const char *names[] = { "shared_ctf", "vmlinux" };
+ ctf_encoding_t enc = { CTF_INT_SIGNED, 0, 32 };
+ ctf_id_t integer, structure;
+ FILE *raw;
+ void *contents;
+ long size;
+ size_t embedded_size;
+ void *embedded_data;
+ int err;
+
+ CHECK((dicts[0] = ctf_create(&err)) != NULL);
+ CHECK((dicts[1] = ctf_create(&err)) != NULL);
+ CHECK(ctf_setmodel(dicts[0], CTF_MODEL_LP64) == 0);
+ CHECK(ctf_setmodel(dicts[1], CTF_MODEL_LP64) == 0);
+ integer = ctf_add_integer(dicts[0], CTF_ADD_ROOT, "shared_test_int", &enc);
+ CHECK(integer != CTF_ERR);
+ structure = ctf_add_struct(dicts[0], CTF_ADD_ROOT, "shared_test");
+ CHECK(structure != CTF_ERR);
+ CHECK(ctf_add_member(dicts[0], structure, "member", integer) == 0);
+ CHECK(ctf_update(dicts[0]) == 0);
+ CHECK(ctf_import(dicts[1], dicts[0]) == 0);
+ CHECK(ctf_parent_name_set(dicts[1], "shared_ctf") == 0);
+ CHECK(ctf_add_integer(dicts[1], CTF_ADD_ROOT, "module_test_int", &enc) != CTF_ERR);
+ CHECK(ctf_update(dicts[1]) == 0);
+ CHECK((raw = tmpfile()) != NULL);
+ CHECK(ctf_arc_write_fd(fileno(raw), dicts, 2, names, 0) == 0);
+ CHECK(fseek(raw, 0, SEEK_END) == 0);
+ CHECK((size = ftell(raw)) > 0);
+ CHECK((contents = malloc(size)) != NULL);
+ rewind(raw);
+ CHECK(fread(contents, size, 1, raw) == 1);
+ CHECK(fclose(raw) == 0);
+ embedded_data = ctf_write_mem(dicts[0], &embedded_size, (size_t)-1);
+ CHECK(embedded_data != NULL);
+ CHECK(write_elf(embedded, embedded_data, embedded_size) == 0);
+ free(embedded_data);
+ ctf_close(dicts[1]);
+ ctf_close(dicts[0]);
+
+ CHECK(write_elf(path, contents, size) == 0);
+ free(contents);
+ return 0;
+}
+
+static int
+write_btf(const char *path)
+{
+ const struct {
+ struct btf_header hdr;
+ struct btf_type integer;
+ uint32_t encoding;
+ struct btf_type structure;
+ struct btf_member member;
+ char strings[sizeof("\0shared_test_int\0shared_test\0member")];
+ } data = {
+ .hdr = {
+ .magic = BTF_MAGIC, .version = BTF_VERSION,
+ .hdr_len = sizeof(struct btf_header),
+ .type_len = 2 * sizeof(struct btf_type) + sizeof(uint32_t) +
+ sizeof(struct btf_member),
+ .str_off = 2 * sizeof(struct btf_type) + sizeof(uint32_t) +
+ sizeof(struct btf_member),
+ .str_len = sizeof(data.strings),
+ },
+ .integer = { .name_off = 1, .info = BTF_KIND_INT << 24, .size = 4 },
+ .encoding = (BTF_INT_SIGNED << 24) | 32,
+ .structure = { .name_off = sizeof("\0shared_test_int"),
+ .info = (BTF_KIND_STRUCT << 24) | 1, .size = 4 },
+ .member = { .name_off = sizeof("\0shared_test_int\0shared_test"),
+ .type = 1 },
+ .strings = "\0shared_test_int\0shared_test\0member",
+ };
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+
+ CHECK(fd >= 0);
+ CHECK(write(fd, &data, sizeof(data)) == sizeof(data));
+ CHECK(close(fd) == 0);
+ return 0;
+}
+
+static int
+check_source(const char *ctfpath, const char *btfpath, int archived,
+ int kernel_first)
+{
+ dtrace_hdl_t *dtp;
+ dtrace_typeinfo_t ti;
+ dt_module_t *shared, *kernel, *child;
+ ctf_file_t *parent = NULL;
+ ctf_file_t *kernel_ctf = NULL;
+ struct dt_btf *kernel_btf = NULL;
+ ctf_membinfo_t member;
+ int err, i;
+
+ CHECK((dtp = dtrace_open(DTRACE_VERSION, DTRACE_O_LP64, &err)) != NULL);
+ CHECK(dtrace_setopt(dtp, "ctfpath", ctfpath) == 0);
+ CHECK(dtrace_setopt(dtp, "btfpath", btfpath) == 0);
+
+ /* Updates preserve dictionaries and BTF after parents and children load. */
+ for (i = 0; i < 3; i++) {
+ CHECK(dtrace_update(dtp) == 0);
+ CHECK(dt_module_shared_ctf(dtp) == parent);
+ kernel = dtp->dt_exec;
+ CHECK(kernel != NULL && kernel->dm_ctfp == kernel_ctf);
+ CHECK(kernel->dm_btf == kernel_btf);
+ CHECK((shared = dt_module_create(dtp, "shared_ctf")) != NULL);
+
+ /* Both the synthetic module and vmlinux can initialize the source. */
+ if (kernel_first) {
+ CHECK(dtrace_lookup_by_type(dtp, "vmlinux",
+ "struct shared_test", &ti) == 0);
+ CHECK(!!(kernel->dm_flags & DT_DM_CTF_ARCHIVED) == archived);
+ }
+ CHECK(dtrace_lookup_by_type(dtp, "shared_ctf", "struct shared_test", &ti) == 0);
+ parent = dt_module_shared_ctf(dtp);
+ CHECK(parent != NULL && parent == shared->dm_ctfp);
+ CHECK(dt_module_lookup_by_ctf(dtp, parent) == shared);
+ CHECK(ctf_member_info(parent, ti.dtt_type, "member", &member) == 0);
+ CHECK(member.ctm_offset == 0);
+ CHECK(ctf_type_size(parent, ti.dtt_type) == 4);
+ CHECK(dtrace_lookup_by_type(dtp, "vmlinux", "struct shared_test", &ti) == 0);
+ CHECK(ti.dtt_ctfp == kernel->dm_ctfp && ti.dtt_ctfp != parent);
+ kernel_ctf = kernel->dm_ctfp;
+ kernel_btf = kernel->dm_btf;
+ CHECK(dt_module_lookup_by_ctf(dtp, ti.dtt_ctfp) == kernel);
+ CHECK(ctf_parent_dict(ti.dtt_ctfp) == parent);
+
+ if (archived) {
+ CHECK(dtrace_lookup_by_type(dtp, "vmlinux", "module_test_int", &ti) == 0);
+ CHECK(ctf_lookup_by_name(parent, "module_test_int") == CTF_ERR);
+ } else {
+ /* A module without local BTF must import the same parent. */
+ CHECK((child = dt_module_create(dtp, "shared_ctf_test_child")) != NULL);
+ child->dm_flags |= DT_DM_KERNEL;
+ CHECK(dtrace_lookup_by_type(dtp, child->dm_name, "struct shared_test", &ti) == 0);
+ CHECK(ctf_parent_dict(ti.dtt_ctfp) == parent);
+ CHECK(dt_module_lookup_by_ctf(dtp, ti.dtt_ctfp) == child);
+ }
+ }
+ dtrace_close(dtp);
+ return 0;
+}
+static int
+check_embedded(const char *missing, const char *embedded)
+{
+ dtrace_hdl_t *dtp;
+ dtrace_typeinfo_t ti;
+ dt_module_t *shared;
+ int err, i;
+
+ CHECK((dtp = dtrace_open(DTRACE_VERSION, DTRACE_O_LP64, &err)) != NULL);
+ CHECK(dtrace_setopt(dtp, "ctfpath", missing) == 0);
+ CHECK(dtrace_setopt(dtp, "btfpath", "none") == 0);
+ CHECK(dtrace_update(dtp) == 0);
+ /* Use a private path map so host ctf.ko files cannot satisfy this lookup. */
+ dt_htab_destroy(dtp->dt_kernpaths);
+ dtp->dt_kernpaths = NULL;
+ CHECK(dt_kern_path_create(dtp, strdup("shared_ctf_test"), strdup(missing)) != NULL);
+ CHECK((shared = dt_module_create(dtp, "shared_ctf")) != NULL);
+ CHECK(dt_module_getctf(dtp, shared) == NULL);
+ CHECK(dtrace_errno(dtp) == EDT_NOCTF);
+ CHECK(dt_module_shared_ctf(dtp) == NULL);
+
+ /* The old ctf.ko dictionary becomes the synthetic module's dictionary. */
+ CHECK(dt_kern_path_create(dtp, strdup("ctf"), strdup(embedded)) != NULL);
+ for (i = 0; i < 3; i++) {
+ CHECK(dtrace_lookup_by_type(dtp, "shared_ctf", "struct shared_test", &ti) == 0);
+ CHECK(ti.dtt_ctfp == dt_module_shared_ctf(dtp));
+ CHECK(dt_module_lookup_by_ctf(dtp, ti.dtt_ctfp) == shared);
+ CHECK(ctf_type_size(ti.dtt_ctfp, ti.dtt_type) == 4);
+ CHECK(dtrace_update(dtp) == 0);
+ CHECK(dt_module_shared_ctf(dtp) == ti.dtt_ctfp);
+ }
+ dtrace_close(dtp);
+ return 0;
+}
+
+#endif
+
+int
+main(int argc, char **argv)
+{
+#ifdef HAVE_LIBCTF
+ char archive[PATH_MAX], btf[PATH_MAX], missing[PATH_MAX], embedded[PATH_MAX];
+
+ CHECK(argc == 2);
+ snprintf(archive, sizeof(archive), "%s/test.ctfa", argv[1]);
+ snprintf(btf, sizeof(btf), "%s/vmlinux", argv[1]);
+ snprintf(missing, sizeof(missing), "%s/missing.ctfa", argv[1]);
+ snprintf(embedded, sizeof(embedded), "%s/ctf.ko", argv[1]);
+ CHECK(write_archive(archive, embedded) == 0);
+ CHECK(write_btf(btf) == 0);
+ CHECK(check_source(archive, "none", 1, 0) == 0);
+ CHECK(check_source(archive, "none", 1, 1) == 0);
+ CHECK(check_source(missing, argv[1], 0, 0) == 0);
+ CHECK(check_source(missing, argv[1], 0, 1) == 0);
+ CHECK(check_embedded(missing, embedded) == 0);
+#endif
+ return 0;
+}
--
2.52.0
reply other threads:[~2026-10-30 19:38 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=307cf8d7cbf63295d5448222112686f5@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.