summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-05-13 15:54:04 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-05-13 15:54:04 +0800
commitc9f158361cba8218a45fdc0ccb9a14ba05ddac3a (patch)
tree1266015cafa092543da3db44d9a7648b82fb8978
parent68fdbf4667b6c9f6d6e6d8adb288bc71ddc7ac02 (diff)
downloadlume-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.mk17
-rw-r--r--na/scripts/pack_image.py227
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)