uintptr_t FaultManager::GetFaultPc(siginfo_t* siginfo, void* context) { // SEGV_MTEAERR (Async MTE fault) is delivered at an arbitrary point after the actual fault. // Register contents, including PC and SP, are unrelated to the fault and can only confuse ART // signal handlers. if (siginfo->si_signo == SIGSEGV && siginfo->si_code == SEGV_MTEAERR) {
VLOG(signals) << "Async MTE fault"; return0u;
}
static uintptr_t nterp_op_aget_start() { // There are four nterp handler sets, 20KiB apart, and we're using the one that's 16KiB-aligned.
uintptr_t op_nop = reinterpret_cast<uintptr_t>(nterp3_op_nop);
uintptr_t op_aget = reinterpret_cast<uintptr_t>(nterp3_op_aget);
uintptr_t num_sets_to_subtract = (op_nop >> 12) & 3u;
DCHECK_ALIGNED(op_nop - num_sets_to_subtract * 20 * KB, 16 * KB); return op_aget - num_sets_to_subtract * 20 * KB;
}
bool NullPointerHandler::Action([[maybe_unused]] int sig, siginfo_t* info, void* context) { // If changing this function, also update CodeSimulatorArm64::HandleNullPointer as that should // stay in sync.
uintptr_t fault_address = reinterpret_cast<uintptr_t>(info->si_addr); if (!IsValidFaultAddress(fault_address)) { returnfalse;
}
// For null checks in compiled code we insert a stack map that is immediately // after the load/store instruction that might cause the fault and we need to // pass the return PC to the handler. For null checks in Nterp, we similarly // need the return PC to recognize that this was a null check in Nterp, so // that the handler can get the needed data from the Nterp frame.
if (return_pc - nterp_op_aget_start() < kNterpAgetAputHandlersSize) { // Export the dex PC here, so that the nterp `aget*`/`aput*` opcode handlers do not need // to export it before doing implicit null checks. The `EXPORT_PC` in nterp expands to // stur x22, [x25, #-0x10]
uintptr_t* dex_pc_slot = reinterpret_cast<uintptr_t*>(mc->regs[kNterpRefsReg] - kNterpExportedDexPcOffset);
*dex_pc_slot = mc->regs[kNterpDexPcReg];
}
// Push the return PC to the stack and pass the fault address in LR.
mc->sp -= sizeof(uintptr_t);
*reinterpret_cast<uintptr_t*>(mc->sp) = return_pc;
mc->regs[30] = fault_address;
// Arrange for the signal handler to return to the NPE entrypoint.
mc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
VLOG(signals) << "Generating null pointer exception"; returntrue;
}
// A suspend check is done using the following instruction: // 0x...: f94002b5 ldr x21, [x21, #0] // To check for a suspend check, we examine the instruction that caused the fault (at PC). bool SuspensionHandler::Action([[maybe_unused]] int sig,
[[maybe_unused]] siginfo_t* info, void* context) {
constexpr uint32_t checkinst = 0xf9400000 | (kSuspendCheckRegister << 5) | (kSuspendCheckRegister << 0);
ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
mcontext_t* mc = reinterpret_cast<mcontext_t*>(&uc->uc_mcontext);
if (OatQuickMethodHeader::IsNterpPc(mc->pc)) { // Interpreter does not do suspend check through the exception handler returnfalse;
}
uint32_t inst = *reinterpret_cast<uint32_t*>(mc->pc);
VLOG(signals) << "checking suspend; inst: " << std::hex << inst << " checkinst: " << checkinst; if (inst != checkinst) { // The instruction is not good, not ours. returnfalse;
}
// This is a suspend check.
VLOG(signals) << "suspend check match";
// Set LR so that after the suspend check it will resume after the // `ldr x21, [x21,#0]` instruction that triggered the suspend check.
mc->regs[30] = mc->pc + 4; // Arrange for the signal handler to return to `art_quick_implicit_suspend()`.
mc->pc = reinterpret_cast<uintptr_t>(art_quick_implicit_suspend);
// Now remove the suspend trigger that caused this fault.
Thread::Current()->RemoveSuspendTrigger();
VLOG(signals) << "removed suspend trigger invoking test suspend";
returntrue;
}
void FaultManager::SuspendFaster(siginfo_t* info, void* context) { // We need to trigger a suspend check. Since we are in an asynchronous signal handler, we // cannot do so directly. But we can make sure the next suspend check does so, rather than // waiting for the one after that, by clearing kSuspendCheckRegister. This is likely to be // beneficial immediately after a flip, when we are likely to see several signals in a row.
if (!IsInGeneratedCode(info, context)) { // Suspend trigger register is reserved for the purpose only in ART-generated code. return;
}
ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
mcontext_t* mc = reinterpret_cast<mcontext_t*>(&uc->uc_mcontext);
mc->regs[kSuspendCheckRegister] = reinterpret_cast<uintptr_t>(static_cast<void*>(nullptr));
VLOG(signals) << "Accelerating suspension";
}
bool StackOverflowHandler::Action([[maybe_unused]] int sig,
[[maybe_unused]] siginfo_t* info, void* context) {
ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
mcontext_t* mc = reinterpret_cast<mcontext_t*>(&uc->uc_mcontext);
VLOG(signals) << "stack overflow handler with sp at " << std::hex << &uc;
VLOG(signals) << "sigcontext: " << std::hex << mc;
// Check that the fault address is the value expected for a stack overflow. if (fault_addr != overflow_addr) {
VLOG(signals) << "Not a stack overflow"; returnfalse;
}
VLOG(signals) << "Stack overflow found";
// Now arrange for the signal handler to return to art_quick_throw_stack_overflow. // The value of LR must be the same as it was when we entered the code that // caused this fault. This will be inserted into a callee save frame by // the function to which this handler returns (art_quick_throw_stack_overflow).
mc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow);
// The kernel will now return to the address in sc->pc. returntrue;
}
} // namespace art
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.