cmake_minimum_required(VERSION 3.18)

# ollvm-clang 后端：必须在 project() 之前 FORCE 覆盖编译器。
# NDK 的 android.toolchain.cmake 会无条件 set CMAKE_{C,CXX}_COMPILER 指向
# 自带 clang，命令行 -DCMAKE_CXX_COMPILER 传进来的值会被它覆盖掉。
if(OLLVM_CLANGXX)
    set(CMAKE_C_COMPILER "${OLLVM_CLANGXX}" CACHE FILEPATH "ollvm-clang" FORCE)
    set(CMAKE_CXX_COMPILER "${OLLVM_CLANGXX}" CACHE FILEPATH "ollvm-clang" FORCE)
endif()

project(protector CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(protector SHARED
        protector.cpp
        cipher.cpp
        sha256.cpp
        payload.cpp
        rasp.cpp
        selfkey.cpp
        selfmap.cpp
        sofile.cpp
        hookguard.cpp
        apkintegrity.cpp
        swap.cpp
        dalvikvm.cpp)

# 获取 NDK 路径
get_filename_component(NDK_ROOT "${CMAKE_TOOLCHAIN_FILE}" DIRECTORY)
get_filename_component(NDK_ROOT "${NDK_ROOT}" DIRECTORY)
get_filename_component(NDK_ROOT "${NDK_ROOT}" DIRECTORY)

set(LLVM_DIR "${NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64")

target_compile_options(protector PRIVATE
        -O2
        -fvisibility=hidden
        -fno-strict-aliasing
        -Wall
        -Wextra)

find_library(log-lib log)
find_library(android-lib android)
find_library(z-lib z)
if(NOT z-lib)
    set(z-lib z)
endif()
# apkintegrity.cpp 调 inflateInit2_/inflate/inflateEnd。ollvm-clang 换编译器后
# 若不显式 -lz，lld 默认允许 shlib 未定义符号，产物 DT_NEEDED 没有 libz.so，
# 真机 dlopen 报 cannot locate symbol inflateInit2_ → 白屏。
target_link_libraries(protector ${log-lib} ${android-lib} ${z-lib} z)
target_link_options(protector PRIVATE
        "-Wl,-z,max-page-size=16384"
        "-Wl,--no-undefined")

# 上游 clang（ollvm-clang / XObf）对 Android 默认动态链接 libc++_shared.so。
# 该库不在 APK 内时 System.loadLibrary 失败，表现为启动白屏 + 干净退出。
# ANDROID_STL=c++_static 只对 NDK 工具链生效；这里必须显式再给一次。
target_link_options(protector PRIVATE "-static-libstdc++")

# XOBF_EXTRA_FLAGS：compiler-rt / libunwind / lld / static-libstdc++ / resource-dir。
# 必须独立于 XOBF_PLUGIN 生效——ollvm-clang 路径只设 EXTRA_FLAGS、不设插件，
# 旧写法把 EXTRA_FLAGS 关在 if(XOBF_PLUGIN) 里，导致 ollvm 产物仍 NEEDED libc++_shared。
if(XOBF_EXTRA_FLAGS)
    target_compile_options(protector PRIVATE ${XOBF_EXTRA_FLAGS})
    target_link_options(protector PRIVATE ${XOBF_EXTRA_FLAGS})
endif()

# XObf 自研插件（推荐）：与 build_so.py 选定的 host clang 同源，无 ABI 风险
if(XOBF_PLUGIN)
    message(STATUS "XObf Plugin: ${XOBF_PLUGIN}")
    target_compile_options(protector PRIVATE "-fpass-plugin=${XOBF_PLUGIN}")
endif()

# OLLVM 22 混淆 (xollvm fork-free) - 需要 LTO
if(OLLVM_PLUGIN)
    message(STATUS "OLLVM Plugin: ${OLLVM_PLUGIN}")
    target_link_options(protector PRIVATE
        "-fpass-plugin=${OLLVM_PLUGIN}"
        "-flto"
    )
endif()

# ollvm-clang（Hikari）混淆开关：逐项传给编译器。
# 必须用单 token 的 -mllvm=<flag> 形式：写成两个独立参数（"-mllvm" "-enable-x"）
# 时 CMake 会把重复的 "-mllvm" 去重，导致部分 flag 裸奔到驱动层报 unknown argument。
# 只作用于编译期（混淆 pass 在前端运行）；链接期不需要也不接受这些开关。
if(DEFINED OLLVM_OBF_FLAGS AND NOT "${OLLVM_OBF_FLAGS}" STREQUAL "")
    separate_arguments(OLLVM_OBF_FLAGS_LIST UNIX_COMMAND "${OLLVM_OBF_FLAGS}")
    foreach(f ${OLLVM_OBF_FLAGS_LIST})
        target_compile_options(protector PRIVATE "-mllvm=${f}")
    endforeach()
endif()

# O-MVLL 混淆
if(OMVLL_PLUGIN AND OMVLL_CONFIG AND OMVLL_PYTHON)
    message(STATUS "O-MVLL Plugin: ${OMVLL_PLUGIN}")
    message(STATUS "O-MVLL Config: ${OMVLL_CONFIG}")
    
    # O-MVLL 使用 opt 插件，需要启用 LLVM Pass。
    # 注意：**不能**传 "-mllvm -omvll-config=..."——clang 在解析 -mllvm 参数时
    # 插件尚未加载，该选项未注册，会被判为 Unknown command line argument。
    # 配置文件通过 OMVLL_CONFIG 环境变量传给插件（build_so.py 已导出）。
    target_compile_options(protector PRIVATE
        "-fpass-plugin=${OMVLL_PLUGIN}"
    )
    
    # 链接时也加载插件
    target_link_options(protector PRIVATE
        "-fpass-plugin=${OMVLL_PLUGIN}"
    )
endif()
