From: Kris Van Hees <kris.van.hees@oracle.com>
To: dtrace@lists.linux.dev, dtrace-devel@oss.oracle.com
Subject: [PATCH 15/20] libdtrace: search both ELF symbol tables for module symbols
Date: Fri, 30 Oct 2026 19:36:55 +0000 [thread overview]
Message-ID: <9a3ef30ca35af2d994b6f8a42a8e6700@oracle.com> (raw)
Keep independent symbol and address indices for SHT_SYMTAB and SHT_DYNSYM,
loading strings through each table’s sh_link. Search .symtab before .dynsym
and include dynamic-only names in module symbol iteration.
Preserve .symtab indices for CTF, place dynamic and external symbols in
separate ID ranges, and use .dynsym for CTF when .symtab is absent. Allow
offline kernel modules with known address ranges to use ELF address lookup
while retaining runtime symbol lookup for loaded kernel modules.
Add synthetic 32-bit and 64-bit ELF tests for missing and individual tables,
duplicate-name precedence, distinct linked string tables, public name and
address lookup, symbol IDs, and iteration for user and offline kernel
modules.
---
libdtrace/dt_impl.h | 41 +++--
libdtrace/dt_module.c | 281 +++++++++++++++++++-----------
libdtrace/dt_symbol_modops.h | 16 +-
test/internals/elf/tst.symbols.sh | 10 ++
test/triggers/Build | 8 +-
test/triggers/elf-symbols.c | 203 +++++++++++++++++++++
test/triggers/shared-ctf.c | 6 +
7 files changed, 433 insertions(+), 132 deletions(-)
create mode 100755 test/internals/elf/tst.symbols.sh
create mode 100644 test/triggers/elf-symbols.c
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index c83fedb2..adb20884 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -115,12 +115,14 @@ typedef struct dt_intdesc {
uintmax_t did_limit; /* maximum positive value held by type */
} dt_intdesc_t;
+struct dt_module_symbols;
+
typedef struct dt_modops {
- uint_t (*do_syminit)(struct dt_module *);
- void (*do_symsort)(struct dt_module *);
- GElf_Sym *(*do_symname)(struct dt_module *,
+ uint_t (*do_syminit)(struct dt_module_symbols *);
+ void (*do_symsort)(struct dt_module_symbols *);
+ GElf_Sym *(*do_symname)(const struct dt_module_symbols *,
const char *, GElf_Sym *, uint_t *);
- GElf_Sym *(*do_symaddr)(struct dt_module *,
+ GElf_Sym *(*do_symaddr)(const struct dt_module_symbols *,
GElf_Addr, GElf_Sym *, uint_t *);
} dt_modops_t;
@@ -138,6 +140,20 @@ typedef struct dt_modsym {
uint_t dms_next; /* index of next element in hash chain */
} dt_modsym_t;
+/* Independent ELF symbol tables: preserve indices used by CTF. */
+typedef struct dt_module_symbols {
+ ctf_sect_t dm_symtab;
+ ctf_sect_t dm_strtab;
+ uint_t *dm_symbuckets; /* symbol table hash buckets (chain indices) */
+ dt_modsym_t *dm_symchains; /* symbol table hash chains buffer */
+ void *dm_asmap; /* symbol pointers sorted by value */
+ uint_t dm_symfree; /* index of next free hash element */
+ uint_t dm_nsymbuckets; /* number of elements in bucket array */
+ uint_t dm_nsymelems; /* number of elements in hash table */
+ uint_t dm_asrsv; /* actual reserved size of dm_asmap */
+ uint_t dm_aslen; /* number of entries in dm_asmap */
+} dt_module_symbols_t;
+
typedef struct dt_module {
dt_list_t dm_list; /* list forward/back pointers */
char dm_name[DTRACE_MODNAMELEN]; /* string name of module */
@@ -157,7 +173,7 @@ typedef struct dt_module {
dt_idhash_t *dm_extern; /* external symbol definitions */
/*
- * Kernel modules populate the fields below only for the sake of CTF.
+ * Loaded kernel modules populate these fields only for the sake of CTF.
* When a CTF archive is available, most are not populated at all.
*/
const dt_modops_t *dm_ops; /* pointer to data model's ops vector */
@@ -165,8 +181,7 @@ typedef struct dt_module {
ctf_file_t *dm_ctfp; /* CTF container handle */
void *dm_ctdata_data; /* CTF section data (dynamically allocated) */
ctf_sect_t dm_ctdata; /* CTF data for module */
- ctf_sect_t dm_symtab; /* symbol table */
- ctf_sect_t dm_strtab; /* string table */
+ dt_module_symbols_t dm_symbols[2]; /* .symtab, then .dynsym */
uint32_t dm_btf_id; /* BTF dict id */
struct dt_btf *dm_btf; /* BTF data for module */
@@ -174,18 +189,6 @@ typedef struct dt_module {
* Kernel modules only.
*/
dt_symtab_t *dm_kernsyms; /* module kernel symbol table */
-
- /*
- * Userspace modules only.
- */
- uint_t *dm_symbuckets; /* symbol table hash buckets (chain indices) */
- dt_modsym_t *dm_symchains; /* symbol table hash chains buffer */
- void *dm_asmap; /* symbol pointers sorted by value */
- uint_t dm_symfree; /* index of next free hash element */
- uint_t dm_nsymbuckets; /* number of elements in bucket array */
- uint_t dm_nsymelems; /* number of elements in hash table */
- uint_t dm_asrsv; /* actual reserved size of dm_asmap */
- uint_t dm_aslen; /* number of entries in dm_asmap */
} dt_module_t;
/* Handle-wide address index. order preserves module-list precedence. */
diff --git a/libdtrace/dt_module.c b/libdtrace/dt_module.c
index 3b40c1cb..2b91f1e6 100644
--- a/libdtrace/dt_module.c
+++ b/libdtrace/dt_module.c
@@ -270,11 +270,11 @@ static dt_htab_ops_t dt_module_htab_ops = {
};
/*
- * Symbol table management for userspace modules, via ELF parsing.
+ * Symbol table management for ELF-backed modules.
*/
static void
-dt_module_symhash_insert(dt_module_t *dmp, const char *name, uint_t id)
+dt_module_symhash_insert(dt_module_symbols_t *dmp, const char *name, uint_t id)
{
dt_modsym_t *dsp = &dmp->dm_symchains[dmp->dm_symfree];
uint_t h;
@@ -322,6 +322,56 @@ dt_module_symgelf64(const Elf64_Sym *src, GElf_Sym *dst)
#include <dt_symbol_modops.h>
#undef BITS
+/*
+ * Keep the ELF tables independent so .symtab IDs remain valid CTF IDs.
+ * Dynamic symbols occupy the public ID range following .symtab.
+ */
+static GElf_Sym *
+dt_module_symname(dt_module_t *dmp, const char *name, GElf_Sym *symp,
+ uint_t *idp)
+{
+ uint_t id;
+
+ if (dmp->dm_ops->do_symname(&dmp->dm_symbols[0], name, symp, &id)) {
+ if (idp != NULL)
+ *idp = id;
+ return symp;
+ }
+ if (dmp->dm_ops->do_symname(&dmp->dm_symbols[1], name, symp, &id)) {
+ if (idp != NULL)
+ *idp = dmp->dm_symbols[0].dm_nsymelems + id;
+ return symp;
+ }
+ return NULL;
+}
+
+static GElf_Sym *
+dt_module_symaddr(dt_module_t *dmp, GElf_Addr addr, GElf_Sym *symp,
+ uint_t *idp)
+{
+ uint_t id;
+
+ if (dmp->dm_ops->do_symaddr(&dmp->dm_symbols[0], addr, symp, &id)) {
+ if (idp != NULL)
+ *idp = id;
+ return symp;
+ }
+ if (dmp->dm_ops->do_symaddr(&dmp->dm_symbols[1], addr, symp, &id)) {
+ if (idp != NULL)
+ *idp = dmp->dm_symbols[0].dm_nsymelems + id;
+ return symp;
+ }
+ return NULL;
+}
+
+static const char *
+dt_module_symstr(dt_module_t *dmp, uint_t id, const GElf_Sym *sym)
+{
+ int t = id >= dmp->dm_symbols[0].dm_nsymelems;
+
+ return (const char *)dmp->dm_symbols[t].dm_strtab.cts_data + sym->st_name;
+}
+
dt_module_t *
dt_module_create(dtrace_hdl_t *dtp, const char *name)
{
@@ -777,6 +827,73 @@ static void *dt_ctf_uncompress(dt_module_t *dmp, ctf_sect_t *ctsp)
return NULL;
}
+/* Load a symbol table and its linked string table, including stripped ELFs. */
+static int
+dt_module_load_symbols(dtrace_hdl_t *dtp, dt_module_t *dmp,
+ dt_module_symbols_t *syms, uint_t type)
+{
+ Elf_Scn *scn = NULL, *strscn;
+ Elf_Data *data, *strdata;
+ GElf_Shdr sh, strsh;
+ size_t entsize = dmp->dm_ops == &dt_modops_64 ?
+ sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
+
+ while ((scn = elf_nextscn(dmp->dm_elf, scn)) != NULL) {
+ if (gelf_getshdr(scn, &sh) == NULL || sh.sh_type != type)
+ continue;
+
+ if (sh.sh_entsize != entsize || sh.sh_size % entsize != 0 ||
+ sh.sh_size / entsize >= UINT_MAX ||
+ (strscn = elf_getscn(dmp->dm_elf, sh.sh_link)) == NULL ||
+ gelf_getshdr(strscn, &strsh) == NULL ||
+ strsh.sh_type != SHT_STRTAB ||
+ (data = elf_getdata(scn, NULL)) == NULL ||
+ (strdata = elf_getdata(strscn, NULL)) == NULL ||
+ data->d_size != sh.sh_size || strdata->d_size != strsh.sh_size ||
+ (data->d_size != 0 && data->d_buf == NULL) ||
+ (strdata->d_size != 0 && strdata->d_buf == NULL))
+ return dt_set_errno(dtp, EDT_NOMOD);
+
+ syms->dm_symtab.cts_name = type == SHT_SYMTAB ? ".symtab" : ".dynsym";
+ syms->dm_symtab.cts_data = data->d_buf;
+ syms->dm_symtab.cts_size = data->d_size;
+ syms->dm_symtab.cts_entsize = entsize;
+ syms->dm_strtab.cts_name = type == SHT_SYMTAB ? ".strtab" : ".dynstr";
+ syms->dm_strtab.cts_data = strdata->d_buf;
+ syms->dm_strtab.cts_size = strdata->d_size;
+#ifndef HAVE_LIBCTF
+ syms->dm_symtab.cts_type = type;
+ syms->dm_strtab.cts_type = SHT_STRTAB;
+#endif
+ syms->dm_nsymelems = data->d_size / entsize;
+ if (syms->dm_nsymelems == 0)
+ return 0;
+
+ syms->dm_nsymbuckets = _dtrace_strbuckets;
+ syms->dm_symfree = 1;
+ syms->dm_symbuckets = calloc(syms->dm_nsymbuckets, sizeof(uint_t));
+ syms->dm_symchains = calloc((size_t)syms->dm_nsymelems + 1,
+ sizeof(dt_modsym_t));
+ if (syms->dm_symbuckets == NULL || syms->dm_symchains == NULL)
+ return dt_set_errno(dtp, EDT_NOMEM);
+
+ syms->dm_asrsv = dmp->dm_ops->do_syminit(syms);
+ if (syms->dm_asrsv != 0) {
+ syms->dm_asmap = calloc(syms->dm_asrsv, sizeof(void *));
+ if (syms->dm_asmap == NULL)
+ return dt_set_errno(dtp, EDT_NOMEM);
+ dmp->dm_ops->do_symsort(syms);
+ }
+
+ dt_dprintf("loaded %s [%s] (%u symbols, %u addresses)\n",
+ dmp->dm_name, syms->dm_symtab.cts_name,
+ syms->dm_symfree - 1, syms->dm_aslen);
+ return 0;
+ }
+
+ return 0;
+}
+
static int
dt_module_load(dtrace_hdl_t *dtp, dt_module_t *dmp)
{
@@ -864,76 +981,23 @@ dt_module_load(dtrace_hdl_t *dtp, dt_module_t *dmp)
return 0;
}
- dmp->dm_symtab.cts_name = ".symtab";
-#ifndef HAVE_LIBCTF
- dmp->dm_symtab.cts_type = SHT_SYMTAB;
- dmp->dm_symtab.cts_flags = 0;
- dmp->dm_symtab.cts_offset = 0;
-#endif
- dmp->dm_symtab.cts_data = NULL;
- dmp->dm_symtab.cts_size = 0;
- dmp->dm_symtab.cts_entsize = dmp->dm_ops == &dt_modops_64 ?
- sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
-
- dmp->dm_strtab.cts_name = ".strtab";
-#ifndef HAVE_LIBCTF
- dmp->dm_strtab.cts_type = SHT_STRTAB;
- dmp->dm_strtab.cts_flags = 0;
- dmp->dm_strtab.cts_offset = 0;
-#endif
- dmp->dm_strtab.cts_data = NULL;
- dmp->dm_strtab.cts_size = 0;
- dmp->dm_strtab.cts_entsize = 0;
+ /* Cached CTF does not supply the ELF symbols of an unloaded module. */
+ if (dmp->dm_elf == NULL && dt_module_init_elf(dtp, dmp) != 0)
+ return -1; /* dt_errno is set for us */
- /*
- * Now load the module's symbol and string table sections.
- */
- if (dt_module_load_sect(dtp, dmp, &dmp->dm_symtab) == -1 ||
- dt_module_load_sect(dtp, dmp, &dmp->dm_strtab) == -1) {
+ if (dt_module_load_symbols(dtp, dmp, &dmp->dm_symbols[0],
+ SHT_SYMTAB) == -1 ||
+ dt_module_load_symbols(dtp, dmp, &dmp->dm_symbols[1],
+ SHT_DYNSYM) == -1) {
dt_module_clear_elf(dmp);
- return -1; /* dt_errno is set for us */
+ return -1;
}
- /*
- * Allocate the hash chains and hash buckets for symbol name lookup.
- * This is relatively simple since the symbol table is of fixed size
- * and is known in advance. We allocate one extra element since we
- * use element indices instead of pointers and zero is our sentinel.
- */
- dmp->dm_nsymelems =
- dmp->dm_symtab.cts_size / dmp->dm_symtab.cts_entsize;
-
- dmp->dm_nsymbuckets = _dtrace_strbuckets;
- dmp->dm_symfree = 1; /* first free element is index 1 */
-
- dmp->dm_symbuckets = malloc(sizeof(uint_t) * dmp->dm_nsymbuckets);
- dmp->dm_symchains = malloc(sizeof(dt_modsym_t) * dmp->dm_nsymelems + 1);
-
- if (dmp->dm_symbuckets == NULL || dmp->dm_symchains == NULL)
- goto oom;
-
- memset(dmp->dm_symbuckets, 0, sizeof(uint_t) * dmp->dm_nsymbuckets);
- memset(dmp->dm_symchains, 0, sizeof(dt_modsym_t) * dmp->dm_nsymelems
- + 1);
-
- /*
- * Iterate over the symbol table data buffer and insert each symbol
- * name into the name hash if the name and type are valid. Then
- * allocate the address map, fill it in, and sort it.
- */
- dmp->dm_asrsv = dmp->dm_ops->do_syminit(dmp);
-
- dt_dprintf("hashed %s [%s] (%u symbols)\n",
- dmp->dm_name, dmp->dm_symtab.cts_name, dmp->dm_symfree - 1);
-
- if ((dmp->dm_asmap = malloc(sizeof(void *) * dmp->dm_asrsv)) == NULL)
+ /* Keep the combined public symbol ID space representable. */
+ if (dmp->dm_symbols[1].dm_nsymelems >
+ UINT_MAX - dmp->dm_symbols[0].dm_nsymelems)
goto oom;
- dmp->dm_ops->do_symsort(dmp);
-
- dt_dprintf("sorted %s [%s] (%u symbols)\n",
- dmp->dm_name, dmp->dm_symtab.cts_name, dmp->dm_aslen);
-
dmp->dm_flags |= DT_DM_LOADED;
return 0;
oom:
@@ -949,6 +1013,7 @@ dt_module_getctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
{
const char *parent;
int model;
+ dt_module_symbols_t *syms;
if (!(dmp->dm_flags & DT_DM_LOADED))
if (dt_module_load(dtp, dmp) != 0)
@@ -981,12 +1046,14 @@ dt_module_getctf(dtrace_hdl_t *dtp, dt_module_t *dmp)
return NULL;
}
+ syms = &dmp->dm_symbols[dmp->dm_symbols[0].dm_nsymelems == 0];
if (dmp->dm_flags & DT_DM_KERNEL)
dmp->dm_ctfp = ctf_bufopen(&dmp->dm_ctdata, NULL, NULL,
&dtp->dt_ctferr);
else
dmp->dm_ctfp = ctf_bufopen(&dmp->dm_ctdata,
- &dmp->dm_symtab, &dmp->dm_strtab, &dtp->dt_ctferr);
+ &syms->dm_symtab, &syms->dm_strtab,
+ &dtp->dt_ctferr);
if (dmp->dm_ctfp == NULL) {
dt_dprintf("ctf loading for module %s failed: error: %s\n",
@@ -1027,23 +1094,14 @@ dt_module_clear_elf(dt_module_t *dmp)
dmp->dm_ctdata_data = NULL;
memset(&dmp->dm_ctdata, 0, sizeof(ctf_sect_t));
- memset(&dmp->dm_symtab, 0, sizeof(ctf_sect_t));
- memset(&dmp->dm_strtab, 0, sizeof(ctf_sect_t));
-
- free(dmp->dm_symbuckets);
- dmp->dm_symbuckets = NULL;
+ for (int i = 0; i < 2; i++) {
+ dt_module_symbols_t *syms = &dmp->dm_symbols[i];
- free(dmp->dm_symchains);
- dmp->dm_symchains = NULL;
-
- free(dmp->dm_asmap);
- dmp->dm_asmap = NULL;
-
- dmp->dm_symfree = 0;
- dmp->dm_nsymbuckets = 0;
- dmp->dm_nsymelems = 0;
- dmp->dm_asrsv = 0;
- dmp->dm_aslen = 0;
+ free(syms->dm_symbuckets);
+ free(syms->dm_symchains);
+ free(syms->dm_asmap);
+ memset(syms, 0, sizeof(*syms));
+ }
elf_end(dmp->dm_elf);
dmp->dm_elf = NULL;
@@ -1079,7 +1137,9 @@ dt_module_extern(dtrace_hdl_t *dtp, dt_module_t *dmp,
uint_t id;
if (dmp->dm_extern == NULL && (dmp->dm_extern = dt_idhash_create(
- "extern", NULL, dmp->dm_nsymelems, UINT_MAX)) == NULL) {
+ "extern", NULL,
+ dmp->dm_symbols[0].dm_nsymelems +
+ dmp->dm_symbols[1].dm_nsymelems, UINT_MAX)) == NULL) {
dt_set_errno(dtp, EDT_NOMEM);
return NULL;
}
@@ -2315,11 +2375,10 @@ dtrace_lookup_by_name(dtrace_hdl_t *dtp, const char *object, const char *name,
} else {
uint_t id;
- if (dmp->dm_ops->do_symname(dmp, name, symp, &id) != NULL) {
+ if (dt_module_symname(dmp, name, symp, &id) != NULL) {
if (sip != NULL) {
sip->object = dmp->dm_name;
- sip->name = (const char *)
- dmp->dm_strtab.cts_data + symp->st_name;
+ sip->name = dt_module_symstr(dmp, id, symp);
sip->id = id;
}
return 0;
@@ -2494,13 +2553,11 @@ dtrace_lookup_by_addr(dtrace_hdl_t *dtp, GElf_Addr addr,
if (dt_module_load(dtp, dmp) == -1)
return -1; /* dt_errno is set for us */
- if (dmp->dm_flags & DT_DM_KERNEL) {
+ if ((dmp->dm_flags & DT_DM_KERNEL) &&
+ !(dmp->dm_flags & DT_DM_KERN_UNLOADED)) {
dt_symbol_t *dt_symp;
- /*
- * This is probably a non-loaded kernel module. Looking
- * anything up by address in this case is hopeless.
- */
+ /* Loaded kernel modules use runtime addresses from /proc. */
if (!dmp->dm_kernsyms)
return dt_set_errno(dtp, EDT_NOSYMADDR);
@@ -2521,7 +2578,7 @@ dtrace_lookup_by_addr(dtrace_hdl_t *dtp, GElf_Addr addr,
return 0;
} else {
if (symp != NULL) {
- if (dmp->dm_ops->do_symaddr(dmp, addr, symp, &id) == NULL)
+ if (dt_module_symaddr(dmp, addr, symp, &id) == NULL)
return dt_set_errno(dtp, EDT_NOSYMADDR);
}
@@ -2529,8 +2586,7 @@ dtrace_lookup_by_addr(dtrace_hdl_t *dtp, GElf_Addr addr,
sip->object = dmp->dm_name;
if (symp != NULL) {
- sip->name = (const char *)
- dmp->dm_strtab.cts_data + symp->st_name;
+ sip->name = dt_module_symstr(dmp, id, symp);
sip->id = id;
} else {
sip->name = NULL;
@@ -2712,20 +2768,33 @@ dt_module_sym_iter(const dt_module_t *dmp, proc_sym_f *func, void *arg)
uint_t i;
int rv = 0;
- for (i = 0; i < dmp->dm_aslen; i++) {
- const char *name;
+ for (int t = 0; t < 2; t++) {
+ const dt_module_symbols_t *syms = &dmp->dm_symbols[t];
- 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);
+ for (i = 0; i < syms->dm_aslen; i++) {
+ const char *name;
- name = (const char *)dmp->dm_strtab.cts_data + sym.st_name;
+ if (dmp->dm_ops == &dt_modops_64)
+ dt_module_symgelf64(
+ ((const Elf64_Sym **)syms->dm_asmap)[i], &sym);
+ else
+ dt_module_symgelf32(
+ ((const Elf32_Sym **)syms->dm_asmap)[i], &sym);
- if ((rv = func(arg, &sym, name)) != 0)
- break;
+ name = (const char *)syms->dm_strtab.cts_data + sym.st_name;
+
+ /* Do not report symbols already covered by .symtab. */
+ if (t != 0) {
+ GElf_Sym primary;
+
+ if (dmp->dm_ops->do_symname(
+ &dmp->dm_symbols[0],
+ name, &primary, NULL) != NULL)
+ continue;
+ }
+ if ((rv = func(arg, &sym, name)) != 0)
+ return rv;
+ }
}
return rv;
diff --git a/libdtrace/dt_symbol_modops.h b/libdtrace/dt_symbol_modops.h
index d05cb172..e5c2ceda 100644
--- a/libdtrace/dt_symbol_modops.h
+++ b/libdtrace/dt_symbol_modops.h
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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.
*/
@@ -18,7 +18,7 @@
#define ELFIZE(suffix) EXJOINMID(ELF,BITS,_##suffix)
static uint_t
-BITIZE(dt_module_syminit)(dt_module_t *dmp)
+BITIZE(dt_module_syminit)(dt_module_symbols_t *dmp)
{
const ElfIZE(Sym) *sym = dmp->dm_symtab.cts_data;
const char *base = dmp->dm_strtab.cts_data;
@@ -27,7 +27,7 @@ BITIZE(dt_module_syminit)(dt_module_t *dmp)
uint_t asrsv = 0;
for (i = 0; i < n; i++, sym++) {
- const char *name = base + sym->st_name;
+ const char *name;
uchar_t type = ELFIZE(ST_TYPE)(sym->st_info);
if (type >= STT_NUM || type == STT_SECTION)
@@ -36,6 +36,10 @@ BITIZE(dt_module_syminit)(dt_module_t *dmp)
if (sym->st_name == 0 || sym->st_name >= ss_size)
continue; /* skip null or invalid names */
+ name = base + sym->st_name;
+ if (memchr(name, '\0', ss_size - sym->st_name) == NULL)
+ continue; /* skip unterminated names */
+
if (sym->st_value != 0 &&
(ELFIZE(ST_BIND)(sym->st_info) != STB_LOCAL || sym->st_size))
asrsv++; /* reserve space in the address map */
@@ -83,7 +87,7 @@ BITIZE(dt_module_symcomp)(const void *lp, const void *rp, void *strtabp)
}
static void
-BITIZE(dt_module_symsort)(dt_module_t *dmp)
+BITIZE(dt_module_symsort)(dt_module_symbols_t *dmp)
{
ElfIZE(Sym) *symtab = (ElfIZE(Sym) *)dmp->dm_symtab.cts_data;
ElfIZE(Sym) **sympp = (ElfIZE(Sym) **)dmp->dm_asmap;
@@ -106,7 +110,7 @@ BITIZE(dt_module_symsort)(dt_module_t *dmp)
}
static GElf_Sym *
-BITIZE(dt_module_symname)(dt_module_t *dmp, const char *name,
+BITIZE(dt_module_symname)(const dt_module_symbols_t *dmp, const char *name,
GElf_Sym *symp, uint_t *idp)
{
const ElfIZE(Sym) *symtab = dmp->dm_symtab.cts_data;
@@ -136,7 +140,7 @@ BITIZE(dt_module_symname)(dt_module_t *dmp, const char *name,
}
static GElf_Sym *
-BITIZE(dt_module_symaddr)(dt_module_t *dmp, GElf_Addr addr,
+BITIZE(dt_module_symaddr)(const dt_module_symbols_t *dmp, GElf_Addr addr,
GElf_Sym *symp, uint_t *idp)
{
const ElfIZE(Sym) **asmap = (const ElfIZE(Sym) **)dmp->dm_asmap;
diff --git a/test/internals/elf/tst.symbols.sh b/test/internals/elf/tst.symbols.sh
new file mode 100755
index 00000000..2d41e4dd
--- /dev/null
+++ b/test/internals/elf/tst.symbols.sh
@@ -0,0 +1,10 @@
+#!/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: ELF-backed modules search .symtab and .dynsym independently.
+#
+
+test/triggers/elf-symbols
diff --git a/test/triggers/Build b/test/triggers/Build
index 8d648bab..0dd546ac 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 \
- btf-cyclic-types btf-lazy-types shared-ctf module-discovery
+ btf-cyclic-types btf-lazy-types shared-ctf module-discovery elf-symbols
INTERNAL_32BIT_TRIGGERS := libproc-sleeper-32 libproc-sleeper-pie-32
INTERNAL_TRIGGERS = $(INTERNAL_64BIT_TRIGGERS) $(if $(NATIVE_BITNESS_ONLY),,$(INTERNAL_32BIT_TRIGGERS))
@@ -272,3 +272,9 @@ 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)
+
+# Exercise both ELF symbol tables without relying on host binaries.
+elf-symbols_CFLAGS := -Ilibproc -Ilibdtrace -Ilibcommon -Wno-pedantic
+elf-symbols_DEPS := build-libdtrace.a build-libproc.a libcommon.a libport.a
+elf-symbols_LIBS := $(objdir)/build-libdtrace.a $(objdir)/build-libproc.a \
+ $(objdir)/build-libcommon.a $(objdir)/build-libport.a $(libdtrace_LIBS)
diff --git a/test/triggers/elf-symbols.c b/test/triggers/elf-symbols.c
new file mode 100644
index 00000000..dc6adb58
--- /dev/null
+++ b/test/triggers/elf-symbols.c
@@ -0,0 +1,203 @@
+/*
+ * 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 <dt_module.h>
+
+#define CHECK(expr) do { \
+ if (!(expr)) { \
+ fprintf(stderr, "Check failed at line %d: %s\n", __LINE__, #expr); \
+ exit(1); \
+ } \
+} while (0)
+
+/* No section names: the loader must use section types and sh_link. */
+static void
+write_elf(int fd, int bits, int tables)
+{
+ Elf *elf;
+ GElf_Ehdr eh;
+ char strings[] = "\0common\0private";
+ char dynstrings[] = "\0dynamic\0common";
+ union {
+ Elf64_Sym s64[2][3];
+ Elf32_Sym s32[2][3];
+ } storage = { 0 };
+
+ CHECK(ftruncate(fd, 0) == 0);
+ CHECK((elf = elf_begin(fd, ELF_C_WRITE, NULL)) != NULL);
+ CHECK(gelf_newehdr(elf, bits) != 0);
+ CHECK(gelf_getehdr(elf, &eh) != NULL);
+ eh.e_type = ET_DYN;
+ eh.e_machine = bits == ELFCLASS64 ? EM_X86_64 : EM_386;
+ CHECK(gelf_update_ehdr(elf, &eh) != 0);
+
+ for (int t = 0; t < 2; t++) {
+ Elf_Scn *strscn, *symscn;
+ Elf_Data *strdata, *symdata;
+ GElf_Shdr sh = { 0 };
+ GElf_Sym sym = { 0 };
+ size_t entsize = bits == ELFCLASS64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
+
+ if (!(tables & (1 << t)))
+ continue;
+ CHECK((strscn = elf_newscn(elf)) != NULL);
+ CHECK((strdata = elf_newdata(strscn)) != NULL);
+ strdata->d_buf = t == 0 ? strings : dynstrings;
+ strdata->d_size = t == 0 ? sizeof(strings) : sizeof(dynstrings);
+ strdata->d_type = ELF_T_BYTE;
+ strdata->d_version = EV_CURRENT;
+ sh.sh_type = SHT_STRTAB;
+ sh.sh_addralign = 1;
+ CHECK(gelf_update_shdr(strscn, &sh) != 0);
+
+ CHECK((symscn = elf_newscn(elf)) != NULL);
+ CHECK((symdata = elf_newdata(symscn)) != NULL);
+ symdata->d_buf = bits == ELFCLASS64 ?
+ (void *)storage.s64[t] : (void *)storage.s32[t];
+ symdata->d_size = 3 * entsize;
+ symdata->d_type = ELF_T_SYM;
+ symdata->d_version = EV_CURRENT;
+ sh.sh_type = t == 0 ? SHT_SYMTAB : SHT_DYNSYM;
+ sh.sh_link = elf_ndxscn(strscn);
+ sh.sh_info = 1;
+ sh.sh_entsize = entsize;
+ sh.sh_addralign = bits == ELFCLASS64 ? 8 : 4;
+ CHECK(gelf_update_shdr(symscn, &sh) != 0);
+ CHECK(gelf_update_sym(symdata, 0, &sym) != 0);
+ sym.st_name = t == 0 ? 1 : 9; /* common */
+ sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
+ sym.st_shndx = SHN_ABS;
+ sym.st_value = t == 0 ? 0x1000 : 0x1100;
+ sym.st_size = 16;
+ CHECK(gelf_update_sym(symdata, 1, &sym) != 0);
+ sym.st_name = t == 0 ? 8 : 1;
+ sym.st_value = t == 0 ? 0x1200 : 0x1300;
+ CHECK(gelf_update_sym(symdata, 2, &sym) != 0);
+ }
+ CHECK(elf_update(elf, ELF_C_WRITE) != -1);
+ CHECK(elf_end(elf) == 0);
+}
+
+static int
+count_symbol(void *arg, const GElf_Sym *sym, const char *name)
+{
+ int *counts = arg;
+
+ if (strcmp(name, "common") == 0)
+ counts[0]++;
+ else if (strcmp(name, "private") == 0)
+ counts[1]++;
+ else if (strcmp(name, "dynamic") == 0)
+ counts[2]++;
+ else
+ CHECK(0);
+ return 0;
+}
+
+int
+main(void)
+{
+ dtrace_hdl_t *dtp;
+ int fd, err;
+ char path[] = "/tmp/dtrace-elf-symbols-XXXXXX";
+
+ CHECK(elf_version(EV_CURRENT) != EV_NONE);
+ CHECK((fd = mkstemp(path)) >= 0);
+ CHECK((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) != NULL);
+
+ for (int bits = ELFCLASS32; bits <= ELFCLASS64; bits++) {
+ for (int tables = 0; tables <= 3; tables++) {
+ for (int kernel = 0; kernel <= 1; kernel++) {
+ dt_module_t *dmp;
+ GElf_Sym sym;
+ dtrace_syminfo_t si;
+ char name[64];
+ int counts[3] = { 0 };
+
+ write_elf(fd, bits, tables);
+ snprintf(name, sizeof(name), "elf_%d_%d_%d", bits, tables, kernel);
+ CHECK((dmp = dt_module_create(dtp, name)) != NULL);
+ strcpy(dmp->dm_file, path);
+ if (kernel)
+ dmp->dm_flags |= DT_DM_KERNEL | DT_DM_KERN_UNLOADED;
+
+ if (tables == 0) {
+ CHECK(dtrace_lookup_by_name(dtp, name, "common", &sym, &si) == -1);
+ continue;
+ }
+ CHECK(dtrace_lookup_by_name(dtp, name, "common", &sym, &si) == 0);
+ CHECK(sym.st_value == (tables & 1 ? 0x1000 : 0x1100));
+ CHECK(strcmp(si.name, "common") == 0 && si.id == 1);
+
+ if (tables & 1) {
+ CHECK(dtrace_lookup_by_name(dtp, name, "private", &sym, &si) == 0);
+ CHECK(sym.st_value == 0x1200 && si.id == 2);
+ }
+ if (tables & 2) {
+ CHECK(dtrace_lookup_by_name(dtp, name, "dynamic", &sym, &si) == 0);
+ CHECK(sym.st_value == 0x1300);
+ CHECK(strcmp(si.name, "dynamic") == 0);
+ CHECK(si.id == (tables & 1 ? 5 : 2));
+ }
+ CHECK(dt_module_sym_iter(dmp, count_symbol, counts) == 0);
+ CHECK(counts[0] == 1);
+ CHECK(counts[1] == !!(tables & 1));
+ CHECK(counts[2] == !!(tables & 2));
+
+ /* Give only this fixture a range for public address lookup. */
+ CHECK((dmp->dm_text_addrs = calloc(1,
+ sizeof(dtrace_addr_range_t))) != NULL);
+ dmp->dm_text_addrs[0].dar_va = 0x1000;
+ dmp->dm_text_addrs[0].dar_size = 0x400;
+ dmp->dm_text_addrs_size = 1;
+ dtp->dt_modranges_valid = 0;
+
+ for (int t = 0; t < 2; t++) {
+ GElf_Addr addr = t == 0 ? 0x1204 : 0x1304;
+ int found = dtrace_lookup_by_addr(dtp, addr, &sym, &si);
+
+ CHECK((found == 0) == !!(tables & (1 << t)));
+ if (found == 0) {
+ CHECK(sym.st_value == addr - 4);
+ CHECK(strcmp(si.name, t == 0 ? "private" : "dynamic") == 0);
+ CHECK(si.id == (t == 1 && (tables & 1) ? 5 : 2));
+ }
+ }
+ CHECK(dtrace_lookup_by_addr(dtp, 0x13ff, &sym, &si) == -1);
+ dmp->dm_text_addrs_size = 0;
+ dtp->dt_modranges_valid = 0;
+ }
+ }
+ }
+ dtrace_close(dtp);
+
+ /* An unloaded module still needs ELF symbols when its CTF is cached. */
+ CHECK((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) != NULL);
+ {
+ dt_module_t *shared = dt_module_create(dtp, "shared_ctf");
+ dt_module_t *dmp = dt_module_create(dtp, "elf_cached_ctf");
+ GElf_Sym sym;
+
+ CHECK(shared != NULL && dmp != NULL);
+ CHECK((shared->dm_ctfp = ctf_create(&err)) != NULL);
+ CHECK((dmp->dm_ctfp = ctf_create(&err)) != NULL);
+ dmp->dm_flags |= DT_DM_KERNEL | DT_DM_KERN_UNLOADED;
+ dmp->dm_ops = NULL;
+ strcpy(dmp->dm_file, path);
+ write_elf(fd, ELFCLASS64, 3);
+ CHECK(dtrace_lookup_by_name(dtp, dmp->dm_name, "common",
+ &sym, NULL) == 0);
+ CHECK(sym.st_value == 0x1000);
+ }
+ dtrace_close(dtp);
+ close(fd);
+ unlink(path);
+ return 0;
+}
diff --git a/test/triggers/shared-ctf.c b/test/triggers/shared-ctf.c
index 74c55c44..5772429a 100644
--- a/test/triggers/shared-ctf.c
+++ b/test/triggers/shared-ctf.c
@@ -229,6 +229,12 @@ check_source(const char *ctfpath, const char *btfpath, int 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 {
+ GElf_Sym sym;
+
+ /* Inherited BTF must not make an absent ELF module loadable. */
+ CHECK(dtrace_lookup_by_name(dtp, "shared_ctf_test_absent",
+ "missing_symbol", &sym, NULL) == -1);
+
/* 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;
--
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=9a3ef30ca35af2d994b6f8a42a8e6700@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox