Repository: open-obfuscator/o-mvll
Pull request #86: Support AndroidNDK tests on macOS hosts
Author: login: weliveindetail
Created: 2025-05-09T13:40:27Z | Merged: 2025-05-12T15:59:00Z

Adding Android as a target on macOS hosts means that we support a pure cross-compiler now, because clang from the AndroidNDK doesn't target macOS as a platform. Strictly speaking, this was the case on Linux already, because only `*-linux-android` targets are officially supported in the NDK, but `*-linux-gnu` was sufficiently close that we didn't worry.

This affects the JIT compilation parts of the plugin and the StringEncoding pass in particular: while the decode function is injected into the target binary, we used to JIT compile the encode function and run it at compile time. This is not possible anymore, because we cannot rely on NDK clang (i.e. its LLVM fork) to produce correct code for macOS.

Instead, we consider a new approach in two steps:
1. Keep a number of basic encode/decode functions hardcoded, but only provide decode() as a string. The encode() functions are implemented as builtin function of the plugin.
2. Allow to add further encode/decode pairs through the Pyhton config.

This patch implements the 1. step and prepares tests for running properly on macOS with both, Xcode and NDK.


Diff:
diff --git a/src/include/omvll/passes/string-encoding/Routines.h b/src/include/omvll/passes/string-encoding/Routines.h
new file mode 100644
index 00000000..db423fcb
--- /dev/null
+++ b/src/include/omvll/passes/string-encoding/Routines.h
@@ -0,0 +1,15 @@
+#pragma once
+
+//
+// This file is distributed under the Apache License v2.0. See LICENSE for
+// details.
+//
+
+namespace omvll {
+
+using EncRoutineFn = void(char *, const char *, unsigned long long, int);
+EncRoutineFn *getEncodeRoutine(unsigned Idx);
+const char *getDecodeRoutine(unsigned Idx);
+unsigned getNumEncodeDecodeRoutines();
+
+} // end namespace omvll
diff --git a/src/include/omvll/passes/string-encoding/StringEncoding.hpp b/src/include/omvll/passes/string-encoding/StringEncoding.hpp
index c6ab4a37..8028c668 100644
--- a/src/include/omvll/passes/string-encoding/StringEncoding.hpp
+++ b/src/include/omvll/passes/string-encoding/StringEncoding.hpp
@@ -13,6 +13,7 @@
 #include "llvm/IR/PassManager.h"
 #include "llvm/Support/RandomNumberGenerator.h"
 
+#include "omvll/passes/string-encoding/Routines.h"
 #include "omvll/passes/string-encoding/StringEncodingOpt.hpp"
 
 // Forward declarations
@@ -29,8 +30,6 @@ class Jitter;
 
 // See https://obfuscator.re/omvll/passes/strings-encoding for details.
 struct StringEncoding : llvm::PassInfoMixin<StringEncoding> {
-  using EncRoutineFn = void (*)(uint8_t *Out, const char *In, uint64_t Key,
-                                int Len);
   using KeyBufferTy = std::vector<uint8_t>;
   using KeyIntTy = uint64_t;
   using KeyTy = std::variant<std::monostate, KeyBufferTy, KeyIntTy>;
@@ -49,7 +48,7 @@ struct StringEncoding : llvm::PassInfoMixin<StringEncoding> {
     EncodingTy Type = EncodingTy::None;
     KeyTy Key;
     std::unique_ptr<llvm::Module> TM;
-    std::unique_ptr<llvm::Module> HM;
+    EncRoutineFn *EncodeFn;
   };
 
   llvm::PreservedAnalyses run(llvm::Module &M,
@@ -85,8 +84,6 @@ struct StringEncoding : llvm::PassInfoMixin<StringEncoding> {
   }
 
 private:
-  static inline Jitter *HostJIT = nullptr;
-
   void genRoutines(const llvm::Triple &Triple, EncodingInfo &EI,
                    llvm::LLVMContext &Ctx);
   void annotateRoutine(llvm::Module &M);
diff --git a/src/passes/string-encoding/CMakeLists.txt b/src/passes/string-encoding/CMakeLists.txt
index 46d1ae0f..7747f745 100644
--- a/src/passes/string-encoding/CMakeLists.txt
+++ b/src/passes/string-encoding/CMakeLists.txt
@@ -1,5 +1,6 @@
 target_sources(OMVLL PRIVATE
   ${CMAKE_CURRENT_SOURCE_DIR}/StringEncoding.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/Routines.cpp
 )
 
 set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/StringEncoding.cpp
diff --git a/src/passes/string-encoding/Routines.cpp b/src/passes/string-encoding/Routines.cpp
new file mode 100644
index 00000000..908d16f0
--- /dev/null
+++ b/src/passes/string-encoding/Routines.cpp
@@ -0,0 +1,68 @@
+//
+// This file is distributed under the Apache License v2.0. See LICENSE for
+// details.
+//
+
+#include "omvll/passes/string-encoding/Routines.h"
+#include <cassert>
+
+namespace {
+
+const char *DecodeRoutines[] = {
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    }
+  )delim",
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }
+  )delim",
+};
+
+omvll::EncRoutineFn *EncodeRoutines[] = {
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    },
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }};
+
+} // end anonymous namespace
+
+namespace omvll {
+
+template <typename T, unsigned N>
+static constexpr unsigned arraySize(const T (&)[N]) noexcept {
+  return N;
+}
+
+// Encode and decode functions must match in pairs
+static_assert(arraySize(EncodeRoutines) == arraySize(DecodeRoutines));
+
+unsigned getNumEncodeDecodeRoutines() { return arraySize(EncodeRoutines); }
+
+EncRoutineFn *getEncodeRoutine(unsigned Idx) {
+  assert(Idx < arraySize(EncodeRoutines));
+  return EncodeRoutines[Idx];
+}
+
+const char *getDecodeRoutine(unsigned Idx) {
+  assert(Idx < arraySize(DecodeRoutines));
+  return DecodeRoutines[Idx];
+}
+
+} // end namespace omvll
diff --git a/src/passes/string-encoding/StringEncoding.cpp b/src/passes/string-encoding/StringEncoding.cpp
index 00732e84..9d95701f 100644
--- a/src/passes/string-encoding/StringEncoding.cpp
+++ b/src/passes/string-encoding/StringEncoding.cpp
@@ -48,37 +48,6 @@ static constexpr auto CtorPrefixName = "__omvll_ctor_";
 
 namespace omvll {
 
-static const std::vector<std::string> Routines = {
-    R"delim(
-    void encode(char *out, char *in, unsigned long long key, int size) {
-      unsigned char *raw_key = (unsigned char*)(&key);
-      for (int i = 0; i < size; ++i) {
-        out[i] = in[i] ^ raw_key[i % sizeof(key)];
-      }
-    }
-    void decode(char *out, char *in, unsigned long long key, int size) {
-      unsigned char *raw_key = (unsigned char*)(&key);
-      for (int i = 0; i < size; ++i) {
-        out[i] = in[i] ^ raw_key[i % sizeof(key)];
-      }
-    }
-  )delim",
-    R"delim(
-    void encode(char *out, char *in, unsigned long long key, int size) {
-      unsigned char *raw_key = (unsigned char*)(&key);
-      for (int i = 0; i < size; ++i) {
-        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
-      }
-    }
-    void decode(char *out, char *in, unsigned long long key, int size) {
-      unsigned char *raw_key = (unsigned char*)(&key);
-      for (int i = 0; i < size; ++i) {
-        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
-      }
-    }
-  )delim",
-};
-
 inline bool isEligible(const GlobalVariable &G) {
   if (G.isNullValue() || G.isZeroValue())
     return false;
@@ -483,18 +452,12 @@ bool StringEncoding::processGlobal(Instruction &I, Use &Op, GlobalVariable &G,
 
   SDEBUG("Key for {}: 0x{:010x}", Str.str(), Key);
 
-  std::vector<uint8_t> Encoded(StrSz);
+  std::vector<char> Encoded(StrSz);
   EncodingInfo EI(EncodingTy::Global);
   EI.Key = Key;
   genRoutines(Triple(M->getTargetTriple()), EI, Ctx);
 
-  auto JIT = StringEncoding::HostJIT->compile(*EI.HM);
-  if (auto EncodeSym = JIT->lookup("encode")) {
-    auto EncodeFn = reinterpret_cast<EncRoutineFn>(EncodeSym->getValue());
-    EncodeFn(Encoded.data(), Str.data(), Key, StrSz);
-  } else {
-    fatalError("Cannot find encode routine");
-  }
+  EI.EncodeFn(Encoded.data(), Str.data(), Key, StrSz);
 
   Constant *StrEnc = ConstantDataArray::get(Ctx, Encoded);
   G.setConstant(false);
@@ -529,22 +492,15 @@ bool StringEncoding::processGlobal(Instruction &I, Use &Op, GlobalVariable &G,
 
 void StringEncoding::genRoutines(const Triple &TargetTriple, EncodingInfo &EI,
                                  LLVMContext &Ctx) {
-  Triple HostTriple(sys::getProcessTriple());
-  if (!HostJIT)
-    HostJIT = new Jitter(HostTriple.getTriple());
-
-  std::uniform_int_distribution<size_t> Dist(0, Routines.size() - 1);
+  unsigned NumBuiltinRoutines = getNumEncodeDecodeRoutines();
+  std::uniform_int_distribution<size_t> Dist(0, NumBuiltinRoutines - 1);
   size_t Idx = Dist(*RNG);
 
-  ExitOnError ExitOnErr("Nested clang invocation failed: ");
-  std::string Routine =
-      (Twine("extern \"C\" {\n") + Routines[Idx] + "}\n").str();
+  EI.EncodeFn = getEncodeRoutine(Idx);
 
-  {
-    EI.HM = ExitOnErr(generateModule(Routine, HostTriple, "cpp",
-                                     HostJIT->getContext(),
-                                     {"-std=c++17", "-fno-stack-check"}));
-  }
+  ExitOnError ExitOnErr("Nested clang invocation failed: ");
+  const char *DecodeFn = getDecodeRoutine(Idx);
+  std::string Routine = (Twine("extern \"C\" {\n") + DecodeFn + "}\n").str();
 
   {
     Ctx.setDiscardValueNames(false);
@@ -582,19 +538,12 @@ bool StringEncoding::processLocal(Instruction &I, Use &Op, GlobalVariable &G,
 
   SDEBUG("Key for {}: 0x{:010x}", Str.str(), Key);
 
-  std::vector<uint8_t> Encoded(StrSz);
+  std::vector<char> Encoded(StrSz);
   EncodingInfo EI(EncodingTy::Local);
   EI.Key = Key;
 
   genRoutines(Triple(I.getModule()->getTargetTriple()), EI, Ctx);
-
-  auto JIT = StringEncoding::HostJIT->compile(*EI.HM);
-  if (auto EncodeSym = JIT->lookup("encode")) {
-    auto EncodeFn = reinterpret_cast<EncRoutineFn>(EncodeSym->getValue());
-    EncodeFn(Encoded.data(), Str.data(), Key, StrSz);
-  } else {
-    fatalError("Cannot find encode routine");
-  }
+  EI.EncodeFn(Encoded.data(), Str.data(), Key, StrSz);
 
   Constant *StrEnc = ConstantDataArray::get(Ctx, Encoded);
   G.setInitializer(StrEnc);
diff --git a/src/test/lit.cfg.py b/src/test/lit.cfg.py
index ac0e1507..112aef08 100644
--- a/src/test/lit.cfg.py
+++ b/src/test/lit.cfg.py
@@ -26,30 +26,48 @@
 if sys.platform == 'win32':
     config.available_features.add('host-platform-windows')
 
-# Determine host architecture
-host_arch = platform.machine()
-
 # Allow tests to be based on the host architecture
+host_arch = platform.machine()
 if host_arch == 'x86_64':
     config.available_features.add('host-arch-x86')
 elif host_arch == 'arm64':
     config.available_features.add('host-arch-arm64')
 
-# The tools directory defaults to LLVM_BINARY_DIR. In order to run tests with a different compiler,
-# pass the installation base path via LLVM_TOOLS_DIR at configuration time explicitly.
+# For Android tests, use clang from AndroidNDK.
+if config.omvll_plugin_abi == 'Android' or \
+   config.omvll_plugin_abi == 'CustomAndroid':
+    config.available_features.add('android_abi')
+
+    clang_exe = os.path.join(config.llvm_bin_dir, 'clang')
+    if not os.path.exists(clang_exe):
+        print("clang compiler not found:", clang_exe)
+        exit(1)
+    print("Testing compiler:", clang_exe)
+    clang_path = config.llvm_bin_dir
+
 # For iOS tests, always use Apple Clang as default compiler.
-if sys.platform == 'darwin':
+if config.omvll_plugin_abi == 'Apple':
+    config.available_features.add('apple_abi')
+
     try:
         xcode_path = subprocess.check_output(['xcode-select', '-p'], stderr=subprocess.PIPE).strip().decode()
-        compiler_dir_path = os.path.join(xcode_path, 'Toolchains/XcodeDefault.xctoolchain/usr/bin')
     except (subprocess.CalledProcessError, OSError):
         print("xcode-select not found. Please install Xcode.")
         exit(1)
-else:
-    compiler_dir_path = config.llvm_tools_dir
+    clang_path = os.path.join(xcode_path, 'Toolchains/XcodeDefault.xctoolchain/usr/bin')
+    clang_exe = os.path.join(clang_path, 'clang')
+    print("Testing compiler:", clang_exe)
 
-print("Running tests with:", os.path.join(compiler_dir_path, 'clang'))
-llvm_config.add_tool_substitutions(["clang", "clang++"], compiler_dir_path)
+    try:
+        cmd = ["xcrun", "--show-sdk-path", "--sdk", "iphoneos"]
+        ios_sdk = subprocess.check_output(cmd, stderr=subprocess.PIPE).strip().decode()
+    except (subprocess.CalledProcessError, OSError):
+        print("xcrun not found. Please run command: xcode-select --install")
+        exit(1)
+    print("Using iOS SDK:", ios_sdk)
+    config.substitutions.append(('%EXTRA_CC_FLAGS', f"-isysroot {ios_sdk}"))
+
+llvm_config.add_tool_substitutions(["clang", "clang++"], clang_path)
 llvm_config.add_tool_substitutions(["FileCheck", "count", "not"], config.llvm_tools_dir)
 
 # The plugin is a shared library in our build-tree
@@ -59,22 +77,6 @@
 
 print("Available features are:", config.available_features)
 
-extra_linker_flags = ''
-extra_cc_flags = ''
-if sys.platform == 'darwin':
-    try:
-        cmd = ["xcrun", "--show-sdk-path", "--sdk", "iphoneos"]
-        sdk_path = subprocess.check_output(cmd, stderr=subprocess.PIPE).strip().decode()
-        print("Using SDKROOT:", sdk_path)
-        extra_linker_flags = '-Wl,-L{}/usr/lib -Wl,-lSystem'.format(sdk_path)
-        extra_cc_flags = '-isysroot {}'.format(sdk_path)
-    except (subprocess.CalledProcessError, OSError):
-        print("xcrun not found. Please run command: xcode-select --install")
-        exit(1)
-
-config.substitutions.append(('%EXTRA_LINKER_FLAGS', extra_linker_flags))
-config.substitutions.append(('%EXTRA_CC_FLAGS', extra_cc_flags))
-
 # We need this to find the Python standard library
 if 'OMVLL_PYTHONPATH' in os.environ:
     print("OMVLL_PYTHONPATH:", os.environ['OMVLL_PYTHONPATH'])
diff --git a/src/test/lit.site.cfg.py.in b/src/test/lit.site.cfg.py.in
index c107dfd4..e67253cb 100644
--- a/src/test/lit.site.cfg.py.in
+++ b/src/test/lit.site.cfg.py.in
@@ -5,6 +5,7 @@
 import lit.llvm
 
 config.omvll_plugin_dir = "@PROJECT_BINARY_DIR@"
+config.omvll_plugin_abi = "@OMVLL_ABI@"
 config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@"
 config.llvm_bin_dir = "@LLVM_BINARY_DIR@/bin"
 config.llvm_tools_dir = "@LLVM_TOOLS_DIR@/bin"
diff --git a/src/test/passes/arithmetic/xor-aarch64-android.c b/src/test/passes/arithmetic/xor-aarch64-android.c
index b949fe98..93620e90 100644
--- a/src/test/passes/arithmetic/xor-aarch64-android.c
+++ b/src/test/passes/arithmetic/xor-aarch64-android.c
@@ -3,8 +3,7 @@
 // details.
 //
 
-// REQUIRES: aarch64-registered-target
-// XFAIL: host-platform-macOS
+// REQUIRES: aarch64-registered-target && android_abi
 
 // RUN:                                        clang -target aarch64-linux-android                         -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=R0 %s
 // RUN: env OMVLL_CONFIG=%S/config_rounds_0.py clang -target aarch64-linux-android -fpass-plugin=%libOMVLL -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=R0 %s
diff --git a/src/test/passes/arithmetic/xor-aarch64-ios.c b/src/test/passes/arithmetic/xor-aarch64-ios.c
index 1ee40e53..5f60ed86 100644
--- a/src/test/passes/arithmetic/xor-aarch64-ios.c
+++ b/src/test/passes/arithmetic/xor-aarch64-ios.c
@@ -3,8 +3,7 @@
 // details.
 //
 
-// REQUIRES: aarch64-registered-target
-// XFAIL: host-platform-linux
+// REQUIRES: aarch64-registered-target && apple_abi
 
 // RUN:                                        clang -target arm64-apple-ios                         -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=R0 %s
 // RUN: env OMVLL_CONFIG=%S/config_rounds_0.py clang -target arm64-apple-ios -fpass-plugin=%libOMVLL -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=R0 %s
diff --git a/src/test/passes/arithmetic/xor-arm-android.c b/src/test/passes/arithmetic/xor-arm-android.c
index d8e906dd..fac3d9a0 100644
--- a/src/test/passes/arithmetic/xor-arm-android.c
+++ b/src/test/passes/arithmetic/xor-arm-android.c
@@ -3,7 +3,7 @@
 // details.
 //
 
-// REQUIRES: arm-registered-target
+// REQUIRES: arm-registered-target && android_abi
 
 // RUN:                                        clang -target arm-linux-android                         -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=R0 %s
 // RUN: env OMVLL_CONFIG=%S/config_rounds_2.py clang -target arm-linux-android -fpass-plugin=%libOMVLL -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=R2 %s
diff --git a/src/test/passes/arithmetic/xor-x86_64-darwin.c b/src/test/passes/arithmetic/xor-x86_64-darwin.c
index 816f254f..05e3591c 100644
--- a/src/test/passes/arithmetic/xor-x86_64-darwin.c
+++ b/src/test/passes/arithmetic/xor-x86_64-darwin.c
@@ -3,12 +3,10 @@
 // details.
 //
 
-// REQUIRES: x86-registered-target
-// XFAIL: host-platform-linux
+// REQUIRES: x86-registered-target && apple_abi
 
 // RUN:                                        clang -target x86_64-apple-darwin                         -O1 -S %s -o - | FileCheck --check-prefix=R0 %s
 // RUN: env OMVLL_CONFIG=%S/config_rounds_0.py clang -target x86_64-apple-darwin -fpass-plugin=%libOMVLL -O1 -S %s -o - | FileCheck --check-prefix=R0 %s
-// RUN: env OMVLL_CONFIG=%S/config_rounds_1.py clang -target x86_64-apple-darwin -fpass-plugin=%libOMVLL -O1 -S %s -o - | FileCheck --check-prefix=R1 %s
 // RUN: env OMVLL_CONFIG=%S/config_rounds_2.py clang -target x86_64-apple-darwin -fpass-plugin=%libOMVLL -O1 -S %s -o - | FileCheck --check-prefix=R2 %s
 // RUN: env OMVLL_CONFIG=%S/config_rounds_3.py clang -target x86_64-apple-darwin -fpass-plugin=%libOMVLL -O1 -S %s -o - | FileCheck --check-prefix=R3 %s
 
@@ -21,16 +19,6 @@
 // R0:           cmpq	%rcx, %rax
 // R0:           jne	LBB0_2
 
-// FIXME: It seems like rounds=1 doesn't actually do anything!
-// R1-LABEL: _memcpy_xor:
-// R1:       LBB0_2:
-// R1:           movzbl	(%rsi,%rcx), %edx
-// R1:           xorb	$35, %dl
-// R1:           movb	%dl, (%rdi,%rcx)
-// R1:           incq	%rcx
-// R1:           cmpq	%rcx, %rax
-// R1:           jne	LBB0_2
-
 // R2-LABEL: _memcpy_xor:
 // R2:       LBB0_2:
 // R2:           movl	%eax, %ecx
diff --git a/src/test/passes/arithmetic/xor-x86_64-linux.c b/src/test/passes/arithmetic/xor-x86_64-linux.c
index e792d95f..5414aeb6 100644
--- a/src/test/passes/arithmetic/xor-x86_64-linux.c
+++ b/src/test/passes/arithmetic/xor-x86_64-linux.c
@@ -3,8 +3,7 @@
 // details.
 //
 
-// REQUIRES: x86-registered-target
-// XFAIL: host-platform-macOS
+// REQUIRES: x86-registered-target && android_abi
 
 // RUN:                                        clang -target x86_64-pc-linux-gnu                         -O1 -S %s -o - | FileCheck --check-prefix=R0 %s
 // RUN: env OMVLL_CONFIG=%S/config_rounds_0.py clang -target x86_64-pc-linux-gnu -fpass-plugin=%libOMVLL -O1 -S %s -o - | FileCheck --check-prefix=R0 %s
diff --git a/src/test/passes/break-cfg/basic-aarch64-ios.c b/src/test/passes/break-cfg/basic-aarch64-ios.c
index 2eaaae9e..a4761ca7 100644
--- a/src/test/passes/break-cfg/basic-aarch64-ios.c
+++ b/src/test/passes/break-cfg/basic-aarch64-ios.c
@@ -3,8 +3,7 @@
 // details.
 //
 
-// REQUIRES: aarch64-registered-target
-// XFAIL: host-platform-linux
+// REQUIRES: aarch64-registered-target && apple_abi
 
 // RUN:                                   clang -target arm64-apple-ios                         -O1 -fno-verbose-asm -S %s -o - | FileCheck %s
 // RUN: env OMVLL_CONFIG=%S/config_all.py clang -target arm64-apple-ios -fpass-plugin=%libOMVLL -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=BREAKCFG-IOS %s
diff --git a/src/test/passes/cfg-flattening/basic-aarch64-android.c b/src/test/passes/cfg-flattening/basic-aarch64-android.c
index b46386c8..8d4208c5 100644
--- a/src/test/passes/cfg-flattening/basic-aarch64-android.c
+++ b/src/test/passes/cfg-flattening/basic-aarch64-android.c
@@ -3,8 +3,7 @@
 // details.
 //
 
-// REQUIRES: aarch64-registered-target
-// XFAIL: host-platform-macOS
+// REQUIRES: aarch64-registered-target && android_abi
 
 // RUN:                                   clang -target aarch64-linux-android                         -O1 -fno-verbose-asm -S %s -o -
 // RUN: env OMVLL_CONFIG=%S/config_all.py clang -target aarch64-linux-android -fpass-plugin=%libOMVLL -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=FLAT-ANDROID %s
diff --git a/src/test/passes/cfg-flattening/basic-aarch64-ios.c b/src/test/passes/cfg-flattening/basic-aarch64-ios.c
index ab4f0f92..04e7e54c 100644
--- a/src/test/passes/cfg-flattening/basic-aarch64-ios.c
+++ b/src/test/passes/cfg-flattening/basic-aarch64-ios.c
@@ -3,8 +3,7 @@
 // details.
 //
 
-// REQUIRES: aarch64-registered-target
-// XFAIL: host-platform-linux
+// REQUIRES: aarch64-registered-target && apple_abi
 
 // RUN:                                   clang -target arm64-apple-ios                          -O1 -fno-verbose-asm -S %s -o -
 // RUN: env OMVLL_CONFIG=%S/config_all.py clang -target arm64-apple-ios  -fpass-plugin=%libOMVLL -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=FLAT-IOS %s
diff --git a/src/test/passes/cfg-flattening/basic-arm-android.c b/src/test/passes/cfg-flattening/basic-arm-android.c
index 92d7c4ac..3369bde9 100644
--- a/src/test/passes/cfg-flattening/basic-arm-android.c
+++ b/src/test/passes/cfg-flattening/basic-arm-android.c
@@ -3,7 +3,7 @@
 // details.
 //
 
-// REQUIRES: arm-registered-target
+// REQUIRES: arm-registered-target && android_abi
 
 // RUN:                                   clang -target arm-linux-android                         -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=NO-FLAT %s
 // RUN: env OMVLL_CONFIG=%S/config_all.py clang -target arm-linux-android -fpass-plugin=%libOMVLL -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=FLAT %s
diff --git a/src/test/passes/cfg-flattening/basic-native.c b/src/test/passes/cfg-flattening/basic-native.c
deleted file mode 100644
index 69f68c4e..00000000
--- a/src/test/passes/cfg-flattening/basic-native.c
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// This file is distributed under the Apache License v2.0. See LICENSE for
-// details.
-//
-
-// TODO: Require that the native target is registered.
-// TODO: Make sure Clang finds a linker on our host machine.
-
-// Compilation can fail, e.g. if we insert invalid inline assembly.
-// RUN:                                   clang                         %EXTRA_LINKER_FLAGS -O1 %s -o %T/basic-native
-// RUN: env OMVLL_CONFIG=%S/config_all.py clang -fpass-plugin=%libOMVLL %EXTRA_LINKER_FLAGS -O1 %s -o %T/basic-native-obf
-
-// This execution test only fails, if the below C code is invalid.
-// RUN: %T/basic-native right
-// RUN: not %T/basic-native wrong
-
-// This execution test may fail, if our obfuscation is invalid.
-// RUN: %T/basic-native-obf right
-// RUN: not %T/basic-native-obf wrong
-
-int check_password(const char *passwd) {
-  if (passwd[0] == 'r') {
-    if (passwd[1] == 'i') {
-      if (passwd[2] == 'g') {
-        if (passwd[3] == 'h') {
-          if (passwd[4] == 't') {
-            return 0;
-          }
-        }
-      }
-    }
-  }
-  return 1;
-}
-
-int main(int argc, char *argv[]) {
-  if (argc <= 1)
-    return 2;
-  return check_password(argv[1]);
-}
diff --git a/src/test/passes/string-encoding/basic-arm-android.cpp b/src/test/passes/string-encoding/basic-arm-android.cpp
index da262d9a..6f2367b5 100644
--- a/src/test/passes/string-encoding/basic-arm-android.cpp
+++ b/src/test/passes/string-encoding/basic-arm-android.cpp
@@ -3,7 +3,7 @@
 // details.
 //
 
-// REQUIRES: arm-registered-target
+// REQUIRES: arm-registered-target && android_abi
 
 // The default object contains the file-name string:
 //     RUN: clang++ -target arm-linux-android -O1 -c %s -o - | strings | FileCheck --check-prefix=CHECK-DEFAULT  -DFILE_NAME=%s %s
diff --git a/src/test/passes/string-encoding/basic-ios-swift.ll b/src/test/passes/string-encoding/basic-ios-swift.ll
index f8c209ed..a245062c 100644
--- a/src/test/passes/string-encoding/basic-ios-swift.ll
+++ b/src/test/passes/string-encoding/basic-ios-swift.ll
@@ -2,7 +2,7 @@
 ; This file is distributed under the Apache License v2.0. See LICENSE for details.
 ;
 
-; REQUIRES: aarch64-registered-target
+; REQUIRES: aarch64-registered-target && apple_abi
 
 ; The 'replace' configuration encodes the string and adds logic that decodes it at load-time:
 ;     RUN: env OMVLL_CONFIG=%S/config_replace.py clang++ -fpass-plugin=%libOMVLL \


Reviews:
user: login: antoniofrighetto
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T08:26:59Z
user: login: antoniofrighetto
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T08:27:55Z
user: login: weliveindetail
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T09:54:11Z
user: login: antoniofrighetto
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T10:28:11Z
user: login: antoniofrighetto
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T10:28:24Z
user: login: weliveindetail
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T13:25:01Z
user: login: weliveindetail
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T13:28:06Z

Review comments:
diff_hunk: @@ -0,0 +1,8 @@
+namespace omvll {
+
+using EncRoutineFn = void(char *, const char *, unsigned long long, int);
+EncRoutineFn *getEncodeRoutine(unsigned Idx);
+const char *getDecodeRoutine(unsigned Idx);
+unsigned getNumEncodeDecodeRoutines();
+
+} // namespace omvll
path: src/include/omvll/passes/string-encoding/Routines.h
user: login: antoniofrighetto
body: ```suggestion
} // end namespace omvll
```
created_at: 2025-05-12T08:23:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,8 @@
+namespace omvll {
path: src/include/omvll/passes/string-encoding/Routines.h
user: login: antoniofrighetto
body: Missing license here and in cpp file.
created_at: 2025-05-12T08:23:28Z
author_association: COLLABORATOR
diff_hunk: @@ -49,7 +48,7 @@ struct StringEncoding : llvm::PassInfoMixin<StringEncoding> {
     EncodingTy Type = EncodingTy::None;
     KeyTy Key;
     std::unique_ptr<llvm::Module> TM;
-    std::unique_ptr<llvm::Module> HM;
+    EncRoutineFn *BuiltinFn;
path: src/include/omvll/passes/string-encoding/StringEncoding.hpp
user: login: antoniofrighetto
body: ```suggestion
    EncRoutineFn *EncodeFn;
```
created_at: 2025-05-12T08:24:04Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,63 @@
+#include "omvll/passes/string-encoding/Routines.h"
+#include <cassert>
+
+namespace {
+
+const char *DecodeRoutines[] = {
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    }
+  )delim",
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }
+  )delim",
+};
+
+omvll::EncRoutineFn *EncodeRoutines[] = {
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    },
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }};
+
+} // namespace
+
+namespace omvll {
+
+template <typename T, unsigned N>
+static constexpr unsigned arraySize(const T (&)[N]) noexcept {
+  return N;
+}
+
+// Encode and decode functions must match in pairs
+static_assert(arraySize(EncodeRoutines) == arraySize(DecodeRoutines));
path: src/passes/string-encoding/Routines.cpp
user: login: antoniofrighetto
body: How about something like:
```cpp
struct RoutinePair {
    EncRoutineFn *Encode;
    const char *Decode;
};
static constexpr std::array<RoutinePair, 2> Routines = { ... };
```
Then you could have the following without the need of the static_assert and arraySize template: 
```cpp
unsigned getNumRoutines() { return std::size(Routines); }
EncRoutineFn *getEncodeRoutine(unsigned Idx) { return Routines[Idx].Encode; }
const char *getDecodeRoutine(unsigned Idx) { return Routines[Idx].Decode; }
```
created_at: 2025-05-12T08:25:36Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,63 @@
+#include "omvll/passes/string-encoding/Routines.h"
+#include <cassert>
+
+namespace {
+
+const char *DecodeRoutines[] = {
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    }
+  )delim",
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }
+  )delim",
+};
+
+omvll::EncRoutineFn *EncodeRoutines[] = {
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    },
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }};
+
+} // namespace
path: src/passes/string-encoding/Routines.cpp
user: login: antoniofrighetto
body: ```suggestion
} // end anonymous namespace
```
created_at: 2025-05-12T08:25:54Z
author_association: COLLABORATOR
diff_hunk: @@ -26,30 +26,48 @@
 if sys.platform == 'win32':
     config.available_features.add('host-platform-windows')
 
-# Determine host architecture
-host_arch = platform.machine()
-
 # Allow tests to be based on the host architecture
+host_arch = platform.machine()
 if host_arch == 'x86_64':
     config.available_features.add('host-arch-x86')
 elif host_arch == 'arm64':
     config.available_features.add('host-arch-arm64')
 
-# The tools directory defaults to LLVM_BINARY_DIR. In order to run tests with a different compiler,
-# pass the installation base path via LLVM_TOOLS_DIR at configuration time explicitly.
+# For Anroid tests, use clang from AndroidNDK
path: src/test/lit.cfg.py
user: login: antoniofrighetto
body: ```suggestion
# For Android tests, use clang from AndroidNDK.
```
created_at: 2025-05-12T08:26:33Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,63 @@
+#include "omvll/passes/string-encoding/Routines.h"
+#include <cassert>
+
+namespace {
+
+const char *DecodeRoutines[] = {
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    }
+  )delim",
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }
+  )delim",
+};
+
+omvll::EncRoutineFn *EncodeRoutines[] = {
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    },
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }};
+
+} // namespace
+
+namespace omvll {
+
+template <typename T, unsigned N>
+static constexpr unsigned arraySize(const T (&)[N]) noexcept {
+  return N;
+}
+
+// Encode and decode functions must match in pairs
+static_assert(arraySize(EncodeRoutines) == arraySize(DecodeRoutines));
+
+unsigned getNumEncodeDecodeRoutines() { return arraySize(EncodeRoutines); }
+
+EncRoutineFn *getEncodeRoutine(unsigned Idx) {
+  assert(Idx < arraySize(EncodeRoutines));
+  return EncodeRoutines[Idx];
+}
+
+const char *getDecodeRoutine(unsigned Idx) {
+  assert(Idx < arraySize(DecodeRoutines));
+  return DecodeRoutines[Idx];
+}
+
+} // namespace omvll
path: src/passes/string-encoding/Routines.cpp
user: login: antoniofrighetto
body: ```suggestion
} // end namespace omvll
```
created_at: 2025-05-12T08:27:55Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,8 @@
+namespace omvll {
+
+using EncRoutineFn = void(char *, const char *, unsigned long long, int);
+EncRoutineFn *getEncodeRoutine(unsigned Idx);
+const char *getDecodeRoutine(unsigned Idx);
+unsigned getNumEncodeDecodeRoutines();
+
+} // namespace omvll
path: src/include/omvll/passes/string-encoding/Routines.h
user: login: weliveindetail
body: The current code is what clang-format version 20 proposed. Should I still add your proposal on top?
created_at: 2025-05-12T09:38:49Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,8 @@
+namespace omvll {
path: src/include/omvll/passes/string-encoding/Routines.h
user: login: weliveindetail
body: Done
created_at: 2025-05-12T09:38:59Z
author_association: COLLABORATOR
diff_hunk: @@ -49,7 +48,7 @@ struct StringEncoding : llvm::PassInfoMixin<StringEncoding> {
     EncodingTy Type = EncodingTy::None;
     KeyTy Key;
     std::unique_ptr<llvm::Module> TM;
-    std::unique_ptr<llvm::Module> HM;
+    EncRoutineFn *BuiltinFn;
path: src/include/omvll/passes/string-encoding/StringEncoding.hpp
user: login: weliveindetail
body: Done
created_at: 2025-05-12T09:42:28Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,63 @@
+#include "omvll/passes/string-encoding/Routines.h"
+#include <cassert>
+
+namespace {
+
+const char *DecodeRoutines[] = {
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    }
+  )delim",
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }
+  )delim",
+};
+
+omvll::EncRoutineFn *EncodeRoutines[] = {
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    },
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }};
+
+} // namespace
+
+namespace omvll {
+
+template <typename T, unsigned N>
+static constexpr unsigned arraySize(const T (&)[N]) noexcept {
+  return N;
+}
+
+// Encode and decode functions must match in pairs
+static_assert(arraySize(EncodeRoutines) == arraySize(DecodeRoutines));
path: src/passes/string-encoding/Routines.cpp
user: login: weliveindetail
body: This also affects the initialization in the cpp. Can we consider it as an improvement in another PR?
created_at: 2025-05-12T09:44:52Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,8 @@
+namespace omvll {
+
+using EncRoutineFn = void(char *, const char *, unsigned long long, int);
+EncRoutineFn *getEncodeRoutine(unsigned Idx);
+const char *getDecodeRoutine(unsigned Idx);
+unsigned getNumEncodeDecodeRoutines();
+
+} // namespace omvll
path: src/include/omvll/passes/string-encoding/Routines.h
user: login: antoniofrighetto
body: Right, yes – I'd prefer to follow the existing style in the codebase.
created_at: 2025-05-12T10:28:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,63 @@
+#include "omvll/passes/string-encoding/Routines.h"
+#include <cassert>
+
+namespace {
+
+const char *DecodeRoutines[] = {
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    }
+  )delim",
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }
+  )delim",
+};
+
+omvll::EncRoutineFn *EncodeRoutines[] = {
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    },
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }};
+
+} // namespace
+
+namespace omvll {
+
+template <typename T, unsigned N>
+static constexpr unsigned arraySize(const T (&)[N]) noexcept {
+  return N;
+}
+
+// Encode and decode functions must match in pairs
+static_assert(arraySize(EncodeRoutines) == arraySize(DecodeRoutines));
path: src/passes/string-encoding/Routines.cpp
user: login: antoniofrighetto
body: Sounds good, let's leave this for a future improvement.
created_at: 2025-05-12T10:28:24Z
author_association: COLLABORATOR
diff_hunk: @@ -26,30 +26,48 @@
 if sys.platform == 'win32':
     config.available_features.add('host-platform-windows')
 
-# Determine host architecture
-host_arch = platform.machine()
-
 # Allow tests to be based on the host architecture
+host_arch = platform.machine()
 if host_arch == 'x86_64':
     config.available_features.add('host-arch-x86')
 elif host_arch == 'arm64':
     config.available_features.add('host-arch-arm64')
 
-# The tools directory defaults to LLVM_BINARY_DIR. In order to run tests with a different compiler,
-# pass the installation base path via LLVM_TOOLS_DIR at configuration time explicitly.
+# For Anroid tests, use clang from AndroidNDK
path: src/test/lit.cfg.py
user: login: weliveindetail
body: Done
created_at: 2025-05-12T13:25:01Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,63 @@
+#include "omvll/passes/string-encoding/Routines.h"
+#include <cassert>
+
+namespace {
+
+const char *DecodeRoutines[] = {
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    }
+  )delim",
+  R"delim(
+    void decode(char *out, char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char*)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }
+  )delim",
+};
+
+omvll::EncRoutineFn *EncodeRoutines[] = {
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)];
+      }
+    },
+    [](char *out, const char *in, unsigned long long key, int size) {
+      unsigned char *raw_key = (unsigned char *)(&key);
+      for (int i = 0; i < size; ++i) {
+        out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i;
+      }
+    }};
+
+} // namespace
+
+namespace omvll {
+
+template <typename T, unsigned N>
+static constexpr unsigned arraySize(const T (&)[N]) noexcept {
+  return N;
+}
+
+// Encode and decode functions must match in pairs
+static_assert(arraySize(EncodeRoutines) == arraySize(DecodeRoutines));
+
+unsigned getNumEncodeDecodeRoutines() { return arraySize(EncodeRoutines); }
+
+EncRoutineFn *getEncodeRoutine(unsigned Idx) {
+  assert(Idx < arraySize(EncodeRoutines));
+  return EncodeRoutines[Idx];
+}
+
+const char *getDecodeRoutine(unsigned Idx) {
+  assert(Idx < arraySize(DecodeRoutines));
+  return DecodeRoutines[Idx];
+}
+
+} // namespace omvll
path: src/passes/string-encoding/Routines.cpp
user: login: weliveindetail
body: Done
created_at: 2025-05-12T13:28:06Z
author_association: COLLABORATOR

PR comments:
user: login: weliveindetail
created_at: 2025-05-09T13:44:08Z
body: Local test results for Xcode on macOS 15.4.1 (arm64)
```
Testing compiler: /Applications/Xcode_16.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
Using iOS SDK: /Applications/Xcode_16.3.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.4.sdk
Testing plugin file: /workspace/omvll-build-xcode-16_3/libOMVLL.dylib
Available features are: {'host-platform-macOS', 'shell', 'host-arch-arm64', 'system-linker-mach-o', 'aarch64-registered-target', 'system-darwin', 'apple_abi'}
OMVLL_PYTHONPATH: /workspace/omvll-v1.4-macos/Python-3.10.7/Lib
-- Testing: 22 tests, 10 workers --
PASS: O-MVLL Tests :: core/plugin/find-omvll-config-plugindir.c (1 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/string-encoding/basic-arm-android.cpp (2 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/default-config-x86_64-linux.c (3 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/cfg-flattening/basic-aarch64-android.c (4 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/string-encoding/string-encoding-global-exclude-x86_64-linux.c (5 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/xor-x86_64-linux.c (6 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/arithmetic-global-exclude-x86_64-linux.c (7 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/report_diff-x86_64-linux.c (8 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/cfg-flattening/basic-arm-android.c (9 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/xor-arm-android.c (10 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/xor-x86_64-darwin.c (11 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/xor-aarch64-android.c (12 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/cfg-flattening/cfg-flattening-global-exclude-x86_64-linux.c (13 of 22)
PASS: O-MVLL Tests :: core/plugin/find-omvll-config-cwd.c (14 of 22)
PASS: O-MVLL Tests :: passes/break-cfg/basic-aarch64-ios.c (15 of 22)
PASS: O-MVLL Tests :: passes/cfg-flattening/basic-aarch64-ios.c (16 of 22)
PASS: O-MVLL Tests :: passes/string-encoding/basic-ios-swift.ll (17 of 22)
PASS: O-MVLL Tests :: passes/string-encoding/basic-aarch64-optlocal.ll (18 of 22)
PASS: O-MVLL Tests :: passes/string-encoding/basic-aarch64-ce-init.ll (19 of 22)
PASS: O-MVLL Tests :: passes/opaque-constants/opaque-constants-infinite-loop.ll (20 of 22)
PASS: O-MVLL Tests :: passes/arithmetic/xor-aarch64-ios.c (21 of 22)
PASS: O-MVLL Tests :: passes/string-encoding/basic-aarch64.cpp (22 of 22)
```
author_association: COLLABORATOR
user: login: weliveindetail
created_at: 2025-05-09T14:41:08Z
body: Local test results for NDK on macOS 15.4.1 (arm64)
```
Testing compiler: /workspace/AndroidNDK11579264.app/Contents/NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang
Testing plugin file: /workspace/omvll-build-ndk-r26d/libOMVLL.dylib
Available features are: {'host-arch-arm64', 'system-darwin', 'aarch64-registered-target', 'system-linker-mach-o', 'host-platform-macOS', 'shell', 'android_abi'}
OMVLL_PYTHONPATH: /workspace/omvll-v1.4-macos/Python-3.10.7/Lib
-- Testing: 22 tests, 10 workers --
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/xor-x86_64-darwin.c (1 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/arithmetic-global-exclude-x86_64-linux.c (2 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/string-encoding/basic-ios-swift.ll (3 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/xor-arm-android.c (4 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/default-config-x86_64-linux.c (5 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/string-encoding/string-encoding-global-exclude-x86_64-linux.c (6 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/xor-aarch64-ios.c (7 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/cfg-flattening/cfg-flattening-global-exclude-x86_64-linux.c (8 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/xor-x86_64-linux.c (9 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/cfg-flattening/basic-aarch64-ios.c (10 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/string-encoding/basic-arm-android.cpp (11 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/break-cfg/basic-aarch64-ios.c (12 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/cfg-flattening/basic-arm-android.c (13 of 22)
UNSUPPORTED: O-MVLL Tests :: passes/arithmetic/report_diff-x86_64-linux.c (14 of 22)
PASS: O-MVLL Tests :: passes/cfg-flattening/basic-aarch64-android.c (15 of 22)
PASS: O-MVLL Tests :: core/plugin/find-omvll-config-cwd.c (16 of 22)
PASS: O-MVLL Tests :: passes/string-encoding/basic-aarch64-optlocal.ll (17 of 22)
PASS: O-MVLL Tests :: passes/string-encoding/basic-aarch64-ce-init.ll (18 of 22)
PASS: O-MVLL Tests :: core/plugin/find-omvll-config-plugindir.c (19 of 22)
PASS: O-MVLL Tests :: passes/opaque-constants/opaque-constants-infinite-loop.ll (20 of 22)
PASS: O-MVLL Tests :: passes/arithmetic/xor-aarch64-android.c (21 of 22)
PASS: O-MVLL Tests :: passes/string-encoding/basic-aarch64.cpp (22 of 22)
```
author_association: COLLABORATOR