EXPORT_SYMBOL_FOR_MODULES() puts a symbol in a "module:<names>"
namespace, which the module loader grants access to by matching the
importing module's name against that list.
klp_reloc_needed() only creates a klp reloc for module-owned exports; a
vmlinux export gets a normal reloc. For a vmlinux symbol exported with
EXPORT_SYMBOL_FOR_MODULES(), using a normal reloc results in a modpost
failure in klp-build:
ERROR: modpost: module livepatch-foo uses symbol mpol_shared_policy_lookup
from namespace module:kvm, but does not import it.
And the modpost error is correct: even with that error removed, the
patch module would fail to load:
livepatch_foo: module uses symbol (mpol_shared_policy_lookup) from namespace
module:kvm, but does not import it.
livepatch_foo: Unknown symbol mpol_shared_policy_lookup (err -22)
Treat it like an unexported symbol by using a klp reloc.
Note this only affects "module:" namespaces. Ordinary namespaced
exports continue to work with normal relocs thanks to copy_import_ns(),
which propagates the patched object's import_ns tags to the patch
module.
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing
object files")
Reported-by: Joe Lawrence <[email protected]>
Link: https://lore.kernel.org/[email protected]
Signed-off-by: Josh Poimboeuf <[email protected]>
---
tools/objtool/klp-diff.c | 28 +++++++++++++++++++++++-----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 6d34186d8b24c..0f135b74a5b0c 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -30,7 +30,9 @@ struct elfs {
struct export {
struct hlist_node hash;
- char *mod, *sym;
+ char *mod;
+ char *sym;
+ bool mod_ns;
};
bool debug, debug_correlate, debug_clone;
@@ -135,7 +137,7 @@ static int read_exports(void)
}
while (fgets(line, 1024, file)) {
- char *sym, *mod, *type;
+ char *sym, *mod, *type, *namespace;
struct export *export;
sym = strchr(line, '\t');
@@ -162,6 +164,14 @@ static int read_exports(void)
*type++ = '\0';
+ namespace = strchr(type, '\t');
+ if (!namespace) {
+ ERROR("malformed Module.symvers (namespace) at line
%d", line_num);
+ return -1;
+ }
+
+ *namespace++ = '\0';
+
if (*sym == '\0' || *mod == '\0') {
ERROR("malformed Module.symvers at line %d", line_num);
return -1;
@@ -188,6 +198,9 @@ static int read_exports(void)
return -1;
}
+ /* EXPORT_SYMBOL_FOR_MODULES() */
+ export->mod_ns = strstarts(namespace, "module:");
+
hash_add(exports, &export->hash, str_hash(sym));
}
@@ -1174,11 +1187,16 @@ static bool klp_reloc_needed(struct reloc
*patched_reloc)
* clusterfunk that is late module patching, the patch module is
* allowed to be loaded before any modules it depends on.
*
- * If exported by vmlinux, a normal reloc will do.
+ * If exported by vmlinux to all modules, a normal reloc will do.
*/
export = find_export(patched_sym);
- if (export)
- return strcmp(export->mod, "vmlinux");
+ if (export) {
+ if (strcmp(export->mod, "vmlinux"))
+ return true;
+
+ /* EXPORT_SYMBOL_FOR_MODULES() gets a klp reloc */
+ return export->mod_ns;
+ }
if (!patched_sym->twin) {
/*
--
2.54.0