Merge pull request #2 from ShirkNeko/dev

Dev
This commit is contained in:
ShirkNeko
2025-03-30 01:57:05 +08:00
committed by GitHub
7 changed files with 822 additions and 69 deletions

View File

@@ -31,7 +31,7 @@ unsigned long sukisu_compact_find_symbol(const char* name);
// ====================================================================== // ======================================================================
const char* kver = "0.10"; const char* kpver = "0.10";
struct CompactAddressSymbol { struct CompactAddressSymbol {
const char* symbol_name; const char* symbol_name;
@@ -46,17 +46,17 @@ struct CompactAliasSymbol {
struct CompactAddressSymbol address_symbol [] = { struct CompactAddressSymbol address_symbol [] = {
{ "kallsyms_lookup_name", &kallsyms_lookup_name }, { "kallsyms_lookup_name", &kallsyms_lookup_name },
{ "compact_find_symbol", &sukisu_compact_find_symbol }, { "compact_find_symbol", &sukisu_compact_find_symbol },
{ "compact_copy_to_user", &copy_to_user }, { "compat_copy_to_user", &copy_to_user },
{ "compact_strncpy_from_user", &strncpy_from_user }, { "compat_strncpy_from_user", &strncpy_from_user },
{ "kver", &kver }, { "kpver", &kpver },
{ "is_run_in_sukisu_ultra", (void*)1 } { "is_run_in_sukisu_ultra", (void*)1 }
}; };
struct CompactAliasSymbol alias_symbol[] = { struct CompactAliasSymbol alias_symbol[] = {
{"kf__strncat", "strncat"}, {"kf_strncat", "strncat"},
{"kf__strlen", "strlen" }, {"kf_strlen", "strlen" },
{"kf__strcpy", "strcpy"}, {"kf_strcpy", "strcpy"},
{"compact_copy_to_user", "__arch_copy_to_user"} {"compat_copy_to_user", "__arch_copy_to_user"}
}; };
unsigned long sukisu_compact_find_symbol(const char* name) { unsigned long sukisu_compact_find_symbol(const char* name) {

View File

@@ -34,6 +34,7 @@
#include <linux/version.h> #include <linux/version.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/insn.h>
#include "kpm.h" #include "kpm.h"
#include "compact.h" #include "compact.h"
@@ -361,19 +362,21 @@ static int kpm_simplify_symbols(struct kpm_module *mod, const struct kpm_load_in
return ret; return ret;
} }
/* ARM64 重定位处理:支持 R_AARCH64_RELATIVER_AARCH64_ABS64 */ /* ARM64 重定位处理:支持 R_AARCH64_RELATIVER_AARCH64_ABS64、R_AARCH64_GLOB_DAT、R_AARCH64_JUMP_SLOT */
static int kpm_apply_relocate_arm64(Elf64_Shdr *sechdrs, const char *strtab, int sym_idx, int rel_idx, struct kpm_module *mod) static int kpm_apply_relocate_arm64(Elf64_Shdr *sechdrs, const char *strtab, int sym_idx, int rel_idx, struct kpm_module *mod)
{ {
Elf64_Shdr *relsec = &sechdrs[rel_idx]; Elf64_Shdr *relsec = &sechdrs[rel_idx];
int num = relsec->sh_size / sizeof(Elf64_Rel); int num = relsec->sh_size / sizeof(Elf64_Rel);
Elf64_Rela *rel = (Elf64_Rela *)((char *)mod->start + relsec->sh_entsize); Elf64_Rel *rel = (Elf64_Rel *)((char *)mod->start + relsec->sh_offset); // 修正为 sh_offset
int i; int i;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
unsigned long type = ELF64_R_TYPE(rel[i].r_info); unsigned long type = ELF64_R_TYPE(rel[i].r_info);
unsigned long *addr = (unsigned long *)(mod->start + rel[i].r_offset); unsigned long *addr = (unsigned long *)(mod->start + rel[i].r_offset);
switch (type) { switch (type) {
case R_AARCH64_RELATIVE: case R_AARCH64_RELATIVE:
*addr = (unsigned long)mod->start + rel[i].r_addend; *addr = (unsigned long)mod->start + *(unsigned long *)addr;
break; break;
default: default:
printk(KERN_ERR "ARM64 KPM Loader: Unsupported REL relocation type %lu\n", type); printk(KERN_ERR "ARM64 KPM Loader: Unsupported REL relocation type %lu\n", type);
@@ -383,34 +386,265 @@ static int kpm_apply_relocate_arm64(Elf64_Shdr *sechdrs, const char *strtab, int
return 0; return 0;
} }
static int kpm_apply_relocate_add_arm64(Elf64_Shdr *sechdrs, const char *strtab, int sym_idx, int rela_idx, struct kpm_module *mod) #ifndef R_AARCH64_GLOB_DAT
#define R_AARCH64_GLOB_DAT 1025 /* Set GOT entry to data address */
#endif
#ifndef R_AARCH64_JUMP_SLOT
#define R_AARCH64_JUMP_SLOT 1026 /* Set GOT entry to code address */
#endif
#ifndef R_ARM_NONE
#define R_ARM_NONE 0
#endif
#ifndef R_AARCH64_NONE
#define R_AARCH64_NONE 256
#endif
/* 重定位操作类型 */
typedef enum {
RELOC_OP_ABS,
RELOC_OP_PREL,
RELOC_OP_PAGE
} reloc_op_t;
// 移植自内核 arch/arm64/kernel/insn.c
int aarch64_insn_patch_imm(void *addr, aarch64_insn_imm_type type, s64 imm)
{
u32 insn = le32_to_cpu(*(u32 *)addr);
u32 new_insn;
switch (type) {
case AARCH64_INSN_IMM_16: // MOVZ/MOVK 的 16-bit 立即数
new_insn = aarch64_insn_encode_immediate(type, insn, imm);
break;
case AARCH64_INSN_IMM_26: // B/BL 的 26-bit 偏移
new_insn = aarch64_insn_encode_offset(insn, imm, 26);
break;
case AARCH64_INSN_IMM_ADR: // ADR 的 21-bit 页偏移
new_insn = aarch64_insn_encode_offset(insn, imm, 21);
break;
case AARCH64_INSN_IMM_19: // 条件跳转的 19-bit 偏移
new_insn = aarch64_insn_encode_offset(insn, imm, 19);
break;
default:
return -EINVAL;
}
*(u32 *)addr = cpu_to_le32(new_insn);
return 0;
}
/* 指令编码辅助函数 */
static int reloc_data(reloc_op_t op, void *loc, u64 val, int len)
{
u64 imm = val;
int shift = 0;
switch (op) {
case RELOC_OP_ABS:
break;
case RELOC_OP_PREL:
imm -= (u64)loc;
break;
case RELOC_OP_PAGE:
imm = (imm >> 12) - ((u64)loc >> 12);
shift = 12;
break;
default:
return -EINVAL;
}
switch (len) {
case 16:
*(u16 *)loc = cpu_to_le16(imm >> shift);
break;
case 32:
*(u32 *)loc = cpu_to_le32(imm >> shift);
break;
case 64:
*(u64 *)loc = cpu_to_le64(imm >> shift);
break;
default:
return -EINVAL;
}
/* 检查溢出 */
if (imm > (BIT_ULL(len) - 1) << shift)
return -ERANGE;
return 0;
}
/* MOVW 指令重定位 */
static int reloc_insn_movw(reloc_op_t op, void *loc, u64 val, int shift, enum aarch64_insn_imm_type imm_type)
{
u64 imm = val;
s64 sval;
switch (op) {
case RELOC_OP_ABS:
break;
case RELOC_OP_PREL:
imm -= (u64)loc;
break;
default:
return -EINVAL;
}
sval = (s64)imm;
return aarch64_insn_patch_imm(loc, imm_type, sval >> shift);
}
/* 立即数指令重定位 */
static int reloc_insn_imm(reloc_op_t op, void *loc, u64 val, int shift, int len, enum aarch64_insn_imm_type imm_type)
{
u64 imm = val;
s64 sval;
switch (op) {
case RELOC_OP_ABS:
break;
case RELOC_OP_PREL:
imm -= (u64)loc;
break;
case RELOC_OP_PAGE:
imm = (imm >> 12) - ((u64)loc >> 12);
shift = 0;
break;
default:
return -EINVAL;
}
sval = (s64)imm;
return aarch64_insn_patch_imm(loc, imm_type, sval >> shift);
}
/* 完整的 ARM64 重定位适配 */
static int kpm_apply_relocate_add_arm64(Elf64_Shdr *sechdrs, const char *strtab,
int sym_idx, int rela_idx, struct kpm_module *mod)
{ {
Elf64_Shdr *relasec = &sechdrs[rela_idx]; Elf64_Shdr *relasec = &sechdrs[rela_idx];
int num = relasec->sh_size / sizeof(Elf64_Rela); int num = relasec->sh_size / sizeof(Elf64_Rela);
Elf64_Rela *rela = (Elf64_Rela *)((char *)mod->start + relasec->sh_entsize); Elf64_Rela *rela = (Elf64_Rela *)((char *)mod->start + relasec->sh_offset);
int i; int i, ovf;
bool overflow_check;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
unsigned long type = ELF64_R_TYPE(rela[i].r_info); unsigned long type = ELF64_R_TYPE(rela[i].r_info);
unsigned long sym_index = ELF64_R_SYM(rela[i].r_info); unsigned long sym_index = ELF64_R_SYM(rela[i].r_info);
unsigned long *addr = (unsigned long *)(mod->start + rela[i].r_offset); void *loc = (void *)(mod->start + rela[i].r_offset);
switch (type) { u64 val;
case R_AARCH64_RELATIVE: Elf64_Sym *sym;
*addr = (unsigned long)mod->start + rela[i].r_addend;
break; /* 处理特殊重定位类型 */
case R_AARCH64_ABS64: if (type == R_AARCH64_NONE || type == R_ARM_NONE)
if (sym_index) { continue;
Elf64_Sym *sym = (Elf64_Sym *)((char *)mod->start + sechdrs[sym_idx].sh_offset) + sym_index;
*addr = sym->st_value + rela[i].r_addend; /* 解析符号值 */
if (sym_index) {
sym = (Elf64_Sym *)((char *)mod->start + sechdrs[sym_idx].sh_offset) + sym_index;
val = sym->st_value + rela[i].r_addend;
/* 全局符号需通过内核解析 */
if (ELF64_ST_BIND(sym->st_info) == STB_GLOBAL) {
const char *name = strtab + sym->st_name;
unsigned long addr = kallsyms_lookup_name(name);
if (!addr) {
printk(KERN_ERR "KPM: Symbol %s not found!\n", name);
return -ENOENT;
}
val = addr + rela[i].r_addend;
} else { } else {
printk(KERN_ERR "ARM64 KPM Loader: R_AARCH64_ABS64 with zero symbol\n"); val += (u64)mod->start; // 转换为模块内绝对地址
return -EINVAL;
} }
} else {
val = rela[i].r_addend;
}
overflow_check = true;
/* 处理所有重定位类型 */
switch (type) {
/* 数据重定位 */
case R_AARCH64_ABS64:
overflow_check = false;
ovf = reloc_data(RELOC_OP_ABS, loc, val, 64);
break; break;
case R_AARCH64_ABS32:
ovf = reloc_data(RELOC_OP_ABS, loc, val, 32);
break;
case R_AARCH64_ABS16:
ovf = reloc_data(RELOC_OP_ABS, loc, val, 16);
break;
case R_AARCH64_PREL64:
overflow_check = false;
ovf = reloc_data(RELOC_OP_PREL, loc, val, 64);
break;
case R_AARCH64_PREL32:
ovf = reloc_data(RELOC_OP_PREL, loc, val, 32);
break;
case R_AARCH64_PREL16:
ovf = reloc_data(RELOC_OP_PREL, loc, val, 16);
break;
/* MOVW 指令 */
case R_AARCH64_MOVW_UABS_G0_NC:
overflow_check = false;
case R_AARCH64_MOVW_UABS_G0:
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, AARCH64_INSN_IMM_16);
break;
case R_AARCH64_MOVW_UABS_G1_NC:
overflow_check = false;
case R_AARCH64_MOVW_UABS_G1:
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, AARCH64_INSN_IMM_16);
break;
case R_AARCH64_MOVW_UABS_G2_NC:
overflow_check = false;
case R_AARCH64_MOVW_UABS_G2:
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, AARCH64_INSN_IMM_16);
break;
case R_AARCH64_MOVW_UABS_G3:
overflow_check = false;
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, AARCH64_INSN_IMM_16);
break;
/* 立即数指令 */
case R_AARCH64_ADR_PREL_LO21:
ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, AARCH64_INSN_IMM_ADR);
break;
case R_AARCH64_ADR_PREL_PG_HI21_NC:
overflow_check = false;
case R_AARCH64_ADR_PREL_PG_HI21:
ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, AARCH64_INSN_IMM_ADR);
break;
case R_AARCH64_ADD_ABS_LO12_NC:
ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, AARCH64_INSN_IMM_12);
break;
/* 跳转指令 */
case R_AARCH64_JUMP26:
case R_AARCH64_CALL26:
ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, AARCH64_INSN_IMM_26);
break;
/* GOT 相关 */
case R_AARCH64_GLOB_DAT:
case R_AARCH64_JUMP_SLOT:
*((u64 *)loc) = val;
ovf = 0;
break;
default: default:
printk(KERN_ERR "ARM64 KPM Loader: Unsupported RELA relocation type %lu\n", type); printk(KERN_ERR "KPM: Unsupported relocation type %lu\n", type);
return -EINVAL; return -ENOEXEC;
}
if (overflow_check && ovf == -ERANGE) {
printk(KERN_ERR "KPM: Relocation overflow (type %lu val 0x%llx)\n", type, val);
return -ENOEXEC;
} else if (ovf) {
return ovf;
} }
} }
return 0; return 0;
} }
@@ -418,22 +652,36 @@ static int kpm_apply_relocations(struct kpm_module *mod, const struct kpm_load_i
{ {
int rc = 0; int rc = 0;
int i; int i;
for (i = 1; i < info->ehdr->e_shnum; i++) { for (i = 1; i < info->ehdr->e_shnum; i++) {
unsigned int target = info->sechdrs[i].sh_info; unsigned int target = info->sechdrs[i].sh_info;
if (target >= info->ehdr->e_shnum)
if (target >= info->ehdr->e_shnum) {
printk(KERN_ERR "ARM64 KPM Loader: Invalid target section index %u\n", target);
return -EINVAL;
}
if (!(info->sechdrs[target].sh_flags & SHF_ALLOC)) {
printk(KERN_INFO "ARM64 KPM Loader: Skipping non-allocated section %d\n", i);
continue; continue;
if (!(info->sechdrs[target].sh_flags & SHF_ALLOC)) }
continue;
if (info->sechdrs[i].sh_type == SHT_REL) if (info->sechdrs[i].sh_type == SHT_REL) {
rc = kpm_apply_relocate_arm64(info->sechdrs, info->strtab, info->index.sym, i, mod); rc = kpm_apply_relocate_arm64(info->sechdrs, info->strtab, info->index.sym, i, mod);
else if (info->sechdrs[i].sh_type == SHT_RELA) } else if (info->sechdrs[i].sh_type == SHT_RELA) {
rc = kpm_apply_relocate_add_arm64(info->sechdrs, info->strtab, info->index.sym, i, mod); rc = kpm_apply_relocate_add_arm64(info->sechdrs, info->strtab, info->index.sym, i, mod);
if (rc < 0) }
if (rc < 0) {
printk(KERN_ERR "ARM64 KPM Loader: Relocation failed at section %d, error %d\n", i, rc);
break; break;
}
} }
return rc; return rc;
} }
/*----------------------------------------------------------- /*-----------------------------------------------------------
* 符号表与字符串表布局 * 符号表与字符串表布局
*----------------------------------------------------------*/ *----------------------------------------------------------*/
@@ -481,45 +729,83 @@ static int kpm_rewrite_section_headers(struct kpm_load_info *info)
/*----------------------------------------------------------- /*-----------------------------------------------------------
* 将各段复制到连续内存区域中 * 将各段复制到连续内存区域中
*----------------------------------------------------------*/ *----------------------------------------------------------*/
/*-----------------------------------------------------------
* 将各段复制到连续内存区域(修复版)
* 关键修复点:
* 1. 段地址按对齐要求正确计算
* 2. 显式设置可执行内存权限
* 3. 刷新指令缓存保证一致性
*----------------------------------------------------------*/
static int kpm_move_module(struct kpm_module *mod, struct kpm_load_info *info) static int kpm_move_module(struct kpm_module *mod, struct kpm_load_info *info)
{ {
int i; int i;
unsigned long curr_offset = 0;
Elf64_Shdr *shdr;
void *dest;
const char *secname;
printk(KERN_INFO "ARM64 KPM Loader: Allocating module memory: size=0x%x\n", mod->size); /* 分配连续内存(按页对齐) */
mod->start = kpm_malloc_exec(mod->size); mod->size = ALIGN(mod->size, PAGE_SIZE);
if (!mod->start) mod->start = module_alloc(mod->size); // 使用内核的 module_alloc 接口
if (!mod->start) {
printk(KERN_ERR "ARM64 KPM Loader: Failed to allocate module memory\n");
return -ENOMEM; return -ENOMEM;
}
memset(mod->start, 0, mod->size); memset(mod->start, 0, mod->size);
printk(KERN_INFO "ARM64 KPM Loader: Final section addresses:\n"); /* 设置内存可执行权限(关键修复) */
for (i = 1; i < info->ehdr->e_shnum; i++) { set_memory_x((unsigned long)mod->start, mod->size >> PAGE_SHIFT);
void *dest;
Elf64_Shdr *shdr = &info->sechdrs[i]; printk(KERN_INFO "ARM64 KPM Loader: Final section addresses (aligned base=0x%px):\n", mod->start);
/* 遍历所有段并按对齐要求布局 */
for (i = 0; i < info->ehdr->e_shnum; i++) {
shdr = &info->sechdrs[i];
if (!(shdr->sh_flags & SHF_ALLOC)) if (!(shdr->sh_flags & SHF_ALLOC))
continue; continue;
dest = mod->start + shdr->sh_entsize;
if (shdr->sh_type != SHT_NOBITS) /* 按段对齐要求调整偏移 */
curr_offset = ALIGN(curr_offset, shdr->sh_addralign);
dest = mod->start + curr_offset;
/* 复制段内容NOBITS 段不复制) */
if (shdr->sh_type != SHT_NOBITS) {
memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size); memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
/* 刷新指令缓存(针对可执行段) */
if (shdr->sh_flags & SHF_EXECINSTR) {
flush_icache_range((unsigned long)dest,
(unsigned long)dest + shdr->sh_size);
}
}
/* 更新段头中的虚拟地址 */
shdr->sh_addr = (unsigned long)dest; shdr->sh_addr = (unsigned long)dest;
if (!mod->init && !strcmp(".kpm.init", info->secstrings + shdr->sh_name)) curr_offset += shdr->sh_size;
/* 定位关键函数指针 */
secname = info->secstrings + shdr->sh_name;
if (!mod->init && !strcmp(".kpm.init", secname)) {
mod->init = (int (*)(const char *, const char *, void *__user))dest; mod->init = (int (*)(const char *, const char *, void *__user))dest;
if (!strcmp(".kpm.exit", info->secstrings + shdr->sh_name)) printk(KERN_DEBUG "Found .kpm.init at 0x%px\n", dest);
} else if (!strcmp(".kpm.exit", secname)) {
mod->exit = (void (*)(void *__user))dest; mod->exit = (void (*)(void *__user))dest;
if (!strcmp(".kpm.ctl0", info->secstrings + shdr->sh_name)) }
mod->ctl0 = (int (*)(const char *, char *__user, int))dest;
if (!strcmp(".kpm.ctl1", info->secstrings + shdr->sh_name))
mod->ctl1 = (int (*)(void *, void *, void *))dest;
if (!mod->info.base && !strcmp(".kpm.info", info->secstrings + shdr->sh_name))
mod->info.base = (const char *)dest;
} }
mod->info.name = info->info.name - info->info.base + mod->info.base;
mod->info.version = info->info.version - info->info.base + mod->info.base; /* 调整元数据指针(基于新基址) */
if (info->info.license) if (info->info.base) {
mod->info.license = info->info.license - info->info.base + mod->info.base; unsigned long delta = (unsigned long)mod->start - (unsigned long)info->hdr;
if (info->info.author) mod->info.name = (const char *)((unsigned long)info->info.name + delta);
mod->info.author = info->info.author - info->info.base + mod->info.base; mod->info.version = (const char *)((unsigned long)info->info.version + delta);
if (info->info.description) if (info->info.license)
mod->info.description = info->info.description - info->info.base + mod->info.base; mod->info.license = (const char *)((unsigned long)info->info.license + delta);
if (info->info.author)
mod->info.author = (const char *)((unsigned long)info->info.author + delta);
if (info->info.description)
mod->info.description = (const char *)((unsigned long)info->info.description + delta);
}
return 0; return 0;
} }
@@ -598,36 +884,47 @@ static int kpm_setup_load_info(struct kpm_load_info *info)
* KPM 模块加载主流程 * KPM 模块加载主流程
*----------------------------------------------------------*/ *----------------------------------------------------------*/
/* 注意:接口名称改为 kpm_load_module避免与内核原有 load_module 冲突 */ /* 注意:接口名称改为 kpm_load_module避免与内核原有 load_module 冲突 */
long kpm_load_module(const void *data, int len, const char *args, const char *event, void *__user reserved) long kpm_load_module(const void *data, int len, const char *args,
const char *event, void *__user reserved)
{ {
struct kpm_load_info load_info = { .hdr = data, .len = len }; struct kpm_load_info load_info = { .hdr = data, .len = len };
long rc = 0; long rc = 0;
struct kpm_module *mod; struct kpm_module *mod;
/* 检查 ELF 头 */
rc = kpm_elf_header_check(&load_info); rc = kpm_elf_header_check(&load_info);
if (rc) if (rc)
goto out; goto out;
rc = kpm_setup_load_info(&load_info); rc = kpm_setup_load_info(&load_info);
if (rc) if (rc)
goto out; goto out;
if (find_sec_num(&load_info, ".kpm.init") == -1 || find_sec_num(&load_info, ".kpm.exit") == -1) {
/* 检查必须存在的模块初始化/退出段 */
if (find_sec_num(&load_info, ".kpm.init") == -1 ||
find_sec_num(&load_info, ".kpm.exit") == -1) {
printk(KERN_ERR "ARM64 KPM Loader: Required sections missing\n"); printk(KERN_ERR "ARM64 KPM Loader: Required sections missing\n");
rc = -ENOEXEC; rc = -ENOEXEC;
goto out; goto out;
} }
/* 检查模块是否已经加载 */
if (find_module(load_info.info.name)) { if (find_module(load_info.info.name)) {
printk(KERN_ERR "ARM64 KPM Loader: Module %s already loaded\n", load_info.info.name); printk(KERN_ERR "ARM64 KPM Loader: Module %s already loaded\n",
load_info.info.name);
rc = -EEXIST; rc = -EEXIST;
goto out; goto out;
} }
mod = vmalloc(sizeof(struct kpm_module)); mod = vmalloc(sizeof(struct kpm_module));
if (!mod) if (!mod) {
return -ENOMEM; rc = -ENOMEM;
goto out;
}
memset(mod, 0, sizeof(struct kpm_module)); memset(mod, 0, sizeof(struct kpm_module));
if (args) { if (args) {
mod->args = (typeof(mod->args)) vmalloc(strlen(args) + 1); mod->args = vmalloc(strlen(args) + 1);
if (!mod->args) { if (!mod->args) {
rc = -ENOMEM; rc = -ENOMEM;
goto free_mod; goto free_mod;
@@ -648,17 +945,21 @@ long kpm_load_module(const void *data, int len, const char *args, const char *ev
if (rc) if (rc)
goto free_mod; goto free_mod;
flush_icache_all(); /* 替换 flush_icache_all() 为 flush_icache_range() */
flush_icache_range((unsigned long)mod->start,
(unsigned long)mod->start + mod->size);
rc = mod->init(mod->args, event, reserved); rc = mod->init(mod->args, event, reserved);
if (!rc) { if (!rc) {
printk(KERN_INFO "ARM64 KPM Loader: Module [%s] loaded successfully with args [%s]\n", mod->info.name, args ? args : ""); printk(KERN_INFO "ARM64 KPM Loader: Module [%s] loaded successfully with args [%s]\n",
mod->info.name, args ? args : "");
spin_lock(&kpm_module_lock); spin_lock(&kpm_module_lock);
list_add_tail(&mod->list, &kpm_module_list); list_add_tail(&mod->list, &kpm_module_list);
spin_unlock(&kpm_module_lock); spin_unlock(&kpm_module_lock);
goto out; goto out;
} else { } else {
printk(KERN_ERR "ARM64 KPM Loader: Module [%s] init failed with error %ld\n", mod->info.name, rc); printk(KERN_ERR "ARM64 KPM Loader: Module [%s] init failed with error %ld\n",
mod->info.name, rc);
mod->exit(reserved); mod->exit(reserved);
} }
free_mod: free_mod:
@@ -929,5 +1230,4 @@ int sukisu_is_kpm_control_code(unsigned long arg2) {
return (arg2 >= CMD_KPM_CONTROL && arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0; return (arg2 >= CMD_KPM_CONTROL && arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
} }
EXPORT_SYMBOL(sukisu_handle_kpm); EXPORT_SYMBOL(sukisu_handle_kpm);

View File

@@ -9,6 +9,7 @@ import com.ramcosta.composedestinations.generated.destinations.HomeScreenDestina
import com.ramcosta.composedestinations.generated.destinations.ModuleScreenDestination import com.ramcosta.composedestinations.generated.destinations.ModuleScreenDestination
import com.ramcosta.composedestinations.generated.destinations.SuperUserScreenDestination import com.ramcosta.composedestinations.generated.destinations.SuperUserScreenDestination
import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination
import com.ramcosta.composedestinations.generated.destinations.KpmScreenDestination
import com.ramcosta.composedestinations.spec.DirectionDestinationSpec import com.ramcosta.composedestinations.spec.DirectionDestinationSpec
import shirkneko.zako.sukisu.R import shirkneko.zako.sukisu.R
@@ -22,5 +23,6 @@ enum class BottomBarDestination(
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false), Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true), SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true), Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true),
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Build, Icons.Outlined.Build, true),
Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false), Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false),
} }

View File

@@ -0,0 +1,297 @@
package shirkneko.zako.sukisu.ui.screen
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import shirkneko.zako.sukisu.R
import shirkneko.zako.sukisu.ui.component.ConfirmResult
import shirkneko.zako.sukisu.ui.component.SearchAppBar
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog
import shirkneko.zako.sukisu.ui.theme.getCardColors
import shirkneko.zako.sukisu.ui.theme.getCardElevation
import shirkneko.zako.sukisu.ui.viewmodel.KpmViewModel
import shirkneko.zako.sukisu.ui.util.loadKpmModule
import shirkneko.zako.sukisu.ui.util.unloadKpmModule
import java.io.File
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@Composable
fun KpmScreen(
navigator: DestinationsNavigator,
viewModel: KpmViewModel = viewModel()
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val snackBarHost = remember { SnackbarHostState() }
val confirmDialog = rememberConfirmDialog()
val loadingDialog = rememberLoadingDialog()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val kpmInstall = stringResource(R.string.kpm_install)
val kpmInstallConfirm = stringResource(R.string.kpm_install_confirm)
val kpmInstallSuccess = stringResource(R.string.kpm_install_success)
val kpmInstallFailed = stringResource(R.string.kpm_install_failed)
val install = stringResource(R.string.install)
val cancel = stringResource(R.string.cancel)
val kpmUninstall = stringResource(R.string.kpm_uninstall)
val kpmUninstallConfirmTemplate = stringResource(R.string.kpm_uninstall_confirm)
val uninstall = stringResource(R.string.uninstall)
val kpmUninstallSuccess = stringResource(R.string.kpm_uninstall_success)
val kpmUninstallFailed = stringResource(R.string.kpm_uninstall_failed)
val selectPatchLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode != RESULT_OK) return@rememberLauncherForActivityResult
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
scope.launch {
// 复制文件到临时目录
val tempFile = File(context.cacheDir, "temp_patch.kpm")
context.contentResolver.openInputStream(uri)?.use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
}
val confirmResult = confirmDialog.awaitConfirm(
title = kpmInstall,
content = kpmInstallConfirm,
confirm = install,
dismiss = cancel
)
if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading {
loadKpmModule(tempFile.absolutePath)
}
Log.d("KsuCli", "loadKpmModule result: $success")
if (success == "success") {
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmInstallSuccess,
duration = SnackbarDuration.Long
)
} else {
// 修正为显示安装失败的消息
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Long
)
}
}
tempFile.delete()
}
}
LaunchedEffect(Unit) {
if (viewModel.moduleList.isEmpty()) {
viewModel.fetchModuleList()
}
}
Scaffold(
topBar = {
SearchAppBar(
title = { Text(stringResource(R.string.kpm_title)) },
searchText = viewModel.search,
onSearchTextChange = { viewModel.search = it },
onClearClick = { viewModel.search = "" },
scrollBehavior = scrollBehavior,
dropdownContent = {
IconButton(onClick = { viewModel.fetchModuleList() }) {
Icon(
imageVector = Icons.Outlined.Refresh,
contentDescription = stringResource(R.string.refresh)
)
}
}
)
},
floatingActionButton = {
ExtendedFloatingActionButton(
onClick = {
selectPatchLauncher.launch(
Intent(Intent.ACTION_GET_CONTENT).apply {
type = "application/*"
}
)
},
icon = {
Icon(
imageVector = Icons.Outlined.Add,
contentDescription = stringResource(R.string.kpm_install)
)
},
text = { Text(stringResource(R.string.kpm_install)) },
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
},
snackbarHost = { SnackbarHost(snackBarHost) }
) { padding ->
PullToRefreshBox(
onRefresh = { viewModel.fetchModuleList() },
isRefreshing = viewModel.isRefreshing,
modifier = Modifier.padding(padding)
) {
if (viewModel.moduleList.isEmpty()) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
stringResource(R.string.kpm_empty),
textAlign = TextAlign.Center
)
}
} else {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(viewModel.moduleList) { module ->
val kpmUninstallConfirm = String.format(kpmUninstallConfirmTemplate, module.name)
KpmModuleItem(
module = module,
onUninstall = {
scope.launch {
val confirmResult = confirmDialog.awaitConfirm(
title = kpmUninstall,
content = kpmUninstallConfirm,
confirm = uninstall,
dismiss = cancel
)
if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading {
unloadKpmModule(module.id)
}
Log.d("KsuCli", "unloadKpmModule result: $success")
if (success == "success") {
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmUninstallSuccess,
duration = SnackbarDuration.Long
)
} else {
snackBarHost.showSnackbar(
message = kpmUninstallFailed,
duration = SnackbarDuration.Long
)
}
}
}
},
onControl = {
viewModel.loadModuleDetail(module.id)
}
)
}
}
}
}
}
}
@Composable
private fun KpmModuleItem(
module: KpmViewModel.ModuleInfo,
onUninstall: () -> Unit,
onControl: () -> Unit
) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = module.name,
style = MaterialTheme.typography.titleMedium
)
Text(
text = "${stringResource(R.string.kpm_version)}: ${module.version}",
style = MaterialTheme.typography.bodyMedium
)
Text(
text = "${stringResource(R.string.kpm_author)}: ${module.author}",
style = MaterialTheme.typography.bodyMedium
)
Text(
text = "${stringResource(R.string.kpm_args)}: ${module.args}",
style = MaterialTheme.typography.bodyMedium
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = module.description,
style = MaterialTheme.typography.bodyMedium
)
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
FilledTonalButton(
onClick = onControl
) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = null
)
Text(stringResource(R.string.kpm_control))
}
FilledTonalButton(
onClick = onUninstall
) {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = null
)
Text(stringResource(R.string.kpm_uninstall))
}
}
}
}
}

View File

@@ -480,3 +480,57 @@ fun susfsSUS_SU_Mode(): String {
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su mode") val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su mode")
return result return result
} }
private fun getKpmmgrPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libkpmmgr.so"
}
fun loadKpmModule(path: String, args: String? = null): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
val result = ShellUtils.fastCmd(shell, cmd)
return result
}
fun unloadKpmModule(name: String): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} unload $name"
val result = ShellUtils.fastCmd(shell, cmd)
return result
}
fun getKpmModuleCount(): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} num"
val result = ShellUtils.fastCmd(shell, cmd)
return result
}
fun listKpmModules(): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} list"
val result = ShellUtils.fastCmd(shell, cmd)
return result
}
fun getKpmModuleInfo(name: String): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} info $name"
val result = ShellUtils.fastCmd(shell, cmd)
return result
}
fun controlKpmModule(name: String, args: String? = null): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} control $name ${args ?: ""}"
val result = ShellUtils.fastCmd(shell, cmd)
return result
}
fun printKpmModules(): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} print"
val result = ShellUtils.fastCmd(shell, cmd)
return result
}

View File

@@ -0,0 +1,98 @@
package shirkneko.zako.sukisu.ui.viewmodel
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import shirkneko.zako.sukisu.ui.util.*
class KpmViewModel : ViewModel() {
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
private set
var search by mutableStateOf("")
internal set
var isRefreshing by mutableStateOf(false)
private set
var currentModuleDetail by mutableStateOf("")
private set
fun loadModuleDetail(moduleId: String) {
viewModelScope.launch {
currentModuleDetail = withContext(Dispatchers.IO) {
try {
getKpmModuleInfo(moduleId)
} catch (e: Exception) {
"无法获取模块详细信息: ${e.message}"
}
}
Log.d("KsuCli", "Module detail: $currentModuleDetail")
}
}
fun fetchModuleList() {
viewModelScope.launch {
isRefreshing = true
try {
val moduleCount = getKpmModuleCount()
Log.d("KsuCli", "Module count: $moduleCount")
val moduleInfo = listKpmModules()
Log.d("KsuCli", "Module info: $moduleInfo")
val modules = parseModuleList(moduleInfo)
moduleList = modules
} finally {
isRefreshing = false
}
}
}
private fun getInstalledKernelPatches(): List<ModuleInfo> {
return try {
val output = printKpmModules()
parseModuleList(output)
} catch (e: Exception) {
emptyList()
}
}
private fun parseModuleList(output: String): List<ModuleInfo> {
return output.split("\n").mapNotNull { line ->
if (line.isBlank()) return@mapNotNull null
val parts = line.split("|")
if (parts.size < 7) return@mapNotNull null
ModuleInfo(
id = parts[0].trim(),
name = parts[1].trim(),
version = parts[2].trim(),
author = parts[3].trim(),
description = parts[4].trim(),
args = parts[6].trim(),
enabled = true,
hasAction = controlKpmModule(parts[0].trim()).isNotBlank()
)
}
}
data class ModuleInfo(
val id: String,
val name: String,
val version: String,
val author: String,
val description: String,
val args: String,
val enabled: Boolean,
val hasAction: Boolean
)
}

View File

@@ -236,4 +236,6 @@
<string name="kpm_install_confirm">Confirm installation?</string> <string name="kpm_install_confirm">Confirm installation?</string>
<string name="kpm_install_success">Installation of kpm module successful</string> <string name="kpm_install_success">Installation of kpm module successful</string>
<string name="kpm_install_failed">Installation of kpm module failed</string> <string name="kpm_install_failed">Installation of kpm module failed</string>
<string name="kpm_args">kpm 参数</string>
<string name="kpm_control">kpm 控制</string>
</resources> </resources>