本文主要介绍 ART异常处理,ART对SIGSEGV信号的拦截处理,Implicit Suspend Check的实现,以及一般的 Java Exception在ART种的检测和抛出。由于 StackOverflowError / NullPointerException的检测抛出,throw-catch的实现比较复杂,开始写到一篇文章内,发现文章太长了,后来把这3个比较复杂的处理拆分出来单独列出了。
ART异常处理机制(2) - StackOverflowError 实现
ART异常处理机制(3) - NullPointerException实现
ART异常处理机制(4) - throw & catch & finally实现
实际上 ART 种的主要的两种Exception的处理都是通过产生 SIGSEGV信号好拦截SIGSEGV信号进行实现的。所以我们接下来先弄明白 ART种对 SIGSEGV信号的拦截的处理流程。
1. FaultManager的初始化
ART中处理linux信号是通过 FaultManger来处理,对于特定的信号,先经过ART中的信号处理函数 art_fault_handler进行处理,在ART中能够识别的情况下,把这些信号转换为Java工程师能够识别的 Java Exception 抛出,以便于工程师处理异常。下面我们看下 FaultManger的实现。实际上 FaultManger实在虚拟机启动的时候,就完成了初始化,在虚拟机启动完成,即可立即处理 Java Exception。在 runtime.cc 的 Runtime::Init 函数:其中 fault_manager.Init() 初始化,会通过 signal 信号处理函数,设置拦截几个特定的信号,通过对这些信号进行特殊处理,来实现 Java Exception;下面的几个Handler的创建,实际都是通过构造函数,将自己添加到 fault_manager 的信号处理 handler 集合中,以便后续处理特定信号。bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { ... fault_manager.Init(); if (implicit_suspend_checks_) { new SuspensionHandler(&fault_manager); } if (implicit_so_checks_) { new StackOverflowHandler(&fault_manager); } if (implicit_null_checks_) { new NullPointerHandler(&fault_manager); } if (kEnableJavaStackTraceHandler) { new JavaStackTraceHandler(&fault_manager); } ... }
当前 mask中,包含 SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGSEGV 之外的所有信号。void FaultManager::Init() { CHECK(!initialized_); sigset_t mask; sigfillset(&mask); sigdelset(&mask, SIGABRT); sigdelset(&mask, SIGBUS); sigdelset(&mask, SIGFPE); sigdelset(&mask, SIGILL); sigdelset(&mask, SIGSEGV); SigchainAction sa = { .sc_sigaction = art_fault_handler, .sc_mask = mask, .sc_flags = 0UL, }; AddSpecialSignalHandlerFn(SIGSEGV, &sa); initialized_ = true; }
而 AddSpecialSignalHandlerFn中,只传递了 SIGSEGV过去。extern "C" void AddSpecialSignalHandlerFn(int signal, SigchainAction* sa) { InitializeSignalChain(); if (signal <= 0 || signal >= _NSIG) { fatal("Invalid signal %d", signal); } // Set the managed_handler. chains[signal].AddSpecialHandler(sa); chains[signal].Claim(signal); }
其中 chains是一个数组,其长度是linux 信号的个数: static SignalChain chains[_NSIG];在 InitializeSignalChain函数中,获取 sigchainlib 里的 sigaction 和 sigprocmask 函数,以便后续使用:记得Android之前的版本 libsigchain.so 是在 initrc 中通过 LD_PRELOAD 添加到 ldpath 中的,后来好像改了,修改后还没研究过。__attribute__((constructor)) static void InitializeSignalChain() { ... void* linked_sigaction = dlsym(RTLD_NEXT, "sigaction"); void* linked_sigprocmask = dlsym(RTLD_NEXT, "sigprocmask"); ... }
在 sigchain lib中实现了自己的 sigaction和 sigprocmask函数,通过类似 LD_PRELOAD 的手段,把 libsigchain.so 添加到 ldpath;使得在调用 sigaction 函数以及 sigprocmask时,会调用 libsigchain的这两个函数,而不是 libc的这两个函数。这里通过 dlsym(RTLD_NEXT,"***"),来获取 libc的这两个函数的指针,后续使用。简单来讲,就相当于 hook 了 libc的这两个函数,使得调用这两个函数的地方都会进入 libsigchain 实现的 sigaction和sigprocmask函数内。从代码中看到,通过 sigaction设置的信号会调用 libsigchain 的sigaction函数路径设置new action,如果时我们关注的信号,则没有真正设置new action到kernel,而是将其存放到该信号对应的SignalChain对应的 action_成员,用以记录 old_action,并返回该信号原来的 saved_action;若不是我们关注的信号,则还走 libc的 sigaction 函数,会真正设置new action到kernel。extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) { InitializeSignalChain(); if (signal < 0 || signal >= _NSIG) { errno = EINVAL; return -1; } if (chains[signal].IsClaimed()) { struct sigaction saved_action = chains[signal].GetAction(); if (new_action != nullptr) { chains[signal].SetAction(new_action); } if (old_action != nullptr) { *old_action = saved_action; } return 0; } // Will only get here if the signal chain has not been claimed. We want // to pass the sigaction on to the kernel via the real sigaction in libc. return linked_sigaction(signal, new_action, old_action); }
同时也 hook 了 signal() 函数,目的与 hook sigaction函数一样,只不过,当走默认路径时,并不是使用libc的 signal()函数,而是也使用 libc的sigaction函数。sigchainlib中实现的 sigprocmask也类似,目的是当有程序调用 sigprocmask设置 SIG_BLOCK 要阻塞我们关注的 signal时,要把我们关注的 signal从信号掩码中去除掉,以便影响我们的功能。
接下来的 chains[signal].AddSpecialHandlers(sa),和 Claim(signal) :SigchainAction special_handlers_[2];
void AddSpecialHandler(SigchainAction* sa) { for (SigchainAction& slot : special_handlers_) { if (slot.sc_sigaction == nullptr) { slot = *sa; return; } } fatal("too many special signal handlers"); }
这里把两个 special handler的 SigChainAction 都设置为前面 FaultManger::Init 函数中初始化的 SigChainAction,其中 sc_sigaction = art_fault_handler;Claim(SIGSEGV):void Claim(int signo) { if (!claimed_) { Register(signo); claimed_ = true; } } void Register(int signo) { struct sigaction handler_action = {}; handler_action.sa_sigaction = SignalChain::Handler; handler_action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; sigfillset(&handler_action.sa_mask); linked_sigaction(signo, &handler_action, &action_); }
可以看到在调用 Claim 函数 Register信号时,调用用过了前面的 linked_sigaction,其实就是 libc的 sigaction()函数。这里指定了 SIGSEGV信号的信号处理函数,即 void SignalChain::Handler(int signo, siginfo_t* siginfo, void* ucontext_raw) 函数来处理 SIGSEGV 信号。
总的来讲,fault_manager.Init()函数通过 sigchain中函数,设置了 SIGSEGV 信号的处理函数为 SignalChain::Handler函数,并 hook 了 libc 的 sigaction(),sigprocmask(), signal(),以及32bit时的 bsd_signal()这四个函数,防止被其他程序破坏我们的设置。
在 SignalChain::Handler() 函数中,会先调用 art_fault_handler 来先尝试处理 SIGSEGV信号,如果处理不了,会再使用 saved sigaction(default 或者应用设置的sigaction)来处理这个信号。回到 Runtime::Init()函数,fault_manager.Init()后面的 new SuspensionHandler(&fault_manager);几条语句,实际在这几个Handler的构造函数中,把它们各自都添加到了FaultManager的成员 generated_code_handlers_集合中,后面在 art_fault_hander函数中会使用这几个 Handler 尝试处理 SIGSEGV:比如:传递的第二个参数是 true:NullPointerHandler::NullPointerHandler(FaultManager* manager) : FaultHandler(manager) { manager_->AddHandler(this, true); }
到这里,已经把 NullPointerHander 添加到 generated_code_handlers_集合中;void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) { DCHECK(initialized_); if (generated_code) { generated_code_handlers_.push_back(handler); } else { other_handlers_.push_back(handler); } }
再看 art_fault_hander 函数:static bool art_fault_handler(int sig, siginfo_t* info, void* context) { return fault_manager.HandleFault(sig, info, context); }
bool FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { ... if (IsInGeneratedCode(info, context, true)) { for (const auto& handler : generated_code_handlers_) { VLOG(signals) << "invoking Action on handler " << handler; if (handler->Action(sig, info, context)) { return true; } } ... }
总结:FaultManger 初始化完成了两件事情: