@@ -70,6 +70,7 @@ modules.order
/Module.markers
/modules.builtin
/modules.builtin.modinfo
+/modules.livepatch
/modules.nsdeps
#
@@ -1095,6 +1095,7 @@ PHONY += prepare0
export extmod_prefix = $(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD)/)
export MODORDER := $(extmod_prefix)modules.order
export MODULES_NSDEPS := $(extmod_prefix)modules.nsdeps
+export MODULES_LIVEPATCH := $(extmod_prefix)modules.livepatch
ifeq ($(KBUILD_EXTMOD),)
@@ -1453,8 +1454,8 @@ endif
#
# *.ko are usually independent of vmlinux, but CONFIG_DEBUG_INFO_BTF_MODULES
-# is an exception.
-ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+# and CONFIG_LIVEPATCH are exceptions.
+ifneq ($(or $(CONFIG_DEBUG_INFO_BTF_MODULES),$(CONFIG_LIVEPATCH)),)
KBUILD_BUILTIN := 1
modules: vmlinux
endif
@@ -1477,8 +1478,9 @@ endif # CONFIG_MODULES
# Directories & files removed with 'make clean'
CLEAN_FILES += vmlinux.symvers modules-only.symvers \
modules.builtin modules.builtin.modinfo modules.nsdeps \
- compile_commands.json .thinlto-cache rust/test \
- rust-project.json .vmlinux.objs .vmlinux.export.c
+ modules.livepatch compile_commands.json .thinlto-cache \
+ rust/test rust-project.json .vmlinux.objs \
+ .vmlinux.export.c
# Directories & files removed with 'make mrproper'
MRPROPER_FILES += include/config include/generated \
@@ -14,6 +14,7 @@ include $(srctree)/scripts/Makefile.lib
# find all modules listed in modules.order
modules := $(call read-file, $(MODORDER))
+modules-klp := $(call read-file, $(MODULES_LIVEPATCH))
__modfinal: $(modules:%.o=%.ko)
@:
@@ -60,6 +61,20 @@ endif
targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o)
+# Livepatch
+# ---------------------------------------------------------------------------
+
+%.tmp.ko: %.o %.mod.o FORCE
+ +$(call if_changed,ld_ko_o)
+
+quiet_cmd_klp_convert = KLP $@
+ cmd_klp_convert = scripts/livepatch/klp-convert $< $@
+
+$(modules-klp:%.o=%.ko): %.ko: %.tmp.ko FORCE
+ $(call if_changed,klp_convert)
+
+targets += $(modules-klp:.ko=.tmp.ko)
+
# Add FORCE to the prequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
@@ -48,6 +48,7 @@ modpost-args = \
$(if $(KBUILD_MODPOST_WARN),-w) \
$(if $(KBUILD_NSDEPS),-d $(MODULES_NSDEPS)) \
$(if $(CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS)$(KBUILD_NSDEPS),-N) \
+ $(if $(CONFIG_LIVEPATCH),-l $(MODULES_LIVEPATCH)) \
$(if $(findstring 1, $(KBUILD_EXTRA_WARN)),-W) \
-o $@
@@ -145,6 +146,10 @@ $(output-symdump): $(modpost-deps) FORCE
$(call if_changed,modpost)
__modpost: $(output-symdump)
+ifndef CONFIG_LIVEPATCH
+ $(Q)rm -f $(MODULES_LIVEPATCH)
+ $(Q)touch $(MODULES_LIVEPATCH)
+endif
PHONY += FORCE
FORCE:
@@ -1654,6 +1654,10 @@ static void read_symbols(const char *modname)
}
}
+ /* Livepatch modules have unresolved symbols resolved by klp-convert */
+ if (get_modinfo(&info, "livepatch"))
+ mod->is_livepatch = true;
+
if (extra_warn && !get_modinfo(&info, "description"))
warn("missing MODULE_DESCRIPTION() in %s\n", modname);
for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
@@ -1742,10 +1746,18 @@ static void check_exports(struct module *mod)
const char *basename;
exp = find_symbol(s->name);
if (!exp) {
- if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS)
+ if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS) {
+ /*
+ * In case of livepatch module we allow
+ * unresolved symbol with a specific format
+ */
+ if (mod->is_livepatch &&
+ strncmp(s->name, KLP_SYM_RELA, strlen(KLP_SYM_RELA)) == 0)
+ break;
modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR,
"\"%s\" [%s.ko] undefined!\n",
s->name, mod->name);
+ }
continue;
}
if (exp->module == mod) {
@@ -2178,6 +2190,20 @@ static void write_namespace_deps_files(const char *fname)
free(ns_deps_buf.p);
}
+static void write_livepatch_modules(const char *fname)
+{
+ struct buffer buf = { };
+ struct module *mod;
+
+ list_for_each_entry(mod, &modules, list) {
+ if (mod->is_livepatch)
+ buf_printf(&buf, "%s.o\n", mod->name);
+ }
+
+ write_if_changed(&buf, fname);
+ free(buf.p);
+}
+
struct dump_list {
struct list_head list;
const char *file;
@@ -2189,11 +2215,12 @@ int main(int argc, char **argv)
char *missing_namespace_deps = NULL;
char *unused_exports_white_list = NULL;
char *dump_write = NULL, *files_source = NULL;
+ char *livepatch_modules = NULL;
int opt;
LIST_HEAD(dump_lists);
struct dump_list *dl, *dl2;
- while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:")) != -1) {
+ while ((opt = getopt(argc, argv, "ei:l:MmnT:to:au:WwENd:")) != -1) {
switch (opt) {
case 'e':
external_module = true;
@@ -2206,6 +2233,9 @@ int main(int argc, char **argv)
case 'M':
module_enabled = true;
break;
+ case 'l':
+ livepatch_modules = optarg;
+ break;
case 'm':
modversions = true;
break;
@@ -2285,6 +2315,8 @@ int main(int argc, char **argv)
if (dump_write)
write_dump(dump_write);
+ if (livepatch_modules)
+ write_livepatch_modules(livepatch_modules);
if (sec_mismatch_count && !sec_mismatch_warn_only)
error("Section mismatches detected.\n"
"Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n");
@@ -76,6 +76,8 @@
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#define KLP_SYM_RELA ".klp.sym.rela."
+
void *do_nofail(void *ptr, const char *expr);
struct buffer {
@@ -97,6 +99,7 @@ struct module {
bool is_gpl_compatible;
bool from_dump; /* true if module was loaded from *.symvers */
bool is_vmlinux;
+ bool is_livepatch;
bool seen;
bool has_init;
bool has_cleanup;