diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-05-13 15:54:04 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-05-13 15:54:04 +0800 |
| commit | c9f158361cba8218a45fdc0ccb9a14ba05ddac3a (patch) | |
| tree | 1266015cafa092543da3db44d9a7648b82fb8978 | |
| parent | 68fdbf4667b6c9f6d6e6d8adb288bc71ddc7ac02 (diff) | |
| download | lume-c9f158361cba8218a45fdc0ccb9a14ba05ddac3a.tar.gz lume-c9f158361cba8218a45fdc0ccb9a14ba05ddac3a.tar.xz | |
feat: add KASLR configuration options and implement LZ4 compression in pack_image script
| -rw-r--r-- | na/scripts/Kconfig.mk | 17 | ||||
| -rw-r--r-- | na/scripts/pack_image.py | 227 |
2 files changed, 244 insertions, 0 deletions
diff --git a/na/scripts/Kconfig.mk b/na/scripts/Kconfig.mk index ee2d9de..c979e76 100644 --- a/na/scripts/Kconfig.mk +++ b/na/scripts/Kconfig.mk @@ -49,7 +49,10 @@ CONFIG_KERNEL_STACK_SIZE ?= 8192 # --- Security / Hardening ------------------------------------------- CONFIG_KASLR ?= y +CONFIG_KASLR_POLICY_ID ?= 0 +CONFIG_KASLR_LOG_LEVEL ?= 1 CONFIG_STACK_CANARY ?= y +CONFIG_KERNEL_COMPRESSED ?= y # --- Debug ----------------------------------------------------------- CONFIG_DEBUG ?= y @@ -98,6 +101,8 @@ KCPPFLAGS += -DCONFIG_KERNEL_STACK_SIZE=$(CONFIG_KERNEL_STACK_SIZE) ifeq ($(CONFIG_KASLR),y) KCPPFLAGS += -DCONFIG_KASLR=1 endif +KCPPFLAGS += -DCONFIG_KASLR_POLICY_ID=$(CONFIG_KASLR_POLICY_ID) +KCPPFLAGS += -DCONFIG_KASLR_LOG_LEVEL=$(CONFIG_KASLR_LOG_LEVEL) ifeq ($(CONFIG_STACK_CANARY),y) KCPPFLAGS += -DCONFIG_STACK_CANARY=1 endif @@ -113,6 +118,9 @@ endif ifeq ($(CONFIG_KLOG),y) KCPPFLAGS += -DCONFIG_KLOG=1 endif +ifeq ($(CONFIG_KERNEL_COMPRESSED),y) +KCPPFLAGS += -DCONFIG_KERNEL_COMPRESSED=1 +endif # ------------------------------------------------------------------ # §5 Generate autoconf.h (analogous to Linux's include/generated/autoconf.h) @@ -147,6 +155,8 @@ define generate-autoconf @echo "" >> $(AUTOCONF_H) @echo "/* Security */" >> $(AUTOCONF_H) $(if $(filter y,$(CONFIG_KASLR)), @echo "#define CONFIG_KASLR 1" >> $(AUTOCONF_H)) + @echo "#define CONFIG_KASLR_POLICY_ID $(CONFIG_KASLR_POLICY_ID)" >> $(AUTOCONF_H) + @echo "#define CONFIG_KASLR_LOG_LEVEL $(CONFIG_KASLR_LOG_LEVEL)" >> $(AUTOCONF_H) $(if $(filter y,$(CONFIG_STACK_CANARY)), @echo "#define CONFIG_STACK_CANARY 1" >> $(AUTOCONF_H)) @echo "" >> $(AUTOCONF_H) @echo "/* Debug */" >> $(AUTOCONF_H) @@ -154,6 +164,7 @@ define generate-autoconf $(if $(filter y,$(CONFIG_DEBUG_LOCKS)), @echo "#define CONFIG_DEBUG_LOCKS 1" >> $(AUTOCONF_H)) $(if $(filter y,$(CONFIG_SELFTEST)), @echo "#define CONFIG_SELFTEST 1" >> $(AUTOCONF_H)) $(if $(filter y,$(CONFIG_KLOG)), @echo "#define CONFIG_KLOG 1" >> $(AUTOCONF_H)) + $(if $(filter y,$(CONFIG_KERNEL_COMPRESSED)), @echo "#define CONFIG_KERNEL_COMPRESSED 1" >> $(AUTOCONF_H)) endef # ------------------------------------------------------------------ @@ -179,11 +190,14 @@ savedefconfig: @echo "CONFIG_MAX_ORDER = $(CONFIG_MAX_ORDER)" >> .config @echo "CONFIG_KERNEL_STACK_SIZE = $(CONFIG_KERNEL_STACK_SIZE)" >> .config @echo "CONFIG_KASLR = $(CONFIG_KASLR)" >> .config + @echo "CONFIG_KASLR_POLICY_ID = $(CONFIG_KASLR_POLICY_ID)" >> .config + @echo "CONFIG_KASLR_LOG_LEVEL = $(CONFIG_KASLR_LOG_LEVEL)" >> .config @echo "CONFIG_STACK_CANARY = $(CONFIG_STACK_CANARY)" >> .config @echo "CONFIG_DEBUG = $(CONFIG_DEBUG)" >> .config @echo "CONFIG_DEBUG_LOCKS = $(CONFIG_DEBUG_LOCKS)" >> .config @echo "CONFIG_SELFTEST = $(CONFIG_SELFTEST)" >> .config @echo "CONFIG_KLOG = $(CONFIG_KLOG)" >> .config + @echo "CONFIG_KERNEL_COMPRESSED = $(CONFIG_KERNEL_COMPRESSED)" >> .config # 'make showconfig' — print effective configuration showconfig: @@ -201,9 +215,12 @@ showconfig: @echo " MAX_ORDER = $(CONFIG_MAX_ORDER)" @echo " KERNEL_STACK_SIZE = $(CONFIG_KERNEL_STACK_SIZE)" @echo " KASLR = $(CONFIG_KASLR)" + @echo " KASLR_POLICY_ID = $(CONFIG_KASLR_POLICY_ID)" + @echo " KASLR_LOG_LEVEL = $(CONFIG_KASLR_LOG_LEVEL)" @echo " STACK_CANARY = $(CONFIG_STACK_CANARY)" @echo " DEBUG = $(CONFIG_DEBUG)" @echo " DEBUG_LOCKS = $(CONFIG_DEBUG_LOCKS)" @echo " SELFTEST = $(CONFIG_SELFTEST)" @echo " KLOG = $(CONFIG_KLOG)" + @echo " KERNEL_COMPRESSED = $(CONFIG_KERNEL_COMPRESSED)" @echo "========================================" diff --git a/na/scripts/pack_image.py b/na/scripts/pack_image.py new file mode 100644 index 0000000..dc5c6db --- /dev/null +++ b/na/scripts/pack_image.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2026 Richard Qin + +import argparse +import struct +import sys + +MAGIC = 0x4C554D45434F4D50 # "LUMECOMP" +COMP_NONE = 0 +COMP_LZ4 = 1 + +HDR_FMT = "<QIHHIIQQQQQQII" +HDR_SIZE = struct.calcsize(HDR_FMT) +HDR_OFFSET = 64 + + +def crc32_update(crc: int, data: bytes) -> int: + for b in data: + crc ^= b + for _ in range(8): + mask = -(crc & 1) & 0xFFFFFFFF + crc = ((crc >> 1) ^ (0xEDB88320 & mask)) & 0xFFFFFFFF + return crc + + +def crc32_ieee(data: bytes) -> int: + crc = 0xFFFFFFFF + crc = crc32_update(crc, data) + return (crc ^ 0xFFFFFFFF) & 0xFFFFFFFF + + +def lz4_compress(data: bytes) -> bytes: + n = len(data) + if n == 0: + return b"" + + min_match = 4 + hash_log = 16 + hash_size = 1 << hash_log + max_offset = 65535 + + def hash4(b0, b1, b2, b3): + v = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24) + return (v * 2654435761) & (hash_size - 1) + + htab = [-1] * hash_size + out = bytearray() + anchor = 0 + i = 0 + + while i + min_match <= n: + h = hash4(data[i], data[i + 1], data[i + 2], data[i + 3]) + ref = htab[h] + htab[h] = i + + if ref >= 0 and i - ref <= max_offset and data[ref:ref + min_match] == data[i:i + min_match]: + match_len = min_match + while i + match_len < n and data[ref + match_len] == data[i + match_len]: + match_len += 1 + + lit_len = i - anchor + token = (15 if lit_len >= 15 else lit_len) << 4 + match_field = match_len - min_match + token |= 15 if match_field >= 15 else match_field + out.append(token) + + if lit_len >= 15: + l = lit_len - 15 + while l >= 255: + out.append(255) + l -= 255 + out.append(l) + + out.extend(data[anchor:i]) + + offset = i - ref + out.append(offset & 0xFF) + out.append((offset >> 8) & 0xFF) + + if match_field >= 15: + l = match_field - 15 + while l >= 255: + out.append(255) + l -= 255 + out.append(l) + + i += match_len + anchor = i + continue + + i += 1 + + lit_len = n - anchor + token = (15 if lit_len >= 15 else lit_len) << 4 + out.append(token) + if lit_len >= 15: + l = lit_len - 15 + while l >= 255: + out.append(255) + l -= 255 + out.append(l) + out.extend(data[anchor:]) + return bytes(out) + + +def get_header_offset(data: bytes) -> int: + if len(data) < HDR_OFFSET + HDR_SIZE: + return -1 + if data[HDR_OFFSET:HDR_OFFSET + 8] != struct.pack("<Q", MAGIC): + return -1 + return HDR_OFFSET + + +def read_struct(fmt, data, offset): + size = struct.calcsize(fmt) + return struct.unpack_from(fmt, data, offset), size + + +def parse_elf_symbols(path: str): + data = open(path, "rb").read() + if data[:4] != b"\x7fELF": + return {} + if data[4] != 2 or data[5] != 1: + return {} + (e_ident, e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, + e_flags, e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, + e_shstrndx), _ = read_struct("<16sHHIQQQIHHHHHH", data, 0) + + symbols = {} + for i in range(e_shnum): + off = e_shoff + i * e_shentsize + (sh_name, sh_type, sh_flags, sh_addr, sh_offset, sh_size, + sh_link, sh_info, sh_addralign, sh_entsize), _ = read_struct( + "<IIQQQQIIQQ", data, off) + if sh_type != 2: + continue + if sh_link >= e_shnum: + continue + str_off = e_shoff + sh_link * e_shentsize + (sname, stype, sflags, saddr, soff, ssize, slink, sinfo, + salign, sentsz), _ = read_struct("<IIQQQQIIQQ", data, str_off) + strtab = data[soff:soff + ssize] + entsize = sh_entsize or 24 + count = sh_size // entsize + for j in range(count): + ent_off = sh_offset + j * entsize + (st_name, st_info, st_other, st_shndx, st_value, st_size), _ = read_struct( + "<IBBHQQ", data, ent_off) + if st_name == 0: + continue + end = strtab.find(b"\x00", st_name) + if end < 0: + continue + name = strtab[st_name:end].decode("ascii", errors="ignore") + if name: + symbols[name] = st_value + break + return symbols + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--elf") + ap.add_argument("--input", required=True) + ap.add_argument("--output", required=True) + ap.add_argument("--compress", choices=["none", "lz4"], default="none") + args = ap.parse_args() + + raw = open(args.input, "rb").read() + hdr_off = get_header_offset(raw) + if hdr_off < 0: + raise ValueError("compressed header magic not found") + + header = list(struct.unpack_from(HDR_FMT, raw, hdr_off)) + if header[0] != MAGIC: + raise ValueError("bad header magic") + if header[2] < HDR_SIZE: + raise ValueError("header_size too small") + + if args.compress == "lz4": + payload = lz4_compress(raw) + comp = COMP_LZ4 + else: + payload = b"" + comp = COMP_NONE + + uncompressed_size = len(raw) + compressed_size = len(payload) + payload_offset = (len(raw) + 7) & ~7 if payload else 0 + if payload and args.elf: + syms = parse_elf_symbols(args.elf) + link_base = syms.get("_start") + bss_end = syms.get("_ebss") + if link_base is not None and bss_end is not None and bss_end >= link_base: + payload_offset = (bss_end - link_base + 7) & ~7 + + header[4] = comp + header[6] = uncompressed_size + header[7] = compressed_size + header[8] = 0 # load_phys_base auto + header[11] = payload_offset + header[12] = 0 + header[13] = crc32_ieee(payload) if payload else 0 + + header_bytes = struct.pack(HDR_FMT, *header) + header[12] = crc32_ieee(header_bytes) + + out = bytearray(raw) + struct.pack_into(HDR_FMT, out, hdr_off, *header) + if payload: + if payload_offset > len(out): + out.extend(b"\x00" * (payload_offset - len(out))) + out.extend(payload) + + with open(args.output, "wb") as f: + f.write(out) + + return 0 + + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as e: + print("pack_image: " + str(e), file=sys.stderr) + sys.exit(1) |
