ART异常处理机制(1) - SIGSEGV信号的拦截和处理

本文主要介绍 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 函数:
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);
      }
   ...

}
其中 fault_manager.Init() 初始化,会通过 signal 信号处理函数,设置拦截几个特定的信号,通过对这些信号进行特殊处理,来实现 Java Exception;下面的几个Handler的创建,实际都是通过构造函数,将自己添加到 fault_manager 的信号处理 handler 集合中,以便后续处理特定信号。
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;
}
当前 mask中,包含 SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGSEGV 之外的所有信号。
而 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 函数,以便后续使用:
__attribute__((constructor)) static void InitializeSignalChain() {
   ...
    void* linked_sigaction = dlsym(RTLD_NEXT, "sigaction");
    void* linked_sigprocmask = dlsym(RTLD_NEXT, "sigprocmask");
   ...

}
记得Android之前的版本 libsigchain.so 是在 initrc 中通过 LD_PRELOAD 添加到 ldpath 中的,后来好像改了,修改后还没研究过。
在 sigchain lib中实现了自己的 sigaction和 sigprocmask函数,通过类似 LD_PRELOAD 的手段,把 libsigchain.so 添加到 ldpath;使得在调用 sigaction 函数以及 sigprocmask时,会调用 libsigchain的这两个函数,而不是 libc的这两个函数。
这里通过 dlsym(RTLD_NEXT,"***"),来获取 libc的这两个函数的指针,后续使用。
简单来讲,就相当于 hook 了 libc的这两个函数,使得调用这两个函数的地方都会进入 libsigchain 实现的 sigaction和sigprocmask函数内。
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);
}
从代码中看到,通过 sigaction设置的信号会调用 libsigchain 的sigaction函数路径设置new action,如果时我们关注的信号,则没有真正设置new action到kernel,而是将其存放到该信号对应的SignalChain对应的 action_成员,用以记录 old_action,并返回该信号原来的 saved_action;若不是我们关注的信号,则还走 libc的 sigaction 函数,会真正设置new action到kernel。
同时也 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:
比如:
NullPointerHandler::NullPointerHandler(FaultManager* manager) : FaultHandler(manager) {
  manager_->AddHandler(this, true);
}
传递的第二个参数是 true:
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);
  }
}
到这里,已经把 NullPointerHander 添加到  generated_code_handlers_集合中;
再看 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 初始化完成了两件事情:
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值