Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-03-28 07:46:14

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <cfenv>
0010 #include <csignal>
0011 #include <cstddef>
0012 #include <cstdint>
0013 #include <optional>
0014 
0015 #include "FpeMonitorPlatform.hpp"
0016 #include "FpeMonitorPlatformDarwinCommon.hpp"
0017 
0018 #if !defined(__APPLE__) || !defined(__arm64__)
0019 #error "This translation unit is only valid for macOS arm64"
0020 #endif
0021 
0022 namespace ActsPlugins {
0023 
0024 namespace {
0025 
0026 // Darwin arm64 exposes separate trap-enable bits in FPCR instead of x87/MXCSR
0027 // masks. This helper translates FE_* flags into the corresponding FPCR bits.
0028 std::uint32_t darwinArm64TrapMask(int excepts) {
0029   std::uint32_t mask = 0;
0030   if ((excepts & FE_INVALID) != 0) {
0031     mask |= __fpcr_trap_invalid;
0032   }
0033   if ((excepts & FE_DIVBYZERO) != 0) {
0034     mask |= __fpcr_trap_divbyzero;
0035   }
0036   if ((excepts & FE_OVERFLOW) != 0) {
0037     mask |= __fpcr_trap_overflow;
0038   }
0039   if ((excepts & FE_UNDERFLOW) != 0) {
0040     mask |= __fpcr_trap_underflow;
0041   }
0042   if ((excepts & FE_INEXACT) != 0) {
0043     mask |= __fpcr_trap_inexact;
0044   }
0045   return mask;
0046 }
0047 
0048 std::optional<FpeType> fpeTypeFromDarwinArm64Esr(std::uint32_t esr) {
0049   // For arm64, floating-point traps may arrive as SIGILL with ESR metadata.
0050   // We only decode ESR values belonging to the floating-point exception class.
0051   constexpr std::uint32_t kEsrExceptionClassShift = 26u;
0052   constexpr std::uint32_t kEsrExceptionClassMask = 0x3fu;
0053   constexpr std::uint32_t kFpExceptionClass = 0x2cu;
0054   const std::uint32_t exceptionClass =
0055       (esr >> kEsrExceptionClassShift) & kEsrExceptionClassMask;
0056   if (exceptionClass != kFpExceptionClass) {
0057     return std::nullopt;
0058   }
0059 
0060   const std::uint32_t flags = esr & static_cast<std::uint32_t>(FE_ALL_EXCEPT);
0061   if ((flags & FE_INVALID) != 0) {
0062     return FpeType::FLTINV;
0063   }
0064   if ((flags & FE_DIVBYZERO) != 0) {
0065     return FpeType::FLTDIV;
0066   }
0067   if ((flags & FE_OVERFLOW) != 0) {
0068     return FpeType::FLTOVF;
0069   }
0070   if ((flags & FE_UNDERFLOW) != 0) {
0071     return FpeType::FLTUND;
0072   }
0073   if ((flags & FE_INEXACT) != 0) {
0074     return FpeType::FLTRES;
0075   }
0076   return std::nullopt;
0077 }
0078 
0079 }  // namespace
0080 
0081 bool detail::isRuntimeSupported() {
0082   // The arm64 Darwin implementation has dedicated handlers for trap control,
0083   // signal decoding and stack capture.
0084   return true;
0085 }
0086 
0087 std::optional<FpeType> detail::decodeFpeType(int signal, const siginfo_t* si,
0088                                              void* ctx) {
0089   // Prefer SIGFPE si_code mapping when available.
0090   if (signal == SIGFPE && si != nullptr) {
0091     return fpeTypeFromSiCode(si->si_code);
0092   }
0093 
0094   // Some arm64 floating-point traps surface as SIGILL; decode from ESR in the
0095   // interrupted context.
0096   if (signal == SIGILL && ctx != nullptr) {
0097     auto* uc = static_cast<ucontext_t*>(ctx);
0098     return fpeTypeFromDarwinArm64Esr(uc->uc_mcontext->__es.__esr);
0099   }
0100 
0101   return std::nullopt;
0102 }
0103 
0104 void detail::clearPendingExceptions(int excepts) {
0105   // Clear sticky flags before enabling traps to avoid immediate retriggering.
0106   darwin::clearPendingExceptions(excepts);
0107 }
0108 
0109 void detail::enableExceptions(int excepts) {
0110   // FPCR controls trap enablement on arm64 Darwin; FPSR carries sticky status.
0111   fenv_t env{};
0112   if (fegetenv(&env) != 0) {
0113     return;
0114   }
0115   env.__fpcr |= static_cast<unsigned long long>(darwinArm64TrapMask(excepts));
0116   env.__fpsr &= ~static_cast<unsigned long long>(FE_ALL_EXCEPT);
0117   fesetenv(&env);
0118 }
0119 
0120 void detail::disableExceptions(int excepts) {
0121   // Disable only requested trap classes and leave unrelated bits untouched.
0122   fenv_t env{};
0123   if (fegetenv(&env) != 0) {
0124     return;
0125   }
0126   env.__fpcr &= ~static_cast<unsigned long long>(darwinArm64TrapMask(excepts));
0127   fesetenv(&env);
0128 }
0129 
0130 void detail::maskTrapsInSignalContext(void* ctx, FpeType type) {
0131   // In the interrupted context, disable the current trap kind and clear all
0132   // pending floating-point status bits before resuming unwinding.
0133   const int excepts = darwin::exceptMaskForType(type);
0134   auto* uc = static_cast<ucontext_t*>(ctx);
0135   uc->uc_mcontext->__ns.__fpcr &=
0136       ~static_cast<std::uint32_t>(darwinArm64TrapMask(excepts));
0137   uc->uc_mcontext->__ns.__fpsr &= ~static_cast<std::uint32_t>(FE_ALL_EXCEPT);
0138 }
0139 
0140 std::size_t detail::captureStackFromSignalContext(void* ctx, void* buffer,
0141                                                   std::size_t bufferBytes) {
0142   // Reuse shared Darwin frame-walk logic while extracting arm64 SP/FP/PC from
0143   // the saved thread state.
0144   return darwin::captureStackFromSignalContext(
0145       ctx, buffer, bufferBytes, [](void* rawCtx) {
0146         const auto& uc = *static_cast<ucontext_t*>(rawCtx);
0147         return darwin::RegisterState{
0148             .sp = __darwin_arm_thread_state64_get_sp(uc.uc_mcontext->__ss),
0149             .fp = __darwin_arm_thread_state64_get_fp(uc.uc_mcontext->__ss),
0150             .pc = __darwin_arm_thread_state64_get_pc(uc.uc_mcontext->__ss),
0151         };
0152       });
0153 }
0154 
0155 std::size_t detail::safeDumpSkipFrames() {
0156   // Skip one synthetic frame from the dump helper in final traces.
0157   return 1;
0158 }
0159 
0160 bool detail::shouldFailFastOnUnknownSignal() {
0161   // On arm64 we expect only decodable SIGFPE/SIGILL cases. Unknown deliveries
0162   // likely indicate a corrupt or unsupported context, so fail fast.
0163   return true;
0164 }
0165 
0166 void detail::installSignalHandlers(void (*handler)(int, siginfo_t*, void*)) {
0167   // Install both SIGFPE and SIGILL handlers to cover Darwin arm64 behavior.
0168   struct sigaction action{};
0169   action.sa_sigaction = handler;
0170   action.sa_flags = SA_SIGINFO;
0171   sigaction(SIGFPE, &action, nullptr);
0172   sigaction(SIGILL, &action, nullptr);
0173 }
0174 
0175 }  // namespace ActsPlugins