当前位置: 首页 > news >正文

Android异常信号处理详解

目录

一、异常信号介绍

二、异常信号处理框架

三、异常信号处理流程及关键代码

3.1 异常信号注册

3.2 异常触发及信号生成

3.3 异常信号传递

3.4 异常信号处理

四、其它

4.1 线程设置信号栈


平台:Android 14 & kernel 5.4

一、异常信号介绍

用户进程启动时内核会通过linker链接器为该进程注册8种异常信号,当异常信号被触发时返回用户空间信号处理阶段会去抓取tombstone文件,如下:

序号

信号类型

数值

说明

触发原因

1

SIGABRT

6

Abort Signal(终止信号),表示程序因内部错误主动终止运行。通常由 abort() 函数显式触发。

1)显式调用 abort():如断言失败(assert())、库函数检测到不可恢复错误(如内存分配失败)。

2)未捕获的异常:某些语言(如 C++)中未处理的异常可能转换为 SIGABRT。

2

SIGBUS

7

Bus Error(总线错误),表示内存访问违反硬件对齐规则或地址无效。

1)内存对齐错误(如访问未对齐的指针)

2)访问物理地址无效的内存区域(如设备映射错误)

3)读写未映射或只读的内存页

3

SIGFPE

8

Floating Point Exception(浮点异常),表示数学运算错误(浮点或整数运算)。

1)浮点运算错误(如除以零、溢出、无效操作数)

2)整数运算异常(如除以零或模零)。

4

SIGILL

4

Illegal Instruction(非法指令),表示执行了无效或 CPU 不支持的机器指令。

1)执行无效或不存在的机器指令(如代码损坏、解码错误)。

2)CPU 不支持的指令(如使用特定架构扩展的指令但在硬件不支持时执行)。

3)函数返回值地址被篡改。

5

SIGSEGV

11

段错误(Segmentation Fault),因非法内存访问触发,进程尝试访问未分配或受保护的内存区域。

空指针引用、数组越界、访问已释放的内存、堆栈溢出或内存对齐等。

6

SIGSTKFLT

16

Stack Fault(栈故障),表示栈相关异常。

1)硬件检测到栈相关异常(如栈溢出或栈指针无效)。

2)在自定义栈管理场景中显式触发。

7

SIGSYS

31

Bad System Call(非法系统调用),表示系统调用参数或编号错误。

1)调用无效的系统调用号或参数。

2)系统调用参数类型/值不合法。

8

SIGTRAP

5

Trap Signal(陷阱信号),用于调试器设置断点或软件陷阱。

1)调试器设置断点(通过 int3 指令或硬件断点)。

2)软件陷阱指令(如 trap 指令)。

二、异常信号处理框架

三、异常信号处理流程及关键代码

完整信号处理流程:

1)注册阶段 (用户空间)

应用程序调用 sigaction() 注册处理函数 ---> 内核更新当前进程的 sighand_struct->action[]

2)触发阶段 (内核)

硬件异常/软件信号产生 ---> 内核设置 TIF_SIGPENDING 标志 ---> 将信号加入 pending 队列

3)传递阶段 (内核→用户空间)

进程从内核态返回用户态前 ---> 检查到 TIF_SIGPENDING ---> 调用 handle_signal() ---> 设置用户态栈帧和执行上下文

4)处理阶段 (用户空间)

执行注册的信号处理函数 ---> 完成自定义处理逻辑 ---> sigreturn() 系统调用

5)恢复阶段 (内核)

sigreturn 系统调用 ---> 恢复原始执行上下文 ---> 清除信号状态 ---> 返回到被中断的代码

3.1 异常信号注册

向内核注册8种异常信号的回调处理函数(debuggerd_signal_handler),内核存储该函数指针,当触发异常信号时,内核侧通过该函数指针回调执行该函数。

  • 用户空间

(1) 链接器初始化阶段

内核启动linker连接器,在 __linker_init 函数中调用初始化函数:

// bionic/linker/linker_main.cpp
void __linker_init(...) {...return __linker_init_post_relocation(args, tmp_linker_so);
}
__linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so) {...linker_main(args, exe_to_load);...
}
// bionic/linker/linker_main.cpp 
linker_main(KernelArgumentBlock& args, const char* exe_to_load) {...linker_debuggerd_init();...
}
linker_main(KernelArgumentBlock& args, const char* exe_to_load) {...linker_debuggerd_init();...
}
// bionic/linker/linker_debuggerd_android.cpp 
void linker_debuggerd_init() {...debuggerd_init(&callbacks);
}

(2) 核心注册函数:debuggerd_init()

// system/core/debuggerd/handler/debuggerd_handler.cpp
void debuggerd_init(debuggerd_callbacks_t* callbacks) {...struct sigaction action;memset(&action, 0, sizeof(action));sigfillset(&action.sa_mask);action.sa_sigaction = debuggerd_signal_handler;  // 设置处理函数action.sa_flags = SA_RESTART | SA_SIGINFO;    // 关键标志action.sa_flags |= SA_ONSTACK;    // 关键标志,这确保信号处理与主栈操作完全隔离,避免因主栈溢出或冲突导致程序崩溃。debuggerd_register_handlers(&action);
}
// system/core/debuggerd/include/debuggerd/handler.h
static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {// 注册8种异常信号sigaction(SIGABRT, action, nullptr);sigaction(SIGBUS, action, nullptr);sigaction(SIGFPE, action, nullptr);sigaction(SIGILL, action, nullptr);sigaction(SIGSEGV, action, nullptr);sigaction(SIGSTKFLT, action, nullptr);sigaction(SIGSYS, action, nullptr);sigaction(SIGTRAP, action, nullptr);sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr);
}
  • 内核空间

用户执行sigaction系统调用接口,向内核注册信号(保存信号及信号处理函数指针):

// kernel/kernel/signal.c 
// 信号注册系统调用
SYSCALL_DEFINE3(sigaction, int, sig,const struct old_sigaction __user *, act,struct old_sigaction __user *, oact)
{struct k_sigaction new_ka, old_ka;int ret;// 1. 从用户空间复制处理函数配置if (act) {old_sigset_t mask;if (!access_ok(act, sizeof(*act)) ||__get_user(new_ka.sa.sa_handler, &act->sa_handler) ||__get_user(new_ka.sa.sa_restorer, &act->sa_restorer) ||__get_user(new_ka.sa.sa_flags, &act->sa_flags) ||__get_user(mask, &act->sa_mask))return -EFAULT;//2. 设置信号的处理操作ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);// 3. 复制旧的处理函数配置if (!ret && oact) {if (!access_ok(oact, sizeof(*oact)) ||__put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||__put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) ||__put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))return -EFAULT;}
}

3.2 异常触发及信号生成

以用户进程尝试访问无效内存地址(SIGSEGV)为例。

  • 硬件异常触发

触发条件:

当用户进程尝试访问无效内存地址时(如空指针或未映射地址),ARM64硬件MMU触发 Data Abort异常。

CPU行为:

1)发生数据中止异常(Data Abort)时,CPU 自动切换到 EL1 异常级别

2)保存 PSTATE 到 SPSR_EL1,PC 到 ELR_EL1

3)跳转到异常向量表对应条目

// // arch/arm64/kernel/entry.S
ENTRY(vectors)kernel_ventry   1, sync_invalid         // Synchronous EL1tkernel_ventry   1, irq_invalid          // IRQ EL1tkernel_ventry   1, fiq_invalid          // FIQ EL1tkernel_ventry   1, error_invalid        // Error EL1tkernel_ventry   1, sync                 // Synchronous EL1h  <-- 数据中止走这里kernel_ventry   1, irq                  // IRQ EL1hkernel_ventry   1, fiq_invalid          // FIQ EL1hkernel_ventry   1, error_invalid        // Error EL1h
END(vectors)
  • 内核异常向量处理

同步异常处理入口:

// arch/arm64/kernel/entry.S
SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label)kernel_entry \el, \regsizemov        x0, spbl        el\el\ht\()_\regsize\()_\label\()_handler  // 跳转到 C 函数处理.if \el == 0b        ret_to_user  // 返回用户空间.elseb        ret_to_kernel.endif

数据中止处理(el1h_64_sync_handler为硬件中断处理函数):

// arch/arm64/kernel/entry-common.c asmlinkage void noinstr el1h_64_sync_handler(struct pt_regs *regs)
{unsigned long esr = read_sysreg(esr_el1);switch (ESR_ELx_EC(esr)) {case ESR_ELx_EC_DABT_CUR:case ESR_ELx_EC_IABT_CUR:el1_abort(regs, esr);break;...
}
static void noinstr el1_abort(struct pt_regs *regs, unsigned long esr)
{...do_mem_abort(far, esr, regs);local_daif_mask();exit_to_kernel_mode(regs);
}// arch/arm64/mm/fault.c
void do_mem_abort(unsigned long far, unsigned long esr, struct pt_regs *regs)
{...trace_mem_abort_entries(addr, esr, regs);...arm64_notify_die(inf->name, regs, inf->sig, inf->code, addr, esr);
}trace_mem_abort_entries(unsigned long far, unsigned long esr, struct pt_regs *regs)
{if (!trace_mem_abort_enabled())return;if (user_mode(regs))trace_mem_abort_user(far, esr, regs);elsetrace_mem_abort_kernel(far, esr, regs);
}
  • 信号生成

内核在处理异常时,会根据异常类型生成对应的异常信号(如SIGSEGV):

// arch/arm64/mm/fault.c
static const struct fault_info fault_info[] = {...// do_translation_fault:处理页面未映射的错误(如访问未分配的虚拟地址)// do_page_fault:处理页面存在但权限不足的情况(如访问只读页面){ do_translation_fault,        SIGSEGV, SEGV_MAPERR,        "level 0 translation fault"        },{ do_translation_fault,        SIGSEGV, SEGV_MAPERR,        "level 1 translation fault"        },{ do_translation_fault,        SIGSEGV, SEGV_MAPERR,        "level 2 translation fault"        },{ do_translation_fault,        SIGSEGV, SEGV_MAPERR,        "level 3 translation fault"        },{ do_page_fault,        SIGSEGV, SEGV_ACCERR,        "level 1 access flag fault"        },{ do_page_fault,        SIGSEGV, SEGV_ACCERR,        "level 2 access flag fault"        },{ do_page_fault,        SIGSEGV, SEGV_ACCERR,        "level 3 access flag fault"        },{ do_page_fault,        SIGSEGV, SEGV_ACCERR,        "level 1 permission fault"        },{ do_page_fault,        SIGSEGV, SEGV_ACCERR,        "level 2 permission fault"        },{ do_page_fault,        SIGSEGV, SEGV_ACCERR,        "level 3 permission fault"        },...
};
static int __kprobes do_page_fault(unsigned long far, unsigned long esr,struct pt_regs *regs)
{...__do_page_fault(mm, vma, addr, mm_flags, vm_flags, regs);...// 生成SIGSEGV信号,并发送信号给问题进程arm64_force_sig_fault(SIGSEGV,fault == VM_FAULT_BADACCESS ? SEGV_ACCERR : SEGV_MAPERR,far, inf->name);
}
// arch/arm64/kernel/traps.c
void arm64_force_sig_fault(int signo, int code, unsigned long far,const char *str)
{...force_sig_fault(signo, code, (void __user *)far);
}
// kernel/signal.c 
int force_sig_fault(int sig, int code, void __user *addr___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr))
{return force_sig_fault_to_task(sig, code, addr___ARCH_SI_IA64(imm, flags, isr), current);
}int force_sig_fault_to_task(int sig, int code, void __user *addr___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr), struct task_struct *t)
{// 构建signal结构体struct kernel_siginfo info;clear_siginfo(&info);info.si_signo = sig;info.si_errno = 0;info.si_code  = code;info.si_addr  = addr;// 发送信号给问题进程return force_sig_info_to_task(&info, t, HANDLER_CURRENT);
}

3.3 异常信号传递

// kernel/kernel/signal.c
// 信号传递核心逻辑
force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t,enum sig_handler handler)
{...// 1. 设置信号挂起标志、唤醒进程(如果处于睡眠状态)recalc_sigpending_and_wake(t);// 2. 给问题进程发送信号send_signal_locked(sig, info, t, PIDTYPE_PID);...
}

3.4 异常信号处理

用户进程从内核返回用户空间前,会去处理该进程的信号。跳转到Handler(信号处理函数指针),返回用户空间执行该进程注册的信号回调处理函数(debuggerd_signal_handler)。

  • 返回用户空间前的信号处理

主要做了以下事情:

1)保存当前进程的寄存器状态到用户栈上的信号帧(rt_sigframe)。

2)设置用户空间信号处理函数的参数(如 siginfo_t 和 ucontext_t)。

3)配置用户寄存器和栈指针,使进程在返回用户空间时跳转到信号处理函数执行。

如果线程设置了信号栈,会切换到该信号栈上执行(信号栈设置见4.1).

// kernel/kernel/signal.c
void do_signal(struct pt_regs *regs) {struct ksignal ksig;...if (test_thread_flag(TIF_SIGPENDING) && get_signal(&ksig)) {  // 没有待处理信号,直接返回// 处理信号:保存现场、设置信号栈、跳转到用户空间信号处理函数handle_signal(&ksig, regs);return;}...
}handle_signal(struct ksignal *ksig, struct pt_regs *regs)
{sigset_t *oldset = sigmask_to_save();int failed;/* Set up the stack frame */failed = setup_rt_frame(ksig, oldset, regs);signal_setup_done(failed, ksig, 0);
}setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
{struct rt_sigframe __user *sf;/*获取用户栈空间:如果进程设置了 sigaltstack(独立信号栈),则使用该栈;否则使用当前栈。返回一个指向用户空间 rt_sigframe 结构的指针 sf。*/sf = get_sigframe(ksig, regs, sizeof(struct rt_sigframe));/*保存用户态的寄存器状态:将内核保存的寄存器上下文(regs)复制到用户栈中的 rt_sigframe->uc_mcontext。确保在信号处理完成后可以恢复进程的原始执行状态。*/err |= stash_usr_regs(sf, regs, set);.../*配置寄存器以跳转到信号处理函数:r0: 传递信号编号(如 SIGSEGV)。ret: 修改 PC 寄存器为信号处理函数地址,强制跳转。blink: 指向 sa_restorer(通常为 _rt_sigreturn),用于信号处理完成后恢复现场。sp: 将栈指针指向新构造的信号帧顶部。*/regs->r0 = ksig->sig;// 修改PC指针为信号处理函数地址regs->ret = (unsigned long)ksig->ka.sa.sa_handler;/* 设置返回地址(restorer) */if(!(ksig->ka.sa.sa_flags & SA_RESTORER))return 1;regs->blink = (unsigned long)ksig->ka.sa.sa_restorer;/* 设置用户栈指针指向信号帧 */regs->sp = (unsigned long)sf;...return err;
}
  • 用户空间执行debuggerd_signal_handler信号回调函数

static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* context) {...// 伪线程创建crash_dump子进程,抓取debug信息,传给tombstone保存pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack,CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,&thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);...// 内核收到signal信号,若coredump功能开启,抓取coredump文件resend_signal(info);
}

 coredump实现原理可参考该文档:Linux CoreDump机制详解-CSDN博客

四、其它

  • 4.1 线程设置信号栈

    app线程被创建初始化时,会为线程设置独立的信号栈。当信号处理函数被触发时,系统会切换到这个栈上执行,避免主栈被破坏或者溢出。

// art/runtime/thread.cc
bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {...SetUpAlternateSignalStack();...
}// art/runtime/thread_linux.cc 
void Thread::SetUpAlternateSignalStack() {// Create and set an alternate signal stack.
#ifdef ART_TARGET_ANDROIDLOG(FATAL) << "Invalid use of alternate signal stack on Android";
#endifstack_t ss;ss.ss_sp = new uint8_t[kHostAltSigStackSize];ss.ss_size = kHostAltSigStackSize;ss.ss_flags = 0;CHECK(ss.ss_sp != nullptr);SigAltStack(&ss, nullptr);...
}static void SigAltStack(stack_t* new_stack, stack_t* old_stack) {// 系统调用,执行内核sigaltstack函数sigaltstack(new_stack, old_stack);
}
// kernel/signal.c
SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss)
{stack_t new, old;int err;if (uss && copy_from_user(&new, uss, sizeof(stack_t)))return -EFAULT;err = do_sigaltstack(uss ? &new : NULL, uoss ? &old : NULL,current_user_stack_pointer(),MINSIGSTKSZ);if (!err && uoss && copy_to_user(uoss, &old, sizeof(stack_t)))err = -EFAULT;return err;
}/*ss: 用户提供的新备用栈配置(类型为stack_t*)。oss: 返回当前备用栈的配置(类型为stack_t*)。sp: 当前线程的用户栈指针(用户空间的栈顶地址),用于判断是否在信号栈上执行。min_ss_size: 内核定义的最小备用栈大小(通常为8KB)。
*/
static int
do_sigaltstack (const stack_t *ss, stack_t *oss, unsigned long sp,size_t min_ss_size)
{struct task_struct *t = current;int ret = 0;if (oss) {memset(oss, 0, sizeof(stack_t));oss->ss_sp = (void __user *) t->sas_ss_sp;oss->ss_size = t->sas_ss_size;oss->ss_flags = sas_ss_flags(sp) |(current->sas_ss_flags & SS_FLAG_BITS);}// 设置新的信号栈if (ss) {void __user *ss_sp = ss->ss_sp;size_t ss_size = ss->ss_size;unsigned ss_flags = ss->ss_flags;int ss_mode;if (unlikely(on_sig_stack(sp)))return -EPERM;ss_mode = ss_flags & ~SS_FLAG_BITS;if (unlikely(ss_mode != SS_DISABLE && ss_mode != SS_ONSTACK &&ss_mode != 0))return -EINVAL;if (t->sas_ss_sp == (unsigned long)ss_sp &&t->sas_ss_size == ss_size &&t->sas_ss_flags == ss_flags)return 0;// 更新到该线程对应的task_structsigaltstack_lock();if (ss_mode == SS_DISABLE) {ss_size = 0;ss_sp = NULL;} else {if (unlikely(ss_size < min_ss_size))ret = -ENOMEM;if (!sigaltstack_size_valid(ss_size))ret = -ENOMEM;}if (!ret) {t->sas_ss_sp = (unsigned long) ss_sp;t->sas_ss_size = ss_size;t->sas_ss_flags = ss_flags;}sigaltstack_unlock();}return ret;
}

http://www.lryc.cn/news/611159.html

相关文章:

  • 【网络运维】Linux:系统启动原理与配置
  • Coze开源了!意味着什么?
  • 在Linux上部署RabbitMQ、Redis、ElasticSearch
  • 无监督学习聚类方法——K-means 聚类及应用
  • NFS CENTOS系统 安装配置
  • 走进“Mesh无线自组网”:开启智能家居和智慧工厂
  • 安科瑞智慧能源管理系统在啤酒厂5MW分布式光伏防逆流控制实践
  • uv与conda环境冲突,无法使用uv环境,安装包之后出现ModuleNotFoundError: No module named ‘xxx‘等解决方法
  • unity之 贴图很暗怎么办
  • 【STM32】HAL库中的实现(四):RTC (实时时钟)
  • python的教务管理系统
  • 江协科技STM32学习笔记1
  • Spring 的依赖注入DI是什么?
  • 【计算机网络】6应用层
  • PostgreSQL——函数
  • 【语音技术】什么是VAD
  • Windows 电脑远程访问,ZeroTier 实现内网穿透完整指南(含原理讲解)
  • NLP自然语言处理 03 Transformer架构
  • 人工智能-python-Sklearn 数据加载与处理实战
  • ChatGPT以及ChatGPT强化学习步骤
  • MLIR Bufferization
  • Linux驱动学习(八)设备树
  • 《手撕设计模式》系列导学目录
  • 防火墙安全策略练习
  • Dot1x认证原理详解
  • LeetCode 面试经典 150_数组/字符串_H 指数(9_274_C++_中等)(排序后再进行判断)(计数)
  • 三坐标测量技术解析:从基础原理到斜孔测量难点突破
  • 智慧城市SaaS平台|市容环卫管理系统
  • 微服务—OpenFeign
  • 基于PD控制器的四旋翼无人机群飞行控制系统simulink建模与仿真