@@ -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", ©_to_user },
|
{ "compat_copy_to_user", ©_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) {
|
||||||
|
|||||||
422
kernel/kpm/kpm.c
422
kernel/kpm/kpm.c
@@ -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_RELATIVE 与 R_AARCH64_ABS64 */
|
/* ARM64 重定位处理:支持 R_AARCH64_RELATIVE、R_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);
|
||||||
|
|
||||||
@@ -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),
|
||||||
}
|
}
|
||||||
|
|||||||
297
manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/kpm.kt
Normal file
297
manager/app/src/main/java/shirkneko/zako/sukisu/ui/screen/kpm.kt
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user