Skip to content

Commit c0fa55f

Browse files
authored
Merge pull request #92 from marcosps/templ-multifile
TemplateGen enhancements
2 parents 2d1a4a2 + 0375060 commit c0fa55f

File tree

3 files changed

+143
-93
lines changed

3 files changed

+143
-93
lines changed

klpbuild/klplib/templ.py

+121-91
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,27 @@
1111

1212
from klpbuild.klplib.bugzilla import get_bug_title
1313
from klpbuild.klplib.codestreams_data import get_codestreams_data
14-
from klpbuild.klplib.utils import ARCHS, fix_mod_string, get_mail, get_workdir, get_lp_number
14+
from klpbuild.klplib.utils import ARCHS, fix_mod_string, get_mail, get_workdir, get_lp_number, get_fname
15+
16+
17+
MACRO_PROTO_SYMS = """\
18+
<%
19+
def get_protos(proto_syms):
20+
proto_list = []
21+
22+
if not proto_syms:
23+
return ''
24+
25+
for fname, data in proto_syms.items():
26+
proto_list.append(f"int {fname}_init(void);")
27+
if data["cleanup"]:
28+
proto_list.append(f"void {fname}_cleanup(void);\\n")
29+
else:
30+
proto_list.append(f"static inline void {fname}_cleanup(void);\\n")
31+
32+
return '\\n' + '\\n'.join(proto_list)
33+
%>\
34+
"""
1535

1636

1737
TEMPL_NO_SYMS_H = """\
@@ -38,7 +58,7 @@
3858
% else:
3959
static inline void ${ fname }_cleanup(void) {}
4060
% endif %
41-
61+
${get_protos(proto_syms)}
4262
#else /* !IS_ENABLED(${ config }) */
4363
4464
static inline int ${ fname }_init(void) { return 0; }
@@ -53,7 +73,7 @@
5373
% else:
5474
static inline void ${ fname }_cleanup(void) {}
5575
% endif
56-
76+
${get_protos(proto_syms)}
5777
% endif
5878
#endif /* _${ fname.upper() }_H */
5979
"""
@@ -432,107 +452,109 @@ def __generate_patched_conf(self, cs):
432452

433453
def __generate_header_file(self, lp_path, cs):
434454
out_name = f"livepatch_{self.lp_name}.h"
435-
436-
lp_inc_dir = Path()
437-
configs = set()
438-
config = ""
439-
mod = ""
440-
has_ext = False
441-
has_cleanup = False
442-
443-
for f, data in cs.files.items():
444-
configs.add(data["conf"])
445-
# If we have external symbols we need an init function to load them. If the module
446-
# isn't vmlinux then we also need an _exit function
447-
if data["ext_symbols"]:
448-
has_ext = True
449-
450-
if data["module"] != "vmlinux":
451-
has_cleanup = True
452-
453-
# Only populate the config check in the header if the livepatch is
454-
# patching code under only one config. Otherwise let the developer to
455-
# fill it.
456-
if len(configs) == 1:
457-
config = configs.pop()
458-
459455
render_vars = {
460-
"fname": str(Path(out_name).with_suffix("")).replace("-", "_"),
461-
"check_enabled": self.check_enabled,
462-
"config": config,
463-
"has_cleanup": has_cleanup,
456+
"fname": get_fname(out_name),
464457
}
465458

466-
header_templ = TEMPL_H
467-
# If we don't have any external symbols then we don't need the empty _init/_exit functions
468-
if cs.needs_ibt or not has_ext:
469-
header_templ = TEMPL_NO_SYMS_H
459+
# We don't need any setups on IBT besides the livepatch_init/cleanup ones
460+
header_templ = TEMPL_NO_SYMS_H
461+
462+
if not cs.needs_ibt:
463+
configs = set()
464+
config = ""
465+
has_cleanup = False
466+
proto_syms = {}
467+
468+
for src_file, data in cs.files.items():
469+
configs.add(data["conf"])
470+
# If we have external symbols we need an init function to load them. If the module
471+
# isn't vmlinux then we also need an _exit function
472+
if data["ext_symbols"]:
473+
if data["module"] != "vmlinux":
474+
# Used by the livepatch_cleanup
475+
has_cleanup = True
476+
477+
proto_fname = get_fname(cs.lp_out_file(self.lp_name, src_file))
478+
proto_syms[proto_fname] = {"cleanup": data["module"] != "vmlinux"}
479+
480+
# If we don't have any external symbols then we don't need the empty _init/_exit functions
481+
if proto_syms.keys():
482+
# Only populate the config check in the header if the livepatch is
483+
# patching code under only one config. Otherwise let the developer to
484+
# fill it.
485+
if len(configs) == 1:
486+
config = configs.pop()
487+
488+
# We there was only one entry in the proto_syms means that we have only one file in
489+
# in this livepatch, so we are already covered
490+
# Situations where we don't need any extra symbol prototypes:
491+
# * we don't have any externalized symbols
492+
# * the livepatch has only one file (_init/_cleanup for livepatch_ are created by default)
493+
if len(proto_syms.keys()) == 1 and len(cs.files.keys()) == 1:
494+
proto_syms = {}
495+
496+
render_vars.update({
497+
"check_enabled": self.check_enabled,
498+
"config": config,
499+
"has_cleanup": has_cleanup,
500+
"proto_syms": proto_syms,
501+
})
502+
503+
header_templ = MACRO_PROTO_SYMS + TEMPL_H
470504

471505
with open(Path(lp_path, out_name), "w") as f:
472-
lpdir = TemplateLookup(directories=[lp_inc_dir], preprocessor=TemplateGen.preproc_slashes)
506+
lpdir = TemplateLookup(directories=[Path()], preprocessor=TemplateGen.preproc_slashes)
473507
f.write(Template(header_templ, lookup=lpdir).render(**render_vars))
474508

475-
def __generate_lp_file(self, lp_path, cs, src_file, use_src_name=False):
476-
if src_file:
477-
lp_inc_dir = str(cs.get_ccp_work_dir(self.lp_name, src_file))
478-
lp_file = cs.lp_out_file(self.lp_name, src_file)
479-
fdata = cs.files[str(src_file)]
480-
else:
481-
lp_inc_dir = Path("non-existent")
482-
lp_file = None
483-
fdata = {}
484-
485-
# if use_src_name is True, the final file will be:
486-
# bscXXXXXXX_{src_name}.c
487-
# else:
488-
# livepatch_bscXXXXXXXX.c
489-
if use_src_name:
490-
out_name = lp_file
491-
else:
492-
out_name = f"livepatch_{self.lp_name}.c"
493-
494-
user, email = get_mail()
509+
def __generate_lp_file(self, lp_path, cs, src_file, out_name):
495510
cve = get_codestreams_data('cve')
496511
if not cve:
497512
cve = "XXXX-XXXX"
498-
cmts = get_codestreams_data('commits')
513+
user, email = get_mail()
499514
tvars = {
500-
"include_header": "livepatch_" in out_name,
515+
"check_enabled": self.check_enabled,
516+
"commits": get_codestreams_data('commits'),
517+
"config": "CONFIG_CHANGE_ME",
501518
"cve": cve,
519+
"email": email,
520+
"fname": get_fname(out_name),
521+
"include_header": "livepatch_" in out_name,
502522
"lp_name": self.lp_name,
503523
"lp_num": get_lp_number(self.lp_name),
504-
"fname": str(Path(out_name).with_suffix("")).replace("-", "_"),
505-
"year": datetime.today().year,
506524
"user": user,
507-
"email": email,
508-
"config": fdata.get("conf", ""),
509-
"mod": fix_mod_string(fdata.get("module", "")),
510-
"mod_mutex": cs.is_mod_mutex(),
511-
"check_enabled": self.check_enabled,
512-
"ext_vars": fdata.get("ext_symbols", ""),
513-
"inc_src_file": lp_file,
514-
"ibt": fdata.get("ibt", False),
515-
"commits": cmts
525+
"year": datetime.today().year,
516526
}
517527

518-
with open(Path(lp_path, out_name), "w") as f:
519-
lpdir = TemplateLookup(directories=[lp_inc_dir], preprocessor=TemplateGen.preproc_slashes)
520-
521-
# If we have multiple source files for the same livepatch,
522-
# create one hollow file to wire-up the multiple _init and
523-
# _clean functions
524-
#
525-
# If we are patching a module, we should have the
526-
# module_notifier armed to signal whenever the module comes on
527-
# in order to do the symbol lookups. Otherwise only _init is
528-
# needed, and only if there are externalized symbols being used.
529-
if not lp_file:
530-
temp_str = TEMPL_HOLLOW
531-
elif tvars["mod"]:
528+
# If we have multiple source files for the same livepatch,
529+
# create one hollow file to wire-up the multiple _init and
530+
# _clean functions
531+
#
532+
# If we are patching a module, we should have the
533+
# module_notifier armed to signal whenever the module comes on
534+
# in order to do the symbol lookups. Otherwise only _init is
535+
# needed, and only if there are externalized symbols being used.
536+
if not src_file:
537+
temp_str = TEMPL_HOLLOW
538+
lp_inc_dir = Path("non-existent")
539+
else:
540+
fdata = cs.files[str(src_file)]
541+
tvars.update({
542+
"config": fdata.get("conf", ""),
543+
"ext_vars": fdata.get("ext_symbols", ""),
544+
"ibt": fdata.get("ibt", False),
545+
"inc_src_file": cs.lp_out_file(self.lp_name, src_file),
546+
"mod": fix_mod_string(fdata.get("module", "")),
547+
"mod_mutex": cs.is_mod_mutex(),
548+
})
549+
550+
if tvars["mod"]:
532551
temp_str = TEMPL_GET_EXTS + TEMPL_PATCH_MODULE
533552
else:
534553
temp_str = TEMPL_GET_EXTS + TEMPL_PATCH_VMLINUX
554+
lp_inc_dir = cs.get_ccp_work_dir(self.lp_name, src_file)
535555

556+
lpdir = TemplateLookup(directories=[lp_inc_dir], preprocessor=TemplateGen.preproc_slashes)
557+
with open(Path(lp_path, out_name), "w") as f:
536558
f.write(Template(TEMPL_SUSE_HEADER + temp_str, lookup=lpdir).render(**tvars))
537559

538560
def generate_livepatches(self, cs):
@@ -549,22 +571,30 @@ def generate_livepatches(self, cs):
549571
# config entries empty
550572
self.__generate_header_file(lp_path, cs)
551573

552-
# Run the template engine for each touched source file.
574+
# Run the template engine for each generated source file.
553575
for src_file, _ in files.items():
554-
self.__generate_lp_file(lp_path, cs, src_file, is_multi_files)
576+
# if use_src_name is True, the final file will be:
577+
# bscXXXXXXX_{src_name}.c
578+
# else:
579+
# livepatch_bscXXXXXXXX.c
580+
out_name = f"livepatch_{self.lp_name}.c" if not is_multi_files else \
581+
cs.lp_out_file(self.lp_name, src_file)
582+
583+
self.__generate_lp_file(lp_path, cs, src_file, out_name)
555584

556585
# One additional file to encapsulate the _init and _clenaup methods
557586
# of the other source files
558587
if is_multi_files:
559-
self.__generate_lp_file(lp_path, cs, None, False)
588+
self.__generate_lp_file(lp_path, cs, None, f"livepatch_{self.lp_name}.c")
589+
590+
create_kbuild(self.lp_name, cs)
560591

561-
self.__create_kbuild(cs)
562592

593+
def create_kbuild(lp_name, cs):
563594
# Create Kbuild.inc file adding an entry for all generated livepatch files.
564-
def __create_kbuild(self, cs):
565-
render_vars = {"bsc": self.lp_name, "cs": cs, "lpdir": cs.get_lp_dir(self.lp_name)}
566-
with open(Path(cs.get_lp_dir(self.lp_name), "Kbuild.inc"), "w") as f:
567-
f.write(Template(TEMPL_KBUILD).render(**render_vars))
595+
render_vars = {"bsc": lp_name, "cs": cs, "lpdir": cs.get_lp_dir(lp_name)}
596+
with open(Path(cs.get_lp_dir(lp_name), "Kbuild.inc"), "w") as f:
597+
f.write(Template(TEMPL_KBUILD).render(**render_vars))
568598

569599

570600
def generate_commit_msg_file(lp_name):

klpbuild/klplib/utils.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@
44
# Author: Marcos Paulo de Souza <[email protected]>
55

66
import copy
7-
import git
87
import gzip
98
import io
109
import logging
1110
import lzma
1211
import os
1312
import platform
1413
import re
14+
import git
1515
import zstandard
1616

1717
from elftools.common.utils import bytes2str
1818
from elftools.elf.elffile import ELFFile
1919
from elftools.elf.sections import SymbolTableSection
20-
from pathlib import PurePath
20+
from pathlib import Path, PurePath
2121

2222
from natsort import natsorted
2323

@@ -261,6 +261,17 @@ def fix_mod_string(mod):
261261
return mod.replace("-", "_")
262262

263263

264+
def get_fname(src_name):
265+
"""
266+
Get the source name and transforms into a string version of it, without the extension.
267+
268+
Returns:
269+
String: The same src_name string without externsion and hyphens replaced by underscores.
270+
"""
271+
272+
return str(Path(src_name).with_suffix("")).replace("-", "_")
273+
274+
264275
def get_kgraft_branch(cs_name):
265276
if "6.0" in cs_name:
266277
branch = "MICRO-6-0"

tests/test_templ.py

+9
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ def test_check_header_file_included():
119119
assert "Upstream commit:" not in get_file_content(lp, cs, f"{lp}_kernel_events_core.c")
120120
assert "Upstream commit:" not in get_file_content(lp, cs, f"{lp}_net_ipv6_rpl.c")
121121

122+
# Check that for file kernel/events/core.c there are externalized symbols, so the prototype
123+
# of init/cleanup are created on header
124+
# As net/ipv6/rpl.c there are no externalized symbols we expect that it's prototype isn't
125+
# created on livepatch_header file
126+
header = get_file_content(lp, cs, f"livepatch_{lp}.h")
127+
assert "kernel_events_core_init(void);" in header
128+
assert "kernel_events_core_cleanup(void);" in header
129+
assert "net_ipv6_rpl" not in header
130+
122131

123132
def test_templ_cve_specified():
124133
lp = "bsc_" + inspect.currentframe().f_code.co_name

0 commit comments

Comments
 (0)