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


Reply via email to