612 lines
16 KiB
Diff
612 lines
16 KiB
Diff
From 57f1017a9c971d8c3a5ef82d04e6c4bc584e9f00 Mon Sep 17 00:00:00 2001
|
|
From: "fu.lin" <fulin10@huawei.com>
|
|
Date: Fri, 8 Apr 2022 16:14:40 +0800
|
|
Subject: [PATCH 72/72] kabichk: add KABI check code
|
|
|
|
Theory:
|
|
* The export symbol CRCs source:
|
|
- /boot/symvers-$(uname -r).gz for Image and in tree modules: the
|
|
ima mechanism could ensure the file credibility and non-tamper.
|
|
- ELF section `.symtab` for out of tree modules: the export symbols
|
|
has `__crc_` prefix, and `st_shndx` is `SHN_ABS`
|
|
* compare CRC value between the known and the module
|
|
|
|
Design Details:
|
|
- collect export symbols from
|
|
* collect in tree symbols from `/boot/symvers-<release>.gz`
|
|
* collect out of tree module symbols from the module self
|
|
- compare external symbols stored in `__versions` section for each module
|
|
|
|
Usage:
|
|
python3 -m upgchk.kabichk \
|
|
[[-r <kernel release>],...] \
|
|
[[-m <modname>],...] \
|
|
-c <modname>
|
|
Example:
|
|
python3 -m upgchk.kabichk -c /lib/modules/$(uname -r)/kernel/fs/mbcache.ko
|
|
python3 -m upgchk.kabichk -m notify.ko -c osp_proc.ko
|
|
|
|
Note:
|
|
The pyelftools library can't be import, therefore using elfutils
|
|
wrapper to replace the library.
|
|
|
|
Signed-off-by: fu.lin <fulin10@huawei.com>
|
|
---
|
|
upgchk/Makefile | 23 ++++
|
|
upgchk/lib/modsym.c | 268 ++++++++++++++++++++++++++++++++++++++
|
|
upgchk/lib/modsym.h | 39 ++++++
|
|
upgchk/setup.py | 20 +++
|
|
upgchk/upgchk/__init__.py | 11 ++
|
|
upgchk/upgchk/kabichk.py | 163 +++++++++++++++++++++++
|
|
6 files changed, 524 insertions(+)
|
|
create mode 100644 upgchk/Makefile
|
|
create mode 100644 upgchk/lib/modsym.c
|
|
create mode 100644 upgchk/lib/modsym.h
|
|
create mode 100644 upgchk/setup.py
|
|
create mode 100644 upgchk/upgchk/__init__.py
|
|
create mode 100644 upgchk/upgchk/kabichk.py
|
|
|
|
diff --git a/upgchk/Makefile b/upgchk/Makefile
|
|
new file mode 100644
|
|
index 0000000..df6b60e
|
|
--- /dev/null
|
|
+++ b/upgchk/Makefile
|
|
@@ -0,0 +1,23 @@
|
|
+.PHONY: build install clean
|
|
+
|
|
+PYTHON=/usr/bin/python3
|
|
+TEST=
|
|
+PARAMETERS=
|
|
+
|
|
+build:
|
|
+ ${PYTHON} setup.py build
|
|
+
|
|
+dist:
|
|
+ ${PYTHON} setup.py sdist
|
|
+
|
|
+install:
|
|
+ ${PYTHON} setup.py install
|
|
+
|
|
+clean:
|
|
+ ${PYTHON} setup.py clean
|
|
+ rm -rf \
|
|
+ build \
|
|
+ dist \
|
|
+ upgchk/__pycache__ \
|
|
+ upgchk/*.so \
|
|
+ upgchk.egg-info
|
|
diff --git a/upgchk/lib/modsym.c b/upgchk/lib/modsym.c
|
|
new file mode 100644
|
|
index 0000000..eb75f68
|
|
--- /dev/null
|
|
+++ b/upgchk/lib/modsym.c
|
|
@@ -0,0 +1,268 @@
|
|
+#define PY_SSIZE_T_CLEAN
|
|
+#include <Python.h>
|
|
+
|
|
+#include <fcntl.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <gelf.h>
|
|
+
|
|
+#include "modsym.h"
|
|
+
|
|
+static Elf_Data *get_elf_sec_data(Elf *elf, const char *sec_name)
|
|
+{
|
|
+ Elf_Scn *scn = NULL;
|
|
+ size_t strndx;
|
|
+ GElf_Shdr mem;
|
|
+ GElf_Shdr *shdr;
|
|
+ const char *name;
|
|
+
|
|
+ /* To get the section names. */
|
|
+ if (elf_getshdrstrndx(elf, &strndx) != 0)
|
|
+ return NULL;
|
|
+
|
|
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
|
+ shdr = gelf_getshdr(scn, &mem);
|
|
+ name = elf_strptr (elf, strndx, shdr->sh_name);
|
|
+
|
|
+ if (strcmp(name, sec_name) == 0)
|
|
+ return elf_getdata(scn, NULL);
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void modvers_dealloc(PyObject *obj)
|
|
+{
|
|
+ ModVersState *mvgstate = (ModVersState *)obj;
|
|
+
|
|
+ elf_end(mvgstate->elf);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static PyObject *modvers_iternext(PyObject *obj)
|
|
+{
|
|
+ ModVersState *mvgstate = (ModVersState *)obj;
|
|
+ struct modversion_info *info = mvgstate->d->d_buf;
|
|
+ PyObject *elem = NULL;
|
|
+
|
|
+ if (mvgstate->seq_index >= 0) {
|
|
+ size_t i = mvgstate->enum_index;
|
|
+ /* seq_index < 0 means that the generator is exhausted.
|
|
+ * Returning NULL in this case is enough. The next() builtin
|
|
+ * will raise the StopIteration error for us.
|
|
+ */
|
|
+ elem = Py_BuildValue("(sk)", info[i].name, info[i].crc);
|
|
+ mvgstate->seq_index -= 1;
|
|
+ mvgstate->enum_index += 1;
|
|
+ } else {
|
|
+ /* The reference to the sequence is cleared in the first
|
|
+ * generator call after its exhaustion (after the call that
|
|
+ * returned the last element).
|
|
+ * Py_CLEAR will be harmless for subsequent calls since it's
|
|
+ * idempotent on NULL.
|
|
+ */
|
|
+ mvgstate->seq_index = -1;
|
|
+ }
|
|
+
|
|
+ return elem;
|
|
+}
|
|
+
|
|
+static PyObject *modvers_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
+{
|
|
+ ModVersState *mvgstate = NULL;
|
|
+ PyObject *file;
|
|
+ int fd;
|
|
+ Py_ssize_t len;
|
|
+
|
|
+ if (!PyArg_ParseTuple(args, "O", &file))
|
|
+ return NULL;
|
|
+
|
|
+ fd = PyObject_AsFileDescriptor(file);
|
|
+ if (fd < 0)
|
|
+ return NULL;
|
|
+
|
|
+ mvgstate = (ModVersState *)type->tp_alloc(type, 0);
|
|
+ if (mvgstate == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ elf_version(EV_CURRENT);
|
|
+ mvgstate->elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
|
+ if (mvgstate->elf == NULL) {
|
|
+ PyErr_Format(PyExc_TypeError, "File not usable: %s\n", elf_errmsg(-1));
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ mvgstate->d = get_elf_sec_data(mvgstate->elf, VERS_SEC_NAME);
|
|
+ if (mvgstate->d == NULL) {
|
|
+ PyErr_Format(PyExc_TypeError, "Can't find ELF section `%s`\n", VERS_SEC_NAME);
|
|
+ goto elf_end;
|
|
+ }
|
|
+
|
|
+ len = mvgstate->d->d_size / sizeof(struct modversion_info);
|
|
+ mvgstate->seq_index = len - 1;
|
|
+ mvgstate->enum_index = 0;
|
|
+
|
|
+ return (PyObject *)mvgstate;
|
|
+
|
|
+elf_end:
|
|
+ elf_end(mvgstate->elf);
|
|
+free:
|
|
+ type->tp_free(mvgstate);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+PyTypeObject PyModVersGen_Type = {
|
|
+ PyVarObject_HEAD_INIT(NULL, 0)
|
|
+ .tp_name = "modvers",
|
|
+ .tp_basicsize = sizeof(PyModVersGen_Type),
|
|
+ .tp_itemsize = 0,
|
|
+ .tp_dealloc = modvers_dealloc,
|
|
+ .tp_flags = Py_TPFLAGS_DEFAULT,
|
|
+ .tp_iter = PyObject_SelfIter,
|
|
+ .tp_iternext = modvers_iternext,
|
|
+ .tp_alloc = PyType_GenericAlloc,
|
|
+ .tp_new = modvers_new,
|
|
+};
|
|
+
|
|
+static void modcrcs_dealloc(PyObject *obj)
|
|
+{
|
|
+ ModCRCsState *mcgstate = (ModCRCsState *)obj;
|
|
+
|
|
+ elf_end(mcgstate->elf);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static PyObject *modcrcs_iternext(PyObject *obj)
|
|
+{
|
|
+ ModCRCsState *mcgstate = (ModCRCsState *)obj;
|
|
+ const char *strtab = mcgstate->strtab->d_buf;
|
|
+ GElf_Sym *sym = mcgstate->symtab->d_buf;
|
|
+ PyObject *elem = NULL;
|
|
+
|
|
+ while (mcgstate->seq_index >= 0) {
|
|
+ size_t i = mcgstate->enum_index;
|
|
+ const char *name = strtab + sym[i].st_name;
|
|
+
|
|
+ mcgstate->seq_index -= 1;
|
|
+ mcgstate->enum_index += 1;
|
|
+
|
|
+ /*
|
|
+ * If the symbol has '__crc_' prefix and absolute value,
|
|
+ * it's export symbol, and has CRC.
|
|
+ */
|
|
+ if (strncmp(name, CRC_SYM_PREFIX, strlen(CRC_SYM_PREFIX)) == 0
|
|
+ && sym[i].st_shndx == SHN_ABS) {
|
|
+ elem = Py_BuildValue("(sk)",
|
|
+ name+strlen(CRC_SYM_PREFIX),
|
|
+ sym[i].st_value);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return elem;
|
|
+}
|
|
+
|
|
+static PyObject *modcrcs_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
+{
|
|
+ ModCRCsState *mcgstate = NULL;
|
|
+ PyObject *file;
|
|
+ Elf_Data *d;
|
|
+ int fd;
|
|
+ Py_ssize_t len;
|
|
+
|
|
+ if (!PyArg_ParseTuple(args, "O", &file))
|
|
+ return NULL;
|
|
+
|
|
+ fd = PyObject_AsFileDescriptor(file);
|
|
+ if (fd < 0)
|
|
+ return NULL;
|
|
+
|
|
+ mcgstate = (ModCRCsState *)type->tp_alloc(type, 0);
|
|
+ if (mcgstate == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ elf_version(EV_CURRENT);
|
|
+ mcgstate->elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
|
+ if (mcgstate->elf == NULL) {
|
|
+ PyErr_Format(PyExc_TypeError, "File not usable: %s\n", elf_errmsg(-1));
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ mcgstate->strtab = get_elf_sec_data(mcgstate->elf, STRT_SEC_NAME);
|
|
+ if (mcgstate->strtab == NULL) {
|
|
+ PyErr_Format(PyExc_TypeError, "Can't find ELF section `%s`\n", STRT_SEC_NAME);
|
|
+ goto elf_end;
|
|
+ }
|
|
+
|
|
+ mcgstate->symtab = get_elf_sec_data(mcgstate->elf, SYMT_SEC_NAME);
|
|
+ if (mcgstate->symtab == NULL) {
|
|
+ PyErr_Format(PyExc_TypeError, "Can't find ELF section `%s`\n", SYMT_SEC_NAME);
|
|
+ goto elf_end;
|
|
+ }
|
|
+
|
|
+ len = mcgstate->symtab->d_size / sizeof(GElf_Sym);
|
|
+ mcgstate->seq_index = len - 1;
|
|
+ mcgstate->enum_index = 0;
|
|
+
|
|
+ return (PyObject *)mcgstate;
|
|
+
|
|
+elf_end:
|
|
+ elf_end(mcgstate->elf);
|
|
+free:
|
|
+ type->tp_free(mcgstate);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+PyTypeObject PyModCRCsGen_Type = {
|
|
+ PyVarObject_HEAD_INIT(NULL, 0)
|
|
+ .tp_name = "modcrcs",
|
|
+ .tp_basicsize = sizeof(PyModCRCsGen_Type),
|
|
+ .tp_itemsize = 0,
|
|
+ .tp_dealloc = modcrcs_dealloc,
|
|
+ .tp_flags = Py_TPFLAGS_DEFAULT,
|
|
+ .tp_iter = PyObject_SelfIter,
|
|
+ .tp_iternext = modcrcs_iternext,
|
|
+ .tp_alloc = PyType_GenericAlloc,
|
|
+ .tp_new = modcrcs_new,
|
|
+};
|
|
+
|
|
+/* Module structure */
|
|
+/* Module structure */
|
|
+static struct PyModuleDef modvers_module = {
|
|
+ PyModuleDef_HEAD_INIT,
|
|
+ .m_name = "modsym",
|
|
+ .m_doc = "iter `" VERS_SEC_NAME "` section items",
|
|
+ .m_size = -1,
|
|
+};
|
|
+
|
|
+/* Module initialization function */
|
|
+PyMODINIT_FUNC PyInit_modsym(void)
|
|
+{
|
|
+ PyObject *m = PyModule_Create(&modvers_module);
|
|
+ if (m == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ if (PyType_Ready(&PyModVersGen_Type) < 0)
|
|
+ return NULL;
|
|
+
|
|
+ Py_INCREF(&PyModVersGen_Type);
|
|
+ if (PyModule_AddObject(m, PyModVersGen_Type.tp_name,
|
|
+ (PyObject *)&PyModVersGen_Type) < 0)
|
|
+ goto free_vers;
|
|
+
|
|
+ if (PyType_Ready(&PyModCRCsGen_Type) < 0)
|
|
+ goto free_vers;
|
|
+
|
|
+ Py_INCREF(&PyModCRCsGen_Type);
|
|
+ if (PyModule_AddObject(m, PyModCRCsGen_Type.tp_name,
|
|
+ (PyObject *)&PyModCRCsGen_Type) < 0)
|
|
+ goto free_crcs;
|
|
+
|
|
+ return m;
|
|
+free_crcs:
|
|
+ Py_DECREF(&PyModCRCsGen_Type);
|
|
+free_vers:
|
|
+ Py_DECREF(&PyModVersGen_Type);
|
|
+ Py_DECREF(m);
|
|
+ return NULL;
|
|
+}
|
|
diff --git a/upgchk/lib/modsym.h b/upgchk/lib/modsym.h
|
|
new file mode 100644
|
|
index 0000000..b8069c3
|
|
--- /dev/null
|
|
+++ b/upgchk/lib/modsym.h
|
|
@@ -0,0 +1,39 @@
|
|
+#ifndef __PYTHON_MODSYM_H__
|
|
+#define __PYTHON_MODSYM_H__
|
|
+
|
|
+#include <libelf.h>
|
|
+
|
|
+typedef struct {
|
|
+ PyObject_HEAD
|
|
+ Py_ssize_t seq_index;
|
|
+ Py_ssize_t enum_index;
|
|
+ Elf *elf;
|
|
+ Elf_Data *d;
|
|
+} ModVersState;
|
|
+
|
|
+#define VERS_SEC_NAME "__versions"
|
|
+
|
|
+/* --- the following is copied from linux src --- */
|
|
+#define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long))
|
|
+#define MODULE_NAME_LEN MAX_PARAM_PREFIX_LEN
|
|
+
|
|
+struct modversion_info {
|
|
+ unsigned long crc;
|
|
+ char name[MODULE_NAME_LEN];
|
|
+};
|
|
+/* --- end --- */
|
|
+
|
|
+typedef struct {
|
|
+ PyObject_HEAD
|
|
+ Py_ssize_t seq_index;
|
|
+ Py_ssize_t enum_index;
|
|
+ Elf *elf;
|
|
+ Elf_Data *strtab;
|
|
+ Elf_Data *symtab;
|
|
+} ModCRCsState;
|
|
+
|
|
+#define STRT_SEC_NAME ".strtab"
|
|
+#define SYMT_SEC_NAME ".symtab"
|
|
+#define CRC_SYM_PREFIX "__crc_"
|
|
+
|
|
+#endif /* __PYTHON_MODSYM_H__ */
|
|
diff --git a/upgchk/setup.py b/upgchk/setup.py
|
|
new file mode 100644
|
|
index 0000000..6758c95
|
|
--- /dev/null
|
|
+++ b/upgchk/setup.py
|
|
@@ -0,0 +1,20 @@
|
|
+#!/usr/bin/python3
|
|
+# -*- coding: utf-8 -*-
|
|
+
|
|
+from setuptools import setup, Extension
|
|
+
|
|
+if __name__ == "__main__":
|
|
+
|
|
+ setup(name="upgchk",
|
|
+ version="0.1",
|
|
+ description="Check the kernel upgrading environment",
|
|
+
|
|
+ packages=["upgchk"],
|
|
+ ext_modules=[
|
|
+ Extension("modsym",
|
|
+ sources=["lib/modsym.c"],
|
|
+ libraries=["elf"])
|
|
+ ],
|
|
+
|
|
+ python_requires='>=3.6',
|
|
+ )
|
|
diff --git a/upgchk/upgchk/__init__.py b/upgchk/upgchk/__init__.py
|
|
new file mode 100644
|
|
index 0000000..c831e1d
|
|
--- /dev/null
|
|
+++ b/upgchk/upgchk/__init__.py
|
|
@@ -0,0 +1,11 @@
|
|
+# -*- coding: utf-8 -*-
|
|
+
|
|
+"""
|
|
+.. module:: upgchk
|
|
+ :synopsis: Check the kernel upgrading environment
|
|
+"""
|
|
+
|
|
+__title = "upgchk"
|
|
+__description = "Check the upgrade environment"
|
|
+__license__ = "GPL-2.0-or-later or LGPL-2.1-only"
|
|
+__version__ = "0.1"
|
|
diff --git a/upgchk/upgchk/kabichk.py b/upgchk/upgchk/kabichk.py
|
|
new file mode 100644
|
|
index 0000000..cccacf3
|
|
--- /dev/null
|
|
+++ b/upgchk/upgchk/kabichk.py
|
|
@@ -0,0 +1,163 @@
|
|
+#!/usr/bin/python3
|
|
+# -*- coding: utf-8 -*-
|
|
+
|
|
+'''
|
|
+Theory:
|
|
+- compare CRC value between the known and the module
|
|
+- The export symbols CRC source:
|
|
+ * `/boot/symvers-<release>.gz` for in tree modules and Image
|
|
+ - the ima mechanism could ensure the file credibility and non-tamper
|
|
+ * The `.symtab` section for out of tree modules
|
|
+ - name format: `__crc_<symbol name>`
|
|
+ - it's absolute value, means: `sym->st_shndx == SHN_ABS`
|
|
+
|
|
+Design Details:
|
|
+- collect export symbols from
|
|
+ * collect in tree symbols from `/boot/symvers-<release>.gz`
|
|
+ * collect out of tree module symbols from the module self
|
|
+- compare external symbols stored in `__versions` section for each module
|
|
+
|
|
+`__versions` section data format:
|
|
+
|
|
+ # define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long))
|
|
+ # define MODULE_NAME_LEN MAX_PARAM_PREFIX_LEN
|
|
+
|
|
+ struct modversion_info {
|
|
+ unsigned long crc;
|
|
+ char name[MODULE_NAME_LEN];
|
|
+ };
|
|
+
|
|
+Usage:
|
|
+ python3 -m upgchk.kabichk \
|
|
+ [[-r <kernel release>],...] \
|
|
+ [[-m <modname>],...] \
|
|
+ -c <modname>
|
|
+Example:
|
|
+ python3 -m upgchk.kabichk -c /lib/modules/$(uname -r)/kernel/fs/mbcache.ko
|
|
+ python3 -m upgchk.kabichk -m notify.ko -c osp_proc.ko
|
|
+'''
|
|
+
|
|
+import argparse
|
|
+import gzip
|
|
+import pathlib
|
|
+import platform
|
|
+from typing import Tuple
|
|
+
|
|
+import modsym
|
|
+
|
|
+__all__ = ["KABI"]
|
|
+
|
|
+ELF_SELFMAG = 4
|
|
+ELF_ELFMAG = b"\177ELF"
|
|
+
|
|
+
|
|
+class KABI:
|
|
+ def __init__(self, version: str):
|
|
+ """
|
|
+ read all symbols of the specific kernel
|
|
+ """
|
|
+ self._symbols = dict()
|
|
+ filename = f"symvers-{version}.gz"
|
|
+ filepath = pathlib.Path("/boot/").joinpath(filename)
|
|
+
|
|
+ with gzip.open(filepath, "rt") as f:
|
|
+ for line in f.readlines():
|
|
+ # (crc, sym, loc, type)
|
|
+ (_crc, sym, loc, _) = line.split()
|
|
+ crc = int(_crc, 16) # convert hex crc to integer
|
|
+ self._insert(sym, (crc, sym, loc))
|
|
+
|
|
+ def _insert(self, key: str, val: Tuple[int, str, str]):
|
|
+ inst = self._symbols.get(key)
|
|
+ if inst is None:
|
|
+ self._symbols[key] = val
|
|
+ elif inst != val:
|
|
+ raise KeyError(
|
|
+ f"{key} already exits value {self._symbols[key]}, can't insert new value {val}")
|
|
+
|
|
+ def _get(self, key: str) -> Tuple[int, str, str]:
|
|
+ return self._symbols.get(key)
|
|
+
|
|
+ def _parse_mod_vers(self, filepath: pathlib.Path) -> Tuple[int, str]:
|
|
+ with open(filepath, "rb") as f:
|
|
+ magic = f.read(ELF_SELFMAG)
|
|
+ if magic != ELF_ELFMAG:
|
|
+ raise TypeError(f"{filepath} isn't an ELF file")
|
|
+
|
|
+ for sym, crc in modsym.modvers(f):
|
|
+ yield (sym, crc)
|
|
+
|
|
+ def check_mod_syms(self, filepath: pathlib.Path) -> Tuple[bool, str]:
|
|
+ if not filepath.exists():
|
|
+ raise FileNotFoundError(f"{filepath} isn't found")
|
|
+
|
|
+ for sym, crc in self._parse_mod_vers(filepath):
|
|
+ val = self._get(sym)
|
|
+ if val is None:
|
|
+ msg = f"symbol {sym} isn't known"
|
|
+ return (False, msg)
|
|
+ elif val[0] != crc:
|
|
+ msg = f"symbol {sym} CRC should be {hex(crc)}, but {hex(val[0])}"
|
|
+ return (False, msg)
|
|
+
|
|
+ return (True, "")
|
|
+
|
|
+ def _parse_mod_crcs(self, filepath: pathlib.Path) -> Tuple[int, str]:
|
|
+ with open(filepath, "rb") as f:
|
|
+ magic = f.read(ELF_SELFMAG)
|
|
+ if magic != ELF_ELFMAG:
|
|
+ raise TypeError(f"{filepath} isn't an ELF file")
|
|
+
|
|
+ for inst in modsym.modcrcs(f):
|
|
+ yield inst
|
|
+
|
|
+ def add_mod_crcs(self, filepath: pathlib.Path):
|
|
+ if not filepath.exists():
|
|
+ raise FileNotFoundError(f"{filepath} isn't found")
|
|
+
|
|
+ modname = filepath.name[:-3]
|
|
+ for (sym, crc) in self._parse_mod_crcs(filepath):
|
|
+ self._insert(sym, (crc, sym, modname))
|
|
+
|
|
+
|
|
+def parse_argument() -> argparse.Namespace:
|
|
+ parser = argparse.ArgumentParser()
|
|
+ parser.add_argument("-r", "--release", action="store",
|
|
+ required=False, default=platform.release(),
|
|
+ help="specific the kernel release version")
|
|
+ parser.add_argument("-m", "--module", action="append",
|
|
+ required=False, default=[],
|
|
+ help="specific the out of tree modules")
|
|
+ parser.add_argument("-c", "--check", action="append",
|
|
+ required=True,
|
|
+ help="specific the checked module, e.g. -c a.ko -c b.ko")
|
|
+ options = parser.parse_args()
|
|
+ return (options.release, options.module, options.check)
|
|
+
|
|
+
|
|
+def main():
|
|
+ release, modules, checks = parse_argument()
|
|
+ kabi = KABI(release)
|
|
+
|
|
+ for mod in modules:
|
|
+ filepath = pathlib.Path(mod)
|
|
+ kabi.add_mod_crcs(filepath)
|
|
+
|
|
+ print("-------------- start check --------------")
|
|
+ passed = 0
|
|
+ failed = 0
|
|
+ for mod in checks:
|
|
+ filepath = pathlib.Path(mod)
|
|
+ modname = filepath.name
|
|
+ result, msg = kabi.check_mod_syms(filepath)
|
|
+ if not result:
|
|
+ print(f"module {modname} fail: {msg}")
|
|
+ failed += 1
|
|
+ else:
|
|
+ print(f"module {modname} pass")
|
|
+ passed += 1
|
|
+ print(f"-------------- {passed} pass, {failed} failed --------------")
|
|
+
|
|
+
|
|
+if __name__ == '__main__':
|
|
+ main()
|
|
--
|
|
2.34.1
|
|
|