[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