Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions plugin-nativeprocessor/platforms/android/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ if(DEFINED WITH_QRCODE)
target_link_libraries(${TARGET} ZXing::ZXing)
endif(DEFINED WITH_QRCODE)

# Development only. Hides SVE from the CPU detection done inside the library so that
# KleidiCV uses its NEON code paths: Android emulators on Apple Silicon advertise SVE2
# in HWCAP although the host CPU cannot execute a single SVE instruction.
# See hwcap_sve.cpp
option(DISABLE_SVE "hide SVE from CPU detection, for Android emulators on Apple Silicon" OFF)
if(DISABLE_SVE)
add_definitions(-DDISABLE_SVE)
endif(DISABLE_SVE)

target_link_libraries(${TARGET} log jnigraphics z)

# Specifies libraries CMake should link to your target library. You
Expand Down
63 changes: 63 additions & 0 deletions plugin-nativeprocessor/platforms/android/cpp/hwcap_sve.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Development only: compiled in when the `DISABLE_SVE` CMake option is set
// (`--gradleArgs=-PdisableSVE` on the build command line). Toggling the flag alone may
// not be enough, the CLI reuses the prebuilt plugin aar until a plugin source changes.
//
// Android emulators running on Apple Silicon advertise SVE, SVE2 and friends in HWCAP
// even though the host CPU implements none of them. KleidiCV, which OpenCV uses as its
// arm64 HAL, reads HWCAP in its static initializers and picks its SVE2 kernels
// accordingly, so the first SVE instruction executed (`addvl`) raises SIGILL and kills
// the app. It shows up as a crash in `nativeCrop` through `cv::remap`.
//
// Defining `getauxval` here makes the linker bind every call coming from OpenCV and
// KleidiCV to this version instead of libc's, and hiding the SVE bits makes KleidiCV
// fall back to its NEON implementations. Never enable this for a release build: it
// would give up KleidiCV's SVE2 code paths on the devices that really support them.
#if defined(DISABLE_SVE) && defined(__aarch64__)

#include <asm/hwcap.h>
#include <dlfcn.h>
#include <sys/auxv.h>

namespace {

constexpr unsigned long kSveHwcapMask = HWCAP_SVE;

constexpr unsigned long kSveHwcap2Mask = HWCAP2_SVE2 | HWCAP2_SVEAES | HWCAP2_SVEPMULL |
HWCAP2_SVEBITPERM | HWCAP2_SVESHA3 | HWCAP2_SVESM4 |
HWCAP2_SVEI8MM | HWCAP2_SVEF32MM | HWCAP2_SVEF64MM |
HWCAP2_SVEBF16 | HWCAP2_SVE_EBF16 | HWCAP2_SVE2P1 |
HWCAP2_SVE_B16B16;

using GetAuxValFn = unsigned long (*)(unsigned long);

} // namespace

extern "C" __attribute__((visibility("hidden"))) unsigned long getauxval(unsigned long type) {
static GetAuxValFn realGetAuxVal = nullptr;
static bool resolving = false;
if (realGetAuxVal == nullptr) {
if (resolving) {
// `dlsym` called us back before we could resolve: report no CPU feature at all
return 0;
}
resolving = true;
auto resolved = reinterpret_cast<GetAuxValFn>(dlsym(RTLD_DEFAULT, "getauxval"));
resolving = false;
if (resolved == nullptr || resolved == &getauxval) {
// never call ourselves: this function is hidden so `dlsym` should not see it,
// but a mistake here would recurse until the stack blows up
return 0;
}
realGetAuxVal = resolved;
}
const unsigned long value = realGetAuxVal(type);
if (type == AT_HWCAP) {
return value & ~kSveHwcapMask;
}
if (type == AT_HWCAP2) {
return value & ~kSveHwcap2Mask;
}
return value;
}

#endif // DISABLE_SVE && __aarch64__
10 changes: 7 additions & 3 deletions plugin-nativeprocessor/platforms/android/include.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ android {
cmake {
cppFlags "-frtti -fexceptions"
abiFilters 'armeabi-v7a', "arm64-v8a", 'x86', 'x86_64'
def cmakeArguments = ["-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", "-DANDROID_TOOLCHAIN=clang", "-DANDROID_STL=c++_static"]
if (project.hasProperty("withQRCode")) {
arguments "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", "-DANDROID_TOOLCHAIN=clang", "-DANDROID_STL=c++_static", "-DWITH_QRCODE=1"
} else {
arguments "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", "-DANDROID_TOOLCHAIN=clang", "-DANDROID_STL=c++_static"
cmakeArguments << "-DWITH_QRCODE=1"
}
// development only, see plugin-nativeprocessor/platforms/android/cpp/hwcap_sve.cpp
// always passed explicitly: a value left in the CMake cache would survive
// the flag being dropped and silently disable SVE in a release build
cmakeArguments << ("-DDISABLE_SVE=" + (project.hasProperty("disableSVE") ? "1" : "0"))
arguments(*cmakeArguments)
}
}
}
Expand Down
Loading