[PATCH RFC v2 13/13] modpost: handle MODULE_SYSCTL_TABLE symbols
Mauricio Faria de Oliveira
mfo at igalia.com
Tue Aug 18 19:28:12 PDT 2026
Update file2alias.c to handle 'mod_devicetable' symbols with type 'sysctl'.
The key operation is to iterate over relocation entries, so add the helper
for_each_reloc() with a function callback, and the functions to:
1) Find the sysctl path and table in the 'sysctl' module symbol.
2) Find the sysctl table's entries and their '.procname' field,
adding a module alias 'sysctl:*/<path>/<procname>' for each.
Originally-by: Mauricio Faria de Oliveira <mfo at canonical.com>
Signed-off-by: Mauricio Faria de Oliveira <mfo at igalia.com>
---
scripts/mod/file2alias.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++
scripts/mod/modpost.c | 11 ++-
scripts/mod/modpost.h | 23 +++++
3 files changed, 262 insertions(+), 2 deletions(-)
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 8d36c74dec2d55840de60b3f8ec22b646d16ff8b..797cfc2f478e25dd2ba5f9a5a063a2919fe58cc5 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -1531,6 +1531,231 @@ static const struct devtable devtable[] = {
{"pnp_card", SIZE_pnp_card_device_id, do_pnp_card_entry},
};
+// Looks like: sysctl:*/path/procname
+static void do_sysctl_entry(const char *procname, const char *path,
+ struct module *mod)
+{
+ const char *src;
+ char *dst, buf[256], *end = buf + sizeof(buf) - 1; /* -1 for NUL byte */
+
+ /* Replace '%s' from path template with '*' for wildcard in modprobe. */
+ for (src = path, dst = buf; *src && dst < end; src++, dst++)
+ *dst = (*src == '%') ? (src++, '*') : *src;
+ *dst = '\0';
+
+ module_alias_printf(mod, false, "sysctl:*/%s/%s", buf, procname);
+}
+
+/*
+ * Execute a callback function for each relocation entry in relocation section.
+ * The caller must ensure sechdr->sh_type is SHT_RELA or SHT_REL.
+ */
+static void for_each_reloc(struct elf_info *elf, unsigned int shndx,
+ bool (*fn)(struct elf_info *elf, Elf_Shdr *sechdr,
+ Elf_Sym *sym, Elf_Addr r_offset,
+ Elf_Addr r_addend, void *data),
+ void *data)
+{
+ Elf_Shdr *sechdr = &elf->sechdrs[shndx];
+ const Elf_Rela *rela; /* used as Elf_Rel[a] on SHT_REL[A] */
+ const Elf_Rela *start = (void *) elf->hdr + sechdr->sh_offset;
+ const Elf_Rela *stop = (void *) start + sechdr->sh_size;
+ size_t size = (sechdr->sh_type == SHT_RELA) ? sizeof(Elf_Rela)
+ : sizeof(Elf_Rel);
+
+ for (rela = start; rela < stop; rela = (Elf_Rela *)((void *) rela + size)) {
+ Elf_Sym *sym;
+ Elf_Addr r_offset, r_addend;
+ unsigned int r_type, r_sym;
+
+ r_offset = TO_NATIVE(rela->r_offset);
+ get_rel_type_and_sym(elf, rela->r_info, &r_type, &r_sym);
+
+ sym = elf->symtab_start + r_sym;
+ r_addend = (sechdr->sh_type == SHT_RELA)
+ ? TO_NATIVE(rela->r_addend)
+ : addend_rel(elf, sechdr->sh_info,
+ r_type, r_offset, sym);
+
+ if (fn(elf, sechdr, sym, r_offset, r_addend, data))
+ break;
+ }
+}
+
+/*
+ * Callback parameters and function to loop over the sysctl entries in struct
+ * module_sysctl_table's .table symbol (found below) in its reloction section.
+ */
+struct sysctl_entries {
+ Elf_Addr table_offset;
+ ssize_t table_size;
+ ssize_t entry_size;
+ const char *path;
+ const char *modsymname;
+ struct module *mod;
+};
+
+static bool do_sysctl_entries(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Sym *sym,
+ Elf_Addr r_offset, Elf_Addr r_addend, void *data)
+{
+ struct sysctl_entries *sysctl_entries = (struct sysctl_entries *) data;
+
+ /* Skip until .table starts */
+ if (r_offset < sysctl_entries->table_offset)
+ return false;
+
+ /* Stop after .table ends */
+ if (r_offset >= sysctl_entries->table_offset + sysctl_entries->table_size)
+ return true;
+
+ /* Check for alignment with an array entry (.procname at offset zero) */
+ if ((r_offset - sysctl_entries->table_offset) % sysctl_entries->entry_size == 0) {
+
+ /* The symbol for .procname points to a string */
+ const char *procname = (const char *)
+ sym_get_data_addend(elf, sym, r_addend);
+
+ if (!procname) {
+ warn("%s [%s] found entry with NULL .procname (skip)\n",
+ sysctl_entries->modsymname, sysctl_entries->mod->name);
+ return false;
+ }
+
+ do_sysctl_entry(procname, sysctl_entries->path, sysctl_entries->mod);
+ }
+
+ /* Continue at next entry */
+ return false;
+}
+
+/*
+ * Callback parameters and function to search for struct module_sysctl_table's
+ * pointers (.path and .table) in relocation entries of a relocation section.
+ */
+struct sysctl_pointers {
+ /* Input: offsets */
+ Elf_Addr path_offset;
+ Elf_Addr table_offset;
+
+ /* Output: symbols and relocation addends */
+ Elf_Sym *path_sym;
+ Elf_Sym *table_sym;
+ Elf_Addr path_r_addend;
+ Elf_Addr table_r_addend;
+};
+
+static bool do_sysctl_pointers(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Sym *sym,
+ Elf_Addr r_offset, Elf_Addr r_addend, void *data)
+{
+ struct sysctl_pointers *sysctl_pointers = (struct sysctl_pointers *) data;
+
+ /* Check for relocation entry's offset matching .path or .table */
+ if (!sysctl_pointers->path_sym &&
+ sysctl_pointers->path_offset == r_offset) {
+ sysctl_pointers->path_sym = sym;
+ sysctl_pointers->path_r_addend = r_addend;
+ } else if (!sysctl_pointers->table_sym &&
+ sysctl_pointers->table_offset == r_offset) {
+ sysctl_pointers->table_sym = sym;
+ sysctl_pointers->table_r_addend = r_addend;
+ }
+
+ /* Stop once both .path and .table are found */
+ return (sysctl_pointers->path_sym && sysctl_pointers->table_sym);
+}
+
+static void do_sysctl_table(const char *modsymname, void *modsymval,
+ Elf_Sym *modsym, struct module *mod,
+ struct elf_info *info)
+{
+ /*
+ * The struct module_sysctl_table symbol contains 4 fields:
+ * .path: pointer to string with the dirname in /proc/sys
+ * .table: pointer to struct ctl_table array with filenames (.procname)
+ * .table_size: size of struct ctl_table array
+ * .entry_size: size of struct ctl_table entry in the array
+ */
+
+ /* The size values can be read directly. */
+ DEF_FIELD(modsymval, module_sysctl_table, table_size);
+ DEF_FIELD(modsymval, module_sysctl_table, entry_size);
+
+ struct sysctl_entries sysctl_entries = {
+ .table_size = table_size,
+ .entry_size = entry_size,
+ .modsymname = modsymname,
+ .mod = mod,
+ };
+
+ /*
+ * Step 1:
+ *
+ * Each pointer has a relocation entry in a relocation section,
+ * that links the pointer with the symbol it points to.
+ *
+ * In order to access the symbols pointed to by .path and .table pointers:
+ * 1) find the relocation section of the struct module_sysctl_table symbol;
+ * 2) find the relocation entries for these pointers by their field offset;
+ * 3) then use the symbols found in these relocation entries.
+ */
+
+ struct sysctl_pointers sysctl_pointers = {
+ .path_offset = modsym->st_value + OFF_module_sysctl_table_path,
+ .path_sym = NULL,
+ .table_offset = modsym->st_value + OFF_module_sysctl_table_table,
+ .table_sym = NULL,
+ };
+
+ unsigned int shndx;
+
+ /* Find the relocation section for struct module_sysctl_table symbol. */
+ shndx = get_reloc_secindex(info, modsym);
+ if (shndx == SHN_UNDEF) {
+ error("%s [%s.ko] cannot find relocation section for symbol\n",
+ modsymname, mod->name);
+ return;
+ }
+
+ /* Find the relocation entries for the .path and .table pointers. */
+ for_each_reloc(info, shndx, do_sysctl_pointers, &sysctl_pointers);
+ if (!sysctl_pointers.path_sym || !sysctl_pointers.table_sym) {
+ error("%s [%s.ko] cannot find relocation entry for path/table\n",
+ modsymname, mod->name);
+ return;
+ }
+
+ /*
+ * Step 2:
+ *
+ * The .table symbol is the struct ctl_table array where each entry has
+ * a .procname pointer with a relocation entry for the filename string.
+ *
+ * In order to access the strings pointed to by .procname pointers:
+ * 1) Find the relocation section of the struct ctl_table array symbol;
+ * 2) Find the relocation entries for these pointers by their field offset;
+ * 3) Then use the symbols found in these relocation entries.
+ */
+
+ /* Find the relocation section for the struct ctl_table array. */
+ shndx = get_reloc_secindex(info, sysctl_pointers.table_sym);
+ if (shndx == SHN_UNDEF) {
+ /* Edge case: empty .table: no relocation section. */
+ return;
+ }
+
+ /* The .path symbol can be read directly. */
+ sysctl_entries.path = (const char *)
+ sym_get_data_addend(info, sysctl_pointers.path_sym,
+ sysctl_pointers.path_r_addend);
+
+ /* The .table symbol is the struct ctl_table array. */
+ sysctl_entries.table_offset = sysctl_pointers.table_sym->st_value +
+ sysctl_pointers.table_r_addend;
+
+ /* Add module aliases for entries in the struct ctl_table array. */
+ for_each_reloc(info, shndx, do_sysctl_entries, &sysctl_entries);
+}
+
/* Create MODULE_ALIAS() statements.
* At this time, we cannot write the actual output C source yet,
* so we write into the mod->dev_table_buf buffer. */
@@ -1542,6 +1767,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
const char *type, *name, *modname;
size_t typelen, modnamelen;
static const char *prefix = "__mod_device_table__";
+ bool sym_is_devtable = false;
/* We're looking for a section relative symbol */
if (!sym->st_shndx || get_secindex(info, sym) >= info->num_sections)
@@ -1586,10 +1812,14 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
if (sym_is(type, typelen, p->device_id)) {
do_table(name, symval, sym->st_size, p->id_size,
p->device_id, p->do_entry, mod);
+ sym_is_devtable = true;
break;
}
}
+ if (!sym_is_devtable && sym_is(type, typelen, "sysctl"))
+ do_sysctl_table(name, symval, sym, mod, info);
+
if (mod->is_vmlinux) {
struct module_alias *alias;
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index d18a87b882a67f06d5f75ee92f4c26dc713d83b8..10eb7273cdd817da788fa3bce87d695661dd1b93 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -340,6 +340,13 @@ void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym)
sym->st_value);
}
+void *sym_get_data_addend(const struct elf_info *info, const Elf_Sym *sym,
+ Elf_Addr r_addend)
+{
+ return sym_get_data_by_offset(info, get_secindex(info, sym),
+ sym->st_value + r_addend);
+}
+
static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr)
{
return sym_get_data_by_offset(info, info->secindex_strings,
@@ -1338,8 +1345,8 @@ Elf_Addr addend_rel(struct elf_info *elf, unsigned int secndx,
#define R_LARCH_ALIGN 102
#endif
-static void get_rel_type_and_sym(struct elf_info *elf, uint64_t r_info,
- unsigned int *r_type, unsigned int *r_sym)
+void get_rel_type_and_sym(struct elf_info *elf, uint64_t r_info,
+ unsigned int *r_type, unsigned int *r_sym)
{
typedef struct {
Elf64_Word r_sym; /* Symbol index */
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
index a98811552bd2996c31cf73fd3619e6c58bac8a6d..409fdfc844c4bfcea30fd6ae758f4042384e3e56 100644
--- a/scripts/mod/modpost.h
+++ b/scripts/mod/modpost.h
@@ -186,6 +186,25 @@ static inline unsigned int get_secindex(const struct elf_info *info,
return index;
}
+/* Find the relocation section for the section of a symbol */
+static inline unsigned int get_reloc_secindex(const struct elf_info *info,
+ const Elf_Sym *sym)
+{
+ unsigned int sym_secindex = get_secindex(info, sym);
+ unsigned int secindex;
+
+ for (secindex = 0; secindex < info->num_sections; secindex++) {
+ Elf_Shdr *shdr = &info->sechdrs[secindex];
+
+ if ((shdr->sh_type == SHT_RELA || shdr->sh_type == SHT_REL) &&
+ (shdr->sh_flags & SHF_INFO_LINK) &&
+ shdr->sh_info == sym_secindex)
+ return secindex;
+ }
+
+ return SHN_UNDEF;
+}
+
/*
* If there's no name there, ignore it; likewise, ignore it if it's
* one of the magic symbols emitted used by current tools.
@@ -222,8 +241,12 @@ const char *get_basename(const char *path);
char *read_text_file(const char *filename);
char *get_line(char **stringp);
void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym);
+void *sym_get_data_addend(const struct elf_info *info, const Elf_Sym *sym,
+ Elf_Addr r_addend);
Elf_Addr addend_rel(struct elf_info *elf, unsigned int secndx,
unsigned int r_type, Elf_Addr r_offset, Elf_Sym *tsym);
+void get_rel_type_and_sym(struct elf_info *elf, uint64_t r_info,
+ unsigned int *r_type, unsigned int *r_sym);
void __attribute__((format(printf, 3, 4)))
modpost_log(bool is_error, struct module *mod, const char *fmt, ...);
--
2.47.3
More information about the linux-riscv
mailing list