From bdef42962fb0b86f80fbd2ac8102c1569263d828 Mon Sep 17 00:00:00 2001 From: chiteroman <98092901+chiteroman@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:03:56 +0100 Subject: [PATCH] Prepare v15.7 --- .idea/vcs.xml | 7 - app/build.gradle.kts | 27 +- app/src/main/cpp/Android.mk | 30 - app/src/main/cpp/Application.mk | 8 - app/src/main/cpp/CMakeLists.txt | 13 + app/src/main/cpp/Dobby/.clang-format | 18 + app/src/main/cpp/Dobby/.gitignore | 80 + app/src/main/cpp/Dobby/CMakeLists.txt | 384 + app/src/main/cpp/Dobby/LICENSE | 201 + app/src/main/cpp/Dobby/README.md | 26 + app/src/main/cpp/Dobby/README_zh-cn.md | 3 + .../MGCopyAnswerMonitor.cc | 46 + .../dynamic_loader_monitor.cc | 94 + .../file_operation_monitor.cc | 97 + .../memory_operation_instrument.cc | 58 + ...posix_file_descriptor_operation_monitor.cc | 120 + .../posix_socket_network_monitor.cc | 57 + .../BionicLinkerUtil/bionic_linker_demo.cc | 36 + .../BionicLinkerUtil/bionic_linker_util.cc | 197 + .../BionicLinkerUtil/bionic_linker_util.h | 23 + .../cpp/Dobby/builtin-plugin/CMakeLists.txt | 15 + .../ImportTableReplace/CMakeLists.txt | 3 + .../dobby_import_replace.cc | 192 + .../ImportTableReplace/dobby_import_replace.h | 11 + .../ObjcRuntimeReplace/CMakeLists.txt | 7 + .../dobby_objc_runtime_repalce.h | 18 + .../dobby_objc_runtime_replace.mm | 54 + .../SupervisorCallMonitor/CMakeLists.txt | 23 + .../SupervisorCallMonitor/README | 1 + .../mach_system_call_log_handler.cc | 193 + .../SupervisorCallMonitor/misc_utility.cc | 44 + .../SupervisorCallMonitor/misc_utility.h | 28 + .../sensitive_api_monitor.cc | 95 + .../supervisor_call_monitor.cc | 138 + .../supervisor_call_monitor.h | 24 + .../system_call_log_handler.cc | 98 + .../test_supervisor_call_monitor.cc | 16 + .../SymbolResolver/CMakeLists.txt | 55 + .../SymbolResolver/dobby_symbol_resolver.h | 15 + .../elf/dobby_symbol_resolver.cc | 238 + .../macho/dobby_symbol_resolver.cc | 114 + .../macho/dobby_symbol_resolver_priv.h | 6 + .../SymbolResolver/macho/macho_ctx.cc | 358 + .../SymbolResolver/macho/macho_ctx.h | 86 + .../macho/macho_file_symbol_resolver.cpp | 68 + .../macho/macho_file_symbol_resolver.h | 8 + .../macho/shared-cache/dyld_cache_format.h | 560 + .../SymbolResolver/macho/shared_cache_ctx.cpp | 183 + .../SymbolResolver/macho/shared_cache_ctx.h | 45 + .../SymbolResolver/mmap_file_util.h | 63 + .../pe/dobby_symbol_resolver.cc | 26 + app/src/main/cpp/Dobby/cmake/Macros.cmake | 3 + app/src/main/cpp/Dobby/cmake/Util.cmake | 29 + .../cpp/Dobby/cmake/auto_source_group.cmake | 32 + .../Dobby/cmake/build_environment_check.cmake | 86 + .../cpp/Dobby/cmake/compiler_and_linker.cmake | 55 + .../cpp/Dobby/cmake/dobby.xcode.source.cmake | 94 + .../cmake/platform/platform-darwin.cmake | 30 + .../Dobby/cmake/xcode_generator_helper.cmake | 9 + app/src/main/cpp/Dobby/docs/compile.md | 89 + .../main/cpp/Dobby/examples/CMakeLists.txt | 18 + app/src/main/cpp/Dobby/examples/main.cc | 14 + .../main/cpp/Dobby/examples/socket_example.cc | 212 + .../main/cpp/Dobby/external/TINYSTL/README | 1 + .../cpp/Dobby/external/TINYSTL/algorithm.h | 1 + .../cpp/Dobby/external/TINYSTL/allocator.h | 49 + .../main/cpp/Dobby/external/TINYSTL/buffer.h | 310 + .../cpp/Dobby/external/TINYSTL/function.h | 48 + .../main/cpp/Dobby/external/TINYSTL/hash.h | 53 + .../cpp/Dobby/external/TINYSTL/hash_base.h | 292 + app/src/main/cpp/Dobby/external/TINYSTL/new.h | 44 + .../main/cpp/Dobby/external/TINYSTL/stddef.h | 43 + .../main/cpp/Dobby/external/TINYSTL/string.h | 295 + .../cpp/Dobby/external/TINYSTL/string_view.h | 147 + .../main/cpp/Dobby/external/TINYSTL/traits.h | 100 + .../Dobby/external/TINYSTL/unordered_map.h | 289 + .../Dobby/external/TINYSTL/unordered_set.h | 265 + .../main/cpp/Dobby/external/TINYSTL/vector.h | 353 + .../deprecated/misc-helper/CMakeLists.txt | 18 + .../deprecated/misc-helper/async_logger.cc | 65 + .../misc-helper/deprecated/pthread_helper.cc | 129 + .../misc-helper/deprecated/pthread_helper.h | 85 + .../misc-helper/deprecated/unistd_helper.h | 30 + .../deprecated/misc-helper/format_printer.cc | 11 + .../misc-helper/misc-helper/async_logger.h | 8 + .../misc-helper/misc-helper/format_printer.h | 3 + .../misc-helper/misc-helper/variable_cache.h | 17 + .../deprecated/misc-helper/variable_cache.c | 118 + .../cpp/Dobby/external/logging/CMakeLists.txt | 18 + .../cpp/Dobby/external/logging/logging.cc | 137 + .../external/logging/logging/check_logging.h | 87 + .../Dobby/external/logging/logging/logging.h | 202 + .../Dobby/external/logging/logging_kern.cc | 0 .../external/logging/priv_headers/_simple.h | 162 + .../cpp/Dobby/external/osbase/CMakeLists.txt | 3 + app/src/main/cpp/Dobby/include/dobby.h | 152 + app/src/main/cpp/Dobby/scripts/Dockerfile | 10 + .../cpp/Dobby/scripts/platform_builder.py | 242 + .../scripts/setup_linux_cross_compile.sh | 61 + .../scripts/setup_macos_cross_compile.sh | 22 + .../ExecMemory/clear-cache-tool-all.c | 3 + .../ExecMemory/code-patch-tool-darwin.cc | 0 .../Darwin/ProcessRuntimeUtility.cc | 0 .../UnifiedInterface/platform-darwin.cc | 0 .../ExecMemory/clear-cache-tool-all.c | 145 + .../clear-cache-tool-arm-dummy.cc | 53 + .../clear-cache-tool-arm64-dummy.cc | 103 + .../ExecMemory/code-patch-tool-darwin.cc | 172 + .../ExecMemory/code-patch-tool-posix.cc | 37 + .../ExecMemory/code-patch-tool-windows.cc | 27 + .../mach_interface_support/substrated.defs | 27 + .../MultiThreadSupport/ThreadSupport.cpp | 22 + .../MultiThreadSupport/ThreadSupport.h | 63 + .../Darwin/ProcessRuntimeUtility.cc | 143 + .../Linux/ProcessRuntimeUtility.cc | 237 + .../Windows/ProcessRuntimeUtility.cc | 81 + .../Backend/UserMode/Thread/PlatformThread.cc | 19 + .../Backend/UserMode/Thread/PlatformThread.h | 32 + .../UserMode/Thread/platform-thread-posix.cc | 71 + .../Thread/platform-thread-windows.cc | 25 + .../platform-darwin/mach_vm.h | 933 + .../UnifiedInterface/platform-posix.cc | 183 + .../UnifiedInterface/platform-windows.cc | 86 + .../UserMode/UnifiedInterface/semaphore.cc | 160 + .../UserMode/UnifiedInterface/semaphore.h | 98 + .../InstructionRelocation.h | 5 + .../arm/InstructionRelocationARM.cc | 895 + .../arm/InstructionRelocationARM.h | 307 + .../arm64/InstructionRelocationARM64.cc | 366 + .../arm64/InstructionRelocationARM64.h | 13 + .../arm64/inst_constants.h | 49 + .../arm64/inst_decode_encode_kit.h | 110 + .../x64/InstructionRelocationX64.cc | 79 + .../x64/InstructionRelocationX64.h | 9 + .../x86/InstructionRelocationX86.cc | 80 + .../x86/InstructionRelocationX86.h | 9 + .../x86/InstructionRelocationX86Shared.cc | 218 + .../x86/InstructionRelocationX86Shared.h | 16 + .../InstructionRelocation/x86/X86DecodeKit.h | 21 + .../x86/deprecated/Ia32Disassembler.cc | 388 + .../x86/deprecated/X86OpcodoDecodeTable.cc | 604 + .../x86/deprecated/X86OpcodoDecodeTable.h | 142 + .../x86/x86_insn_decode/build_config.h | 144 + .../x86/x86_insn_decode/x86_insn_decode.c | 564 + .../x86/x86_insn_decode/x86_insn_decode.h | 200 + .../x86/x86_insn_decode/x86_insn_reader.c | 87 + .../x86_opcode_modrm_reg_group.c | 218 + .../x86/x86_insn_decode/x86_opcode_one_byte.c | 215 + .../x86_insn_decode/x86_opcode_sse_group.c | 545 + .../x86/x86_insn_decode/x86_opcode_two_byte.c | 249 + .../main/cpp/Dobby/source/InterceptEntry.cpp | 16 + .../main/cpp/Dobby/source/InterceptEntry.h | 30 + .../InterceptRouting/InterceptRouting.cpp | 97 + .../InterceptRouting/InterceptRouting.h | 62 + .../FunctionInlineHook/FunctionInlineHook.cc | 52 + .../FunctionInlineHookRouting.h | 22 + .../Routing/FunctionInlineHook/RoutingImpl.cc | 22 + .../FunctionWrapper/FunctionWrapperExport.cc | 27 + .../FunctionWrapper/function-wrapper.cc | 38 + .../FunctionWrapper/function-wrapper.h | 40 + .../intercept_routing_handler.cc | 79 + .../intercept_routing_handler.h | 27 + .../InstructionInstrument.cc | 42 + .../InstructionInstrumentRouting.h | 30 + .../InstructionInstrument/RoutingImpl.cc | 40 + .../instrument_routing_handler.cc | 21 + .../instrument_routing_handler.h | 7 + .../NearBranchTrampoline.cc | 50 + .../NearBranchTrampoline.h | 15 + .../near_trampoline_arm64.cc | 81 + .../RoutingPlugin/RoutingPlugin.cc | 11 + .../RoutingPlugin/RoutingPlugin.h | 30 + app/src/main/cpp/Dobby/source/Interceptor.cpp | 40 + app/src/main/cpp/Dobby/source/Interceptor.h | 25 + .../MemoryAllocator/AssemblyCodeBuilder.cc | 34 + .../MemoryAllocator/AssemblyCodeBuilder.h | 14 + .../CodeBuffer/CodeBufferBase.cc | 53 + .../CodeBuffer/CodeBufferBase.h | 40 + .../CodeBuffer/code-buffer-arm.h | 59 + .../CodeBuffer/code-buffer-arm64.h | 24 + .../CodeBuffer/code-buffer-x64.h | 17 + .../CodeBuffer/code-buffer-x86.cc | 18 + .../CodeBuffer/code-buffer-x86.h | 12 + .../CodeBuffer/code_buffer_arm.h | 56 + .../CodeBuffer/code_buffer_arm64.h | 21 + .../CodeBuffer/code_buffer_x64.h | 14 + .../CodeBuffer/code_buffer_x86.h | 14 + .../source/MemoryAllocator/MemoryAllocator.cc | 106 + .../MemoryAllocator/NearMemoryAllocator.cc | 235 + .../MemoryAllocator/NearMemoryAllocator.h | 30 + .../ExecMemory/ClearCacheTool.h | 11 + .../ExecMemory/CodePatchTool.h | 3 + .../MemoryAllocator.h | 101 + .../PlatformUnifiedInterface/platform.h | 82 + .../PlatformUtil/ProcessRuntimeUtility.h | 26 + .../ClosureTrampoline.h | 39 + .../arm/ClosureTrampolineARM.cc | 49 + .../arm/closure_bridge_arm.cc | 90 + .../arm/dummy/closure-bridge-template-arm.cc | 65 + .../dummy/closure-trampoline-template-arm.S | 34 + .../ClosureTrampolineBridge/arm/helper_arm.cc | 13 + .../arm64/ClosureTrampolineARM64.cc | 63 + .../arm64/closure_bridge_arm64.cc | 159 + .../dummy/closure-bridge-template-arm64.c | 103 + .../dummy/closure-trampoline-template-arm64.S | 36 + ...ynamic-closure-trampoline-template-arm64.S | 33 + .../arm64/helper_arm64.cc | 17 + .../common_bridge_handler.cc | 22 + .../common_bridge_handler.h | 17 + .../x64/ClosureTrampolineX64.cc | 45 + .../x64/closure_bridge_x64.cc | 141 + .../x64/dummy/closure-bridge-template-x64.c | 70 + .../dummy/closure-trampoline-template-x64.S | 17 + .../ClosureTrampolineBridge/x64/helper_x64.cc | 17 + .../x86/ClosureTrampolineX86.cc | 44 + .../x86/closure_bridge_x86.cc | 112 + .../ClosureTrampolineBridge/x86/helper_x86.cc | 16 + .../TrampolineBridge/Trampoline/Trampoline.h | 5 + .../Trampoline/arm/trampoline_arm.cc | 62 + .../Trampoline/arm64/trampoline_arm64.cc | 41 + .../Trampoline/x64/trampoline_x64.cc | 54 + .../Trampoline/x86/trampoline_x86.cc | 33 + .../main/cpp/Dobby/source/core/arch/Cpu.cc | 5 + app/src/main/cpp/Dobby/source/core/arch/Cpu.h | 7 + .../cpp/Dobby/source/core/arch/CpuFeature.cc | 7 + .../cpp/Dobby/source/core/arch/CpuFeature.h | 19 + .../cpp/Dobby/source/core/arch/CpuRegister.cc | 10 + .../cpp/Dobby/source/core/arch/CpuRegister.h | 25 + .../cpp/Dobby/source/core/arch/CpuUtils.h | 17 + .../source/core/arch/arm/constants-arm.h | 70 + .../source/core/arch/arm/registers-arm.h | 58 + .../source/core/arch/arm64/constants-arm64.h | 387 + .../source/core/arch/arm64/registers-arm64.h | 142 + .../source/core/arch/x64/constants-x64.h | 21 + .../source/core/arch/x64/registers-x64.h | 244 + .../source/core/arch/x86/constants-x86.h | 19 + .../cpp/Dobby/source/core/arch/x86/cpu-x86.cc | 10 + .../cpp/Dobby/source/core/arch/x86/cpu-x86.h | 120 + .../source/core/arch/x86/registers-x86.h | 124 + .../core/assembler/AssemblerPseudoLabel.h | 85 + .../source/core/assembler/assembler-arch.h | 28 + .../source/core/assembler/assembler-arm.cc | 41 + .../source/core/assembler/assembler-arm.h | 354 + .../source/core/assembler/assembler-arm64.cc | 25 + .../source/core/assembler/assembler-arm64.h | 560 + .../source/core/assembler/assembler-ia32.cc | 34 + .../source/core/assembler/assembler-ia32.h | 456 + .../source/core/assembler/assembler-x64.cc | 34 + .../source/core/assembler/assembler-x64.h | 596 + .../core/assembler/assembler-x86-shared.cc | 17 + .../core/assembler/assembler-x86-shared.h | 707 + .../Dobby/source/core/assembler/assembler.cc | 64 + .../Dobby/source/core/assembler/assembler.h | 74 + .../Dobby/source/core/codegen/codegen-arm.cc | 19 + .../Dobby/source/core/codegen/codegen-arm.h | 22 + .../source/core/codegen/codegen-arm64.cc | 26 + .../Dobby/source/core/codegen/codegen-arm64.h | 21 + .../Dobby/source/core/codegen/codegen-ia32.cc | 23 + .../Dobby/source/core/codegen/codegen-ia32.h | 22 + .../Dobby/source/core/codegen/codegen-x64.cc | 25 + .../Dobby/source/core/codegen/codegen-x64.h | 22 + .../cpp/Dobby/source/core/codegen/codegen.h | 17 + .../cpp/Dobby/source/core/emulator/dummy.cc | 0 app/src/main/cpp/Dobby/source/dobby.cpp | 31 + app/src/main/cpp/Dobby/source/dobby/common.h | 11 + .../cpp/Dobby/source/dobby/dobby_internal.h | 13 + .../Dobby/source/dobby/kernel_mode_header.h | 6 + app/src/main/cpp/Dobby/source/dobby/pac_kit.h | 35 + .../source/dobby/platform_detect_macro.h | 15 + .../Dobby/source/dobby/platform_features.h | 50 + app/src/main/cpp/Dobby/source/dobby/types.h | 10 + .../cpp/Dobby/source/dobby/utility_macro.h | 62 + app/src/main/cpp/Dobby/tests/CMakeLists.txt | 120 + .../main/cpp/Dobby/tests/UniconEmulator.cpp | 219 + app/src/main/cpp/Dobby/tests/UniconEmulator.h | 73 + .../cpp/Dobby/tests/test_insn_decoder_x86.cpp | 267 + .../cpp/Dobby/tests/test_insn_relo_arm.cpp | 116 + .../cpp/Dobby/tests/test_insn_relo_arm64.cpp | 97 + .../cpp/Dobby/tests/test_insn_relo_x64.cpp | 38 + app/src/main/cpp/Dobby/tests/test_native.cpp | 30 + app/src/main/cpp/cJSON/cJSON.c | 3129 -- app/src/main/cpp/cJSON/cJSON.h | 300 - app/src/main/cpp/json.hpp | 24765 ++++++++++++++++ app/src/main/cpp/main.cpp | 129 +- app/src/main/cpp/shadowhook/arch/arm/sh_a32.c | 446 - app/src/main/cpp/shadowhook/arch/arm/sh_a32.h | 41 - .../main/cpp/shadowhook/arch/arm/sh_inst.c | 523 - .../main/cpp/shadowhook/arch/arm/sh_inst.h | 41 - app/src/main/cpp/shadowhook/arch/arm/sh_t16.c | 284 - app/src/main/cpp/shadowhook/arch/arm/sh_t16.h | 46 - app/src/main/cpp/shadowhook/arch/arm/sh_t32.c | 408 - app/src/main/cpp/shadowhook/arch/arm/sh_t32.h | 36 - app/src/main/cpp/shadowhook/arch/arm/sh_txx.c | 60 - app/src/main/cpp/shadowhook/arch/arm/sh_txx.h | 39 - .../main/cpp/shadowhook/arch/arm64/sh_a64.c | 310 - .../main/cpp/shadowhook/arch/arm64/sh_a64.h | 44 - .../main/cpp/shadowhook/arch/arm64/sh_inst.c | 203 - .../main/cpp/shadowhook/arch/arm64/sh_inst.h | 42 - app/src/main/cpp/shadowhook/common/bytesig.c | 291 - app/src/main/cpp/shadowhook/common/bytesig.h | 157 - .../main/cpp/shadowhook/common/sh_config.h | 55 - app/src/main/cpp/shadowhook/common/sh_errno.c | 107 - app/src/main/cpp/shadowhook/common/sh_errno.h | 40 - app/src/main/cpp/shadowhook/common/sh_log.c | 53 - app/src/main/cpp/shadowhook/common/sh_log.h | 70 - app/src/main/cpp/shadowhook/common/sh_sig.h | 49 - .../main/cpp/shadowhook/common/sh_trampo.c | 172 - .../main/cpp/shadowhook/common/sh_trampo.h | 52 - app/src/main/cpp/shadowhook/common/sh_util.c | 538 - app/src/main/cpp/shadowhook/common/sh_util.h | 103 - .../main/cpp/shadowhook/include/shadowhook.h | 191 - app/src/main/cpp/shadowhook/sh_enter.c | 47 - app/src/main/cpp/shadowhook/sh_enter.h | 30 - app/src/main/cpp/shadowhook/sh_exit.c | 420 - app/src/main/cpp/shadowhook/sh_exit.h | 34 - app/src/main/cpp/shadowhook/sh_hub.c | 538 - app/src/main/cpp/shadowhook/sh_hub.h | 45 - app/src/main/cpp/shadowhook/sh_jni.c | 139 - app/src/main/cpp/shadowhook/sh_linker.c | 412 - app/src/main/cpp/shadowhook/sh_linker.h | 41 - app/src/main/cpp/shadowhook/sh_recorder.c | 517 - app/src/main/cpp/shadowhook/sh_recorder.h | 37 - app/src/main/cpp/shadowhook/sh_safe.c | 133 - app/src/main/cpp/shadowhook/sh_safe.h | 37 - app/src/main/cpp/shadowhook/sh_switch.c | 343 - app/src/main/cpp/shadowhook/sh_switch.h | 34 - app/src/main/cpp/shadowhook/sh_task.c | 333 - app/src/main/cpp/shadowhook/sh_task.h | 40 - app/src/main/cpp/shadowhook/shadowhook.c | 328 - .../cpp/shadowhook/third_party/bsd/queue.h | 551 - .../cpp/shadowhook/third_party/bsd/tree.h | 759 - .../cpp/shadowhook/third_party/lss/LICENSE | 28 - .../third_party/lss/linux_syscall_support.h | 4867 --- .../cpp/shadowhook/third_party/xdl/LICENSE | 21 - .../main/cpp/shadowhook/third_party/xdl/xdl.c | 916 - .../main/cpp/shadowhook/third_party/xdl/xdl.h | 92 - .../shadowhook/third_party/xdl/xdl_iterate.c | 297 - .../shadowhook/third_party/xdl/xdl_iterate.h | 43 - .../shadowhook/third_party/xdl/xdl_linker.c | 234 - .../shadowhook/third_party/xdl/xdl_linker.h | 40 - .../cpp/shadowhook/third_party/xdl/xdl_lzma.c | 187 - .../cpp/shadowhook/third_party/xdl/xdl_lzma.h | 40 - .../cpp/shadowhook/third_party/xdl/xdl_util.c | 95 - .../cpp/shadowhook/third_party/xdl/xdl_util.h | 71 - .../playintegrityfix/CustomKeyStoreSpi.java | 105 - .../playintegrityfix/CustomProvider.java | 13 +- .../playintegrityfix/EntryPoint.java | 39 +- module/module.prop | 4 +- module/post-fs-data.sh | 10 + module/service.sh | 5 - 350 files changed, 50901 insertions(+), 19873 deletions(-) delete mode 100644 .idea/vcs.xml delete mode 100644 app/src/main/cpp/Android.mk delete mode 100644 app/src/main/cpp/Application.mk create mode 100644 app/src/main/cpp/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/.clang-format create mode 100644 app/src/main/cpp/Dobby/.gitignore create mode 100644 app/src/main/cpp/Dobby/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/LICENSE create mode 100644 app/src/main/cpp/Dobby/README.md create mode 100644 app/src/main/cpp/Dobby/README_zh-cn.md create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/MGCopyAnswerMonitor.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/dynamic_loader_monitor.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/file_operation_monitor.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/memory_operation_instrument.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/posix_file_descriptor_operation_monitor.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/posix_socket_network_monitor.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_demo.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_util.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_util.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/dobby_import_replace.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/dobby_import_replace.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/dobby_objc_runtime_repalce.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/dobby_objc_runtime_replace.mm create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/README create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/mach_system_call_log_handler.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/misc_utility.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/misc_utility.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/sensitive_api_monitor.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/supervisor_call_monitor.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/supervisor_call_monitor.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/system_call_log_handler.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/test_supervisor_call_monitor.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/dobby_symbol_resolver.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/elf/dobby_symbol_resolver.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver_priv.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.cc create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_file_symbol_resolver.cpp create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_file_symbol_resolver.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared-cache/dyld_cache_format.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_ctx.cpp create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_ctx.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/mmap_file_util.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/pe/dobby_symbol_resolver.cc create mode 100644 app/src/main/cpp/Dobby/cmake/Macros.cmake create mode 100644 app/src/main/cpp/Dobby/cmake/Util.cmake create mode 100644 app/src/main/cpp/Dobby/cmake/auto_source_group.cmake create mode 100644 app/src/main/cpp/Dobby/cmake/build_environment_check.cmake create mode 100644 app/src/main/cpp/Dobby/cmake/compiler_and_linker.cmake create mode 100644 app/src/main/cpp/Dobby/cmake/dobby.xcode.source.cmake create mode 100644 app/src/main/cpp/Dobby/cmake/platform/platform-darwin.cmake create mode 100644 app/src/main/cpp/Dobby/cmake/xcode_generator_helper.cmake create mode 100644 app/src/main/cpp/Dobby/docs/compile.md create mode 100644 app/src/main/cpp/Dobby/examples/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/examples/main.cc create mode 100644 app/src/main/cpp/Dobby/examples/socket_example.cc create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/README create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/algorithm.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/allocator.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/buffer.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/function.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/hash.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/hash_base.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/new.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/stddef.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/string.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/string_view.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/traits.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/unordered_map.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/unordered_set.h create mode 100644 app/src/main/cpp/Dobby/external/TINYSTL/vector.h create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/async_logger.cc create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/pthread_helper.cc create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/pthread_helper.h create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/unistd_helper.h create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/format_printer.cc create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/async_logger.h create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/format_printer.h create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/variable_cache.h create mode 100644 app/src/main/cpp/Dobby/external/deprecated/misc-helper/variable_cache.c create mode 100644 app/src/main/cpp/Dobby/external/logging/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/external/logging/logging.cc create mode 100644 app/src/main/cpp/Dobby/external/logging/logging/check_logging.h create mode 100644 app/src/main/cpp/Dobby/external/logging/logging/logging.h create mode 100644 app/src/main/cpp/Dobby/external/logging/logging_kern.cc create mode 100644 app/src/main/cpp/Dobby/external/logging/priv_headers/_simple.h create mode 100644 app/src/main/cpp/Dobby/external/osbase/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/include/dobby.h create mode 100644 app/src/main/cpp/Dobby/scripts/Dockerfile create mode 100644 app/src/main/cpp/Dobby/scripts/platform_builder.py create mode 100644 app/src/main/cpp/Dobby/scripts/setup_linux_cross_compile.sh create mode 100644 app/src/main/cpp/Dobby/scripts/setup_macos_cross_compile.sh create mode 100644 app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/clear-cache-tool-all.c create mode 100644 app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/code-patch-tool-darwin.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/KernelMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/platform-darwin.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool/clear-cache-tool-arm-dummy.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool/clear-cache-tool-arm64-dummy.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-darwin.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-posix.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-windows.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/substrated/mach_interface_support/substrated.defs create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/MultiThreadSupport/ThreadSupport.cpp create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/MultiThreadSupport/ThreadSupport.h create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.h create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/platform-thread-posix.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/platform-thread-windows.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-darwin/mach_vm.h create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-posix.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-windows.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/semaphore.cc create mode 100644 app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/semaphore.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/InstructionRelocation.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.cc create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.cc create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_constants.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_decode_encode_kit.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.cc create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.cc create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.cc create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/X86DecodeKit.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/Ia32Disassembler.cc create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.cc create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/build_config.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_reader.c create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_modrm_reg_group.c create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_one_byte.c create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_sse_group.c create mode 100644 app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_two_byte.c create mode 100644 app/src/main/cpp/Dobby/source/InterceptEntry.cpp create mode 100644 app/src/main/cpp/Dobby/source/InterceptEntry.h create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.cpp create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.h create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHook.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHookRouting.h create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/RoutingImpl.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/FunctionWrapperExport.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.h create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.h create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrument.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrumentRouting.h create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/RoutingImpl.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.h create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.h create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/near_trampoline_arm64.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.cc create mode 100644 app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.h create mode 100644 app/src/main/cpp/Dobby/source/Interceptor.cpp create mode 100644 app/src/main/cpp/Dobby/source/Interceptor.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.cc create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.cc create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-arm.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-arm64.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x64.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.cc create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_arm.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_arm64.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x64.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x86.h create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/MemoryAllocator.cc create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.cc create mode 100644 app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.h create mode 100644 app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/ClearCacheTool.h create mode 100644 app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/CodePatchTool.h create mode 100644 app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/MemoryAllocator.h create mode 100644 app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/platform.h create mode 100644 app/src/main/cpp/Dobby/source/PlatformUtil/ProcessRuntimeUtility.h create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/ClosureTrampolineARM.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/closure_bridge_arm.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-bridge-template-arm.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-trampoline-template-arm.S create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/helper_arm.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/ClosureTrampolineARM64.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/closure_bridge_arm64.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-bridge-template-arm64.c create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-trampoline-template-arm64.S create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/dynamic-closure-trampoline-template-arm64.S create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/helper_arm64.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/ClosureTrampolineX64.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/closure_bridge_x64.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-bridge-template-x64.c create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-trampoline-template-x64.S create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/helper_x64.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/ClosureTrampolineX86.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/closure_bridge_x86.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/helper_x86.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/Trampoline.h create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm/trampoline_arm.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm64/trampoline_arm64.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x64/trampoline_x64.cc create mode 100644 app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x86/trampoline_x86.cc create mode 100644 app/src/main/cpp/Dobby/source/core/arch/Cpu.cc create mode 100644 app/src/main/cpp/Dobby/source/core/arch/Cpu.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/CpuFeature.cc create mode 100644 app/src/main/cpp/Dobby/source/core/arch/CpuFeature.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/CpuRegister.cc create mode 100644 app/src/main/cpp/Dobby/source/core/arch/CpuRegister.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/CpuUtils.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/arm/constants-arm.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/arm/registers-arm.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/arm64/constants-arm64.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/arm64/registers-arm64.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/x64/constants-x64.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/x64/registers-x64.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/x86/constants-x86.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.cc create mode 100644 app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.h create mode 100644 app/src/main/cpp/Dobby/source/core/arch/x86/registers-x86.h create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/AssemblerPseudoLabel.h create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-arch.h create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.cc create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.h create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.cc create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.h create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.cc create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.h create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.cc create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.h create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.cc create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.h create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler.cc create mode 100644 app/src/main/cpp/Dobby/source/core/assembler/assembler.h create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.cc create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.h create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.cc create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.h create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.cc create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.h create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.cc create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.h create mode 100644 app/src/main/cpp/Dobby/source/core/codegen/codegen.h create mode 100644 app/src/main/cpp/Dobby/source/core/emulator/dummy.cc create mode 100644 app/src/main/cpp/Dobby/source/dobby.cpp create mode 100644 app/src/main/cpp/Dobby/source/dobby/common.h create mode 100644 app/src/main/cpp/Dobby/source/dobby/dobby_internal.h create mode 100644 app/src/main/cpp/Dobby/source/dobby/kernel_mode_header.h create mode 100644 app/src/main/cpp/Dobby/source/dobby/pac_kit.h create mode 100644 app/src/main/cpp/Dobby/source/dobby/platform_detect_macro.h create mode 100644 app/src/main/cpp/Dobby/source/dobby/platform_features.h create mode 100644 app/src/main/cpp/Dobby/source/dobby/types.h create mode 100644 app/src/main/cpp/Dobby/source/dobby/utility_macro.h create mode 100644 app/src/main/cpp/Dobby/tests/CMakeLists.txt create mode 100644 app/src/main/cpp/Dobby/tests/UniconEmulator.cpp create mode 100644 app/src/main/cpp/Dobby/tests/UniconEmulator.h create mode 100644 app/src/main/cpp/Dobby/tests/test_insn_decoder_x86.cpp create mode 100644 app/src/main/cpp/Dobby/tests/test_insn_relo_arm.cpp create mode 100644 app/src/main/cpp/Dobby/tests/test_insn_relo_arm64.cpp create mode 100644 app/src/main/cpp/Dobby/tests/test_insn_relo_x64.cpp create mode 100644 app/src/main/cpp/Dobby/tests/test_native.cpp delete mode 100644 app/src/main/cpp/cJSON/cJSON.c delete mode 100644 app/src/main/cpp/cJSON/cJSON.h create mode 100644 app/src/main/cpp/json.hpp delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_a32.c delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_a32.h delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_inst.c delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_inst.h delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_t16.c delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_t16.h delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_t32.c delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_t32.h delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_txx.c delete mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_txx.h delete mode 100644 app/src/main/cpp/shadowhook/arch/arm64/sh_a64.c delete mode 100644 app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h delete mode 100644 app/src/main/cpp/shadowhook/arch/arm64/sh_inst.c delete mode 100644 app/src/main/cpp/shadowhook/arch/arm64/sh_inst.h delete mode 100644 app/src/main/cpp/shadowhook/common/bytesig.c delete mode 100644 app/src/main/cpp/shadowhook/common/bytesig.h delete mode 100644 app/src/main/cpp/shadowhook/common/sh_config.h delete mode 100644 app/src/main/cpp/shadowhook/common/sh_errno.c delete mode 100644 app/src/main/cpp/shadowhook/common/sh_errno.h delete mode 100644 app/src/main/cpp/shadowhook/common/sh_log.c delete mode 100644 app/src/main/cpp/shadowhook/common/sh_log.h delete mode 100644 app/src/main/cpp/shadowhook/common/sh_sig.h delete mode 100644 app/src/main/cpp/shadowhook/common/sh_trampo.c delete mode 100644 app/src/main/cpp/shadowhook/common/sh_trampo.h delete mode 100644 app/src/main/cpp/shadowhook/common/sh_util.c delete mode 100644 app/src/main/cpp/shadowhook/common/sh_util.h delete mode 100644 app/src/main/cpp/shadowhook/include/shadowhook.h delete mode 100644 app/src/main/cpp/shadowhook/sh_enter.c delete mode 100644 app/src/main/cpp/shadowhook/sh_enter.h delete mode 100644 app/src/main/cpp/shadowhook/sh_exit.c delete mode 100644 app/src/main/cpp/shadowhook/sh_exit.h delete mode 100644 app/src/main/cpp/shadowhook/sh_hub.c delete mode 100644 app/src/main/cpp/shadowhook/sh_hub.h delete mode 100644 app/src/main/cpp/shadowhook/sh_jni.c delete mode 100644 app/src/main/cpp/shadowhook/sh_linker.c delete mode 100644 app/src/main/cpp/shadowhook/sh_linker.h delete mode 100644 app/src/main/cpp/shadowhook/sh_recorder.c delete mode 100644 app/src/main/cpp/shadowhook/sh_recorder.h delete mode 100644 app/src/main/cpp/shadowhook/sh_safe.c delete mode 100644 app/src/main/cpp/shadowhook/sh_safe.h delete mode 100644 app/src/main/cpp/shadowhook/sh_switch.c delete mode 100644 app/src/main/cpp/shadowhook/sh_switch.h delete mode 100644 app/src/main/cpp/shadowhook/sh_task.c delete mode 100644 app/src/main/cpp/shadowhook/sh_task.h delete mode 100644 app/src/main/cpp/shadowhook/shadowhook.c delete mode 100644 app/src/main/cpp/shadowhook/third_party/bsd/queue.h delete mode 100644 app/src/main/cpp/shadowhook/third_party/bsd/tree.h delete mode 100644 app/src/main/cpp/shadowhook/third_party/lss/LICENSE delete mode 100644 app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/LICENSE delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl.c delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl.h delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.c delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.h delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.c delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.c delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.c delete mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h delete mode 100644 app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java create mode 100644 module/post-fs-data.sh diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 6680155..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ef9ac2a..4b64b1e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "es.chiteroman.playintegrityfix" minSdk = 26 targetSdk = 34 - versionCode = 15600 - versionName = "v15.6" + versionCode = 15700 + versionName = "v15.7" multiDexEnabled = false buildFeatures { @@ -24,16 +24,20 @@ android { jniLibs { excludes += "**/liblog.so" excludes += "**/libdobby.so" - excludes += "**/libshadowhook.so" } } externalNativeBuild { - ndk { - abiFilters += "arm64-v8a" - abiFilters += "armeabi-v7a" + cmake { + arguments += "-DANDROID_STL=none" + arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel" + arguments += "-DPlugin.Android.BionicLinkerUtil=ON" - jobs = Runtime.getRuntime().availableProcessors() + cppFlags += "-std=c++20" + cppFlags += "-fno-exceptions" + cppFlags += "-fno-rtti" + cppFlags += "-fvisibility=hidden" + cppFlags += "-fvisibility-inlines-hidden" } } } @@ -53,12 +57,17 @@ android { } externalNativeBuild { - ndkBuild { - path = file("src/main/cpp/Android.mk") + cmake { + path = file("src/main/cpp/CMakeLists.txt") + version = "3.22.1" } } } +dependencies { + implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0") +} + tasks.register("updateModuleProp") { doLast { val versionName = project.android.defaultConfig.versionName diff --git a/app/src/main/cpp/Android.mk b/app/src/main/cpp/Android.mk deleted file mode 100644 index 1f5bda8..0000000 --- a/app/src/main/cpp/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := playintegrityfix -LOCAL_SRC_FILES := $(LOCAL_PATH)/main.cpp -LOCAL_C_INCLUDES := $(LOCAL_PATH) - -LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/cJSON/cJSON.c) -LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/*.c) -LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/common/*.c) -LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/third_party/xdl/*.c) - -LOCAL_C_INCLUDES += $(LOCAL_PATH)/cJSON -LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook -LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/common -LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/include -LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/bsd -LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/lss -LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/xdl - -ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm/*.c) - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm -else ifeq ($(TARGET_ARCH_ABI),arm64-v8a) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm64/*.c) - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm64 -endif - -LOCAL_LDLIBS := -llog -include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/app/src/main/cpp/Application.mk b/app/src/main/cpp/Application.mk deleted file mode 100644 index 6160a2d..0000000 --- a/app/src/main/cpp/Application.mk +++ /dev/null @@ -1,8 +0,0 @@ -APP_STL := system -APP_ABI := armeabi-v7a arm64-v8a -APP_CFLAGS := -Oz -flto -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -fno-threadsafe-statics -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-stack-protector -APP_CPPFLAGS := -std=c++2b -fno-exceptions -fno-rtti -APP_CONLYFLAGS := -std=c2x -APP_LDFLAGS := -Oz -flto -Wl,--exclude-libs,ALL -Wl,--gc-sections -APP_PLATFORM := android-26 -APP_THIN_ARCHIVE := true \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..6c6492c --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.22.1) + +project(playintegrityfix) + +find_package(cxx REQUIRED CONFIG) + +link_libraries(cxx::cxx) + +add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp) + +add_subdirectory(Dobby) + +target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC log dobby_static) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/.clang-format b/app/src/main/cpp/Dobby/.clang-format new file mode 100644 index 0000000..17d6bc4 --- /dev/null +++ b/app/src/main/cpp/Dobby/.clang-format @@ -0,0 +1,18 @@ +BasedOnStyle: LLVM + +IndentWidth: 2 +TabWidth: 2 +UseTab: Never +ColumnLimit: 120 + +FixNamespaceComments: true + +# default is false +#AlignConsecutiveMacros: true +#AlignConsecutiveAssignments: true +#AlignConsecutiveDeclarations: true + +# default is true +ReflowComments: false +SortIncludes : false +AllowShortFunctionsOnASingleLine: false \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/.gitignore b/app/src/main/cpp/Dobby/.gitignore new file mode 100644 index 0000000..4bbed76 --- /dev/null +++ b/app/src/main/cpp/Dobby/.gitignore @@ -0,0 +1,80 @@ +.DS_Store +.idea/ +*-build*/ +build-output/ + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Prefab +/prefab/**/*.a +/prefab/**/*.h +/AndroidManifest.xml diff --git a/app/src/main/cpp/Dobby/CMakeLists.txt b/app/src/main/cpp/Dobby/CMakeLists.txt new file mode 100644 index 0000000..6907a14 --- /dev/null +++ b/app/src/main/cpp/Dobby/CMakeLists.txt @@ -0,0 +1,384 @@ +cmake_minimum_required(VERSION 3.5) +project(Dobby) +enable_language(ASM) + +include(cmake/Util.cmake) +include(cmake/Macros.cmake) +include(cmake/build_environment_check.cmake) +include(cmake/auto_source_group.cmake) +include(cmake/xcode_generator_helper.cmake) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_C_STANDARD 11) + +auto_source_group("." "auto-source-group" "\\.(cc|cpp|c|h)$") + +# --- options + +option(DOBBY_DEBUG "Enable debug logging" OFF) + +option(NearBranch "Enable near branch trampoline" ON) + +option(FullFloatingPointRegisterPack "Save and pack all floating-point registers" OFF) + +option(Plugin.SymbolResolver "Enable symbol resolver" ON) + +option(Plugin.ImportTableReplace "Enable import table replace " OFF) + +option(Plugin.Android.BionicLinkerUtil "Enable android bionic linker util" OFF) + +option(DOBBY_BUILD_EXAMPLE "Build example" OFF) + +option(DOBBY_BUILD_TEST "Build test" OFF) + +# --- private +option(DOBBY_BUILD_KERNEL_MODE "Build xnu kernel mode" OFF) + +option(Private.Obfuscation "Enable llvm obfuscation" OFF) + +if ((NOT DEFINED CMAKE_BUILD_TYPE) OR (CMAKE_BUILD_TYPE STREQUAL "Debug")) + set(DOBBY_DEBUG ON) +endif () + + +set(compile_definitions "") + +# for arm64, allow access q8 - q31 +if (FullFloatingPointRegisterPack) + set(compile_definitions "${compile_definitions} -DFULL_FLOATING_POINT_REGISTER_PACK") +endif () + +if (DOBBY_BUILD_KERNEL_MODE) + set(compile_definitions "${compile_definitions} -DBUILDING_KERNEL") +endif () + +if (DOBBY_DEBUG) + set(compile_definitions "${compile_definitions} -DDOBBY_DEBUG") +else () + set(compile_definitions "${compile_definitions} -DDOBBY_LOGGING_DISABLE") +endif () + +if (CMAKE_GENERATOR STREQUAL Xcode) +endif () + +include(cmake/compiler_and_linker.cmake) + +message(STATUS "[Dobby] CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") +message(STATUS "[Dobby] DOBBY_DEBUG: ${DOBBY_DEBUG}") +message(STATUS "[Dobby] NearBranch: ${NearBranch}") +message(STATUS "[Dobby] FullFloatingPointRegisterPack: ${FullFloatingPointRegisterPack}") +message(STATUS "[Dobby] Plugin.SymbolResolver: ${Plugin.SymbolResolver}") +message(STATUS "[Dobby] Plugin.ImportTableReplace: ${Plugin.ImportTableReplace}") +message(STATUS "[Dobby] Plugin.Android.BionicLinkerUtil: ${Plugin.Android.BionicLinkerUtil}") +message(STATUS "[Dobby] DOBBY_BUILD_EXAMPLE: ${DOBBY_BUILD_EXAMPLE}") +message(STATUS "[Dobby] DOBBY_BUILD_TEST: ${DOBBY_BUILD_TEST}") +message(STATUS "[Dobby] DOBBY_BUILD_KERNEL_MODE: ${DOBBY_BUILD_KERNEL_MODE}") +message(STATUS "[Dobby] Private.Obfuscation: ${Private.Obfuscation}") + +# --- + +include_directories( + . + ./include + ./source + source/dobby + + ./external + ./external/logging + + ./builtin-plugin +) + +if (SYSTEM.Darwin AND (NOT DOBBY_BUILD_KERNEL_MODE)) + include_directories( + source/Backend/UserMode + ) +endif () + +# --- + +set(DOBBY_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + # cpu + source/core/arch/CpuFeature.cc + source/core/arch/CpuRegister.cc + + # assembler + source/core/assembler/assembler.cc + source/core/assembler/assembler-arm.cc + source/core/assembler/assembler-arm64.cc + source/core/assembler/assembler-ia32.cc + source/core/assembler/assembler-x64.cc + + # codegen + source/core/codegen/codegen-arm.cc + source/core/codegen/codegen-arm64.cc + source/core/codegen/codegen-ia32.cc + source/core/codegen/codegen-x64.cc + + # memory kit + source/MemoryAllocator/CodeBuffer/CodeBufferBase.cc + source/MemoryAllocator/AssemblyCodeBuilder.cc + source/MemoryAllocator/MemoryAllocator.cc + + # instruction relocation + source/InstructionRelocation/arm/InstructionRelocationARM.cc + source/InstructionRelocation/arm64/InstructionRelocationARM64.cc + source/InstructionRelocation/x86/InstructionRelocationX86.cc + source/InstructionRelocation/x86/InstructionRelocationX86Shared.cc + source/InstructionRelocation/x64/InstructionRelocationX64.cc + source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c + + # intercept routing + source/InterceptRouting/InterceptRouting.cpp + + # intercept routing trampoline + source/TrampolineBridge/Trampoline/arm/trampoline_arm.cc + source/TrampolineBridge/Trampoline/arm64/trampoline_arm64.cc + source/TrampolineBridge/Trampoline/x86/trampoline_x86.cc + source/TrampolineBridge/Trampoline/x64/trampoline_x64.cc + + # closure trampoline bridge - arm + source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm/helper_arm.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm/closure_bridge_arm.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm/ClosureTrampolineARM.cc + # closure trampoline bridge - arm64 + source/TrampolineBridge/ClosureTrampolineBridge/arm64/helper_arm64.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm64/closure_bridge_arm64.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm64/ClosureTrampolineARM64.cc + # closure trampoline bridge - x86 + source/TrampolineBridge/ClosureTrampolineBridge/x86/helper_x86.cc + source/TrampolineBridge/ClosureTrampolineBridge/x86/closure_bridge_x86.cc + source/TrampolineBridge/ClosureTrampolineBridge/x86/ClosureTrampolineX86.cc + # closure trampoline bridge - x64 + source/TrampolineBridge/ClosureTrampolineBridge/x64/helper_x64.cc + source/TrampolineBridge/ClosureTrampolineBridge/x64/closure_bridge_x64.cc + source/TrampolineBridge/ClosureTrampolineBridge/x64/ClosureTrampolineX64.cc + + source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrument.cc + source/InterceptRouting/Routing/InstructionInstrument/RoutingImpl.cc + source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.cc + + source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHook.cc + source/InterceptRouting/Routing/FunctionInlineHook/RoutingImpl.cc + + # plugin register + source/InterceptRouting/RoutingPlugin/RoutingPlugin.cc + + # main + source/dobby.cpp + source/Interceptor.cpp + source/InterceptEntry.cpp + ) + + +if (SYSTEM.Darwin AND NOT DOBBY_BUILD_KERNEL_MODE) + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc + + source/Backend/UserMode/UnifiedInterface/platform-posix.cc + + source/Backend/UserMode/ExecMemory/code-patch-tool-darwin.cc + source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c + ) + +elseif (SYSTEM.Linux OR SYSTEM.Android) + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc + + source/Backend/UserMode/UnifiedInterface/platform-posix.cc + + source/Backend/UserMode/ExecMemory/code-patch-tool-posix.cc + source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c + ) +elseif (SYSTEM.Windows) + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc + + source/Backend/UserMode/UnifiedInterface/platform-windows.cc + + source/Backend/UserMode/ExecMemory/code-patch-tool-windows.cc + source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c + ) +endif () + +if (PROCESSOR.X86_64 OR PROCESSOR.X86) + set(NearBranch ON) +endif () + +# --- + +if (0 AND SYSTEM.iOS AND (NOT DOBBY_BUILD_KERNEL_MODE)) + include_directories( + source/Backend/UserMode/ExecMemory/substrated + ) + set(compile_definitions "${compile_definitions} -DCODE_PATCH_WITH_SUBSTRATED") + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + source/Backend/UserMode/ExecMemory/substrated/mach_interface_support + ) +endif () + +# ----- instrument ----- + +if (FunctionWrapper) + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + # user mode - multi thread support + # source/UserMode/MultiThreadSupport/ThreadSupport.cpp + # source/UserMode/Thread/PlatformThread.cc + # source/UserMode/Thread/platform-thread-${platform1}.cc + ) + message(FATAL_ERROR "[!] FunctionWrapper plugin is not supported") +endif () + +# --- + +if (NearBranch) + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/near_trampoline_arm64.cc + source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.cc + source/MemoryAllocator/NearMemoryAllocator.cc) +endif () + +# --- + +# add logging library +add_subdirectory(external/logging) +get_target_property(logging.SOURCE_FILE_LIST logging SOURCES) + +# add osbase library +add_subdirectory(external/osbase) + +# --- + +if (Plugin.SymbolResolver) + include_directories(builtin-plugin/SymbolResolver) + add_subdirectory(builtin-plugin/SymbolResolver) + get_target_property(symbol_resolver.SOURCE_FILE_LIST dobby_symbol_resolver SOURCES) + set(dobby.plugin.SOURCE_FILE_LIST ${dobby.plugin.SOURCE_FILE_LIST} + ${symbol_resolver.SOURCE_FILE_LIST} + ) +endif () + +# --- + +set(dobby.HEADER_FILE_LIST + include/dobby.h + ) + +# --- + +# add build version +string(TIMESTAMP TODAY "%Y%m%d") +set(VERSION_REVISION "-${TODAY}") +if (EXISTS "${CMAKE_SOURCE_DIR}/.git") + execute_process( + COMMAND git rev-parse --short --verify HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE VERSION_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (VERSION_COMMIT_HASH) + set(VERSION_REVISION "${VERSION_REVISION}-${VERSION_COMMIT_HASH}") + endif () +endif () +set(DOBBY_BUILD_VERSION "Dobby${VERSION_REVISION}") +set(compile_definitions "${compile_definitions} -D__DOBBY_BUILD_VERSION__=\"${DOBBY_BUILD_VERSION}\"") +message(STATUS "[Dobby] ${DOBBY_BUILD_VERSION}") + +# --- + +set(SOURCE_FILE_LIST + ${dobby.HEADER_FILE_LIST} + ${dobby.SOURCE_FILE_LIST} + ${logging.SOURCE_FILE_LIST} + ${dobby.plugin.SOURCE_FILE_LIST} + ) + +get_absolute_path_list(SOURCE_FILE_LIST SOURCE_FILE_LIST_) +set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST_}) + +add_library(dobby SHARED + ${SOURCE_FILE_LIST} + ) + +target_include_directories(dobby PUBLIC + include + ) + +# --- + +add_library(dobby_static STATIC + ${SOURCE_FILE_LIST} + ) + +target_include_directories(dobby_static PUBLIC + include + ) + +set_target_properties(dobby_static + PROPERTIES OUTPUT_NAME "dobby" + ) + +# --- + +set_target_properties(dobby + PROPERTIES + LINK_FLAGS "${linker_flags}" + COMPILE_FLAGS "${compiler_flags}" + ) + +set_target_properties(dobby_static + PROPERTIES + COMPILE_FLAGS "${compiler_flags}" + ) + +target_compile_definitions(dobby PRIVATE + "COMPILE_DEFINITIONS ${compile_definitions}" + ) +target_compile_definitions(dobby_static PRIVATE + "COMPILE_DEFINITIONS ${compile_definitions}" + ) + +# --- + +if (Private.Obfuscation) + set(linker_flags "${linker_flags} -Wl,-mllvm -Wl,-obfuscator-conf=all") +endif () + +# --- + +if (SYSTEM.Android) + target_link_libraries(dobby log) + if (PROCESSOR.ARM) + set_target_properties(dobby + PROPERTIES + ANDROID_ARM_MODE arm + ) + set_target_properties(dobby_static + PROPERTIES + ANDROID_ARM_MODE arm + ) + endif () +endif () + +if (SYSTEM.Linux) + target_link_libraries(dobby dl) +endif () + +# --- + +if (DOBBY_BUILD_EXAMPLE AND (NOT DOBBY_BUILD_KERNEL_MODE)) + add_subdirectory(examples) +endif () + +if (DOBBY_BUILD_TEST AND (NOT DOBBY_BUILD_KERNEL_MODE)) + add_subdirectory(tests) +endif () + +# --- + +if (SYSTEM.Darwin AND (NOT DOBBY_BUILD_KERNEL_MODE)) + include(cmake/platform/platform-darwin.cmake) +endif () \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/LICENSE b/app/src/main/cpp/Dobby/LICENSE new file mode 100644 index 0000000..f49a4e1 --- /dev/null +++ b/app/src/main/cpp/Dobby/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/README.md b/app/src/main/cpp/Dobby/README.md new file mode 100644 index 0000000..c0c0195 --- /dev/null +++ b/app/src/main/cpp/Dobby/README.md @@ -0,0 +1,26 @@ +## Dobby + +[![Contact me Telegram](https://img.shields.io/badge/Contact%20me-Telegram-blue.svg)](https://t.me/IOFramebuffer) [![Join group Telegram](https://img.shields.io/badge/Join%20group-Telegram-brightgreen.svg)](https://t.me/dobby_group) + +Dobby a lightweight, multi-platform, multi-architecture exploit hook framework. + +- Minimal and modular library +- Multi-platform support(Windows/macOS/iOS/Android/Linux) +- Multiple architecture support(X86, X86-64, ARM, ARM64) + +## Compile + +[docs/compile.md](docs/compile.md) + +## Download + +[download latest library](https://github.com/jmpews/Dobby/releases/tag/latest) + +## Credits + +1. [frida-gum](https://github.com/frida/frida-gum) +2. [minhook](https://github.com/TsudaKageyu/minhook) +3. [substrate](https://github.com/jevinskie/substrate). +4. [v8](https://github.com/v8/v8) +5. [dart](https://github.com/dart-lang/sdk) +6. [vixl](https://git.linaro.org/arm/vixl.git) diff --git a/app/src/main/cpp/Dobby/README_zh-cn.md b/app/src/main/cpp/Dobby/README_zh-cn.md new file mode 100644 index 0000000..2f528b4 --- /dev/null +++ b/app/src/main/cpp/Dobby/README_zh-cn.md @@ -0,0 +1,3 @@ +## Dobby + +**å¾…æ›´æ–°** \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/MGCopyAnswerMonitor.cc b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/MGCopyAnswerMonitor.cc new file mode 100644 index 0000000..ddf2ee2 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/MGCopyAnswerMonitor.cc @@ -0,0 +1,46 @@ +#include "./dobby_monitor.h" + +#include +#include + +#define LOG_TAG "MGCopyAnswer" + +static uintptr_t getCallFirstArg(DobbyRegisterContext *ctx) { + uintptr_t result; +#if defined(_M_X64) || defined(__x86_64__) +#if defined(_WIN32) + result = ctx->general.regs.rcx; +#else + result = ctx->general.regs.rdi; +#endif +#elif defined(__arm64__) || defined(__aarch64__) + result = ctx->general.regs.x0; +#elif defined(__arm__) + result = ctx->general.regs.r0; +#else +#error "Not Support Architecture." +#endif + return result; +} + +void common_handler(DobbyRegisterContext *ctx, const InterceptEntry *info) { + CFStringRef key_ = 0; + key_ = (CFStringRef)getCallFirstArg(ctx); + + char str_key[256] = {0}; + CFStringGetCString(key_, str_key, 256, kCFStringEncodingUTF8); + LOG("[#] MGCopyAnswer:: %s\n", str_key); +} + +#if 0 +__attribute__((constructor)) static void ctor() { + void *lib = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_NOW); + void *MGCopyAnswer_addr = DobbySymbolResolver("libMobileGestalt.dylib", "MGCopyAnswer"); + + sleep(1); + + dobby_enable_near_branch_trampoline(); + DobbyInstrument((void *)MGCopyAnswer_addr, common_handler); + dobby_disable_near_branch_trampoline(); +} +#endif diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/dynamic_loader_monitor.cc b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/dynamic_loader_monitor.cc new file mode 100644 index 0000000..3a9764d --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/dynamic_loader_monitor.cc @@ -0,0 +1,94 @@ +#include /* getenv */ +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "dobby.h" + +#include "dobby/common.h" + +#define LOG_TAG "DynamicLoaderMonitor" + +std::unordered_map traced_dlopen_handle_list; + +static void *(*orig_dlopen)(const char *__file, int __mode); +static void *fake_dlopen(const char *__file, int __mode) { + void *result = orig_dlopen(__file, __mode); + if (result != NULL && __file) { + char *traced_filename = (char *)malloc(MAXPATHLEN); + // FIXME: strncpy + strcpy(traced_filename, __file); + INFO_LOG("[-] dlopen handle: %s", __file); + traced_dlopen_handle_list.insert(std::make_pair(result, (const char *)traced_filename)); + } + return result; +} + +static void *(*orig_loader_dlopen)(const char *filename, int flags, const void *caller_addr); +static void *fake_loader_dlopen(const char *filename, int flags, const void *caller_addr) { + void *result = orig_loader_dlopen(filename, flags, caller_addr); + if (result != NULL) { + char *traced_filename = (char *)malloc(MAXPATHLEN); + // FIXME: strncpy + strcpy(traced_filename, filename); + INFO_LOG("[-] dlopen handle: %s", filename); + traced_dlopen_handle_list.insert(std::make_pair(result, (const char *)traced_filename)); + } + return result; +} + +static const char *get_traced_filename(void *handle, bool removed) { + std::unordered_map::iterator it; + it = traced_dlopen_handle_list.find(handle); + if (it != traced_dlopen_handle_list.end()) { + if (removed) + traced_dlopen_handle_list.erase(it); + return it->second; + } + return NULL; +} + +static void *(*orig_dlsym)(void *__handle, const char *__symbol); +static void *fake_dlsym(void *__handle, const char *__symbol) { + const char *traced_filename = get_traced_filename(__handle, false); + if (traced_filename) { + INFO_LOG("[-] dlsym: %s, symbol: %s", traced_filename, __symbol); + } + return orig_dlsym(__handle, __symbol); +} + +static int (*orig_dlclose)(void *__handle); +static int fake_dlclose(void *__handle) { + const char *traced_filename = get_traced_filename(__handle, true); + if (traced_filename) { + INFO_LOG("[-] dlclose: %s", traced_filename); + free((void *)traced_filename); + } + return orig_dlclose(__handle); +} + +#if 0 +__attribute__((constructor)) static void ctor() { +#if defined(__ANDROID__) +#if 0 + void *dl = dlopen("libdl.so", RTLD_LAZY); + void *__loader_dlopen = dlsym(dl, "__loader_dlopen"); +#endif + DobbyHook((void *)DobbySymbolResolver(NULL, "__loader_dlopen"), (void *)fake_loader_dlopen, + (void **)&orig_loader_dlopen); +#else + DobbyHook((void *)DobbySymbolResolver(NULL, "dlopen"), (void *)fake_dlopen, (void **)&orig_dlopen); +#endif + + DobbyHook((void *)dlsym, (void *)fake_dlsym, (void **)&orig_dlsym); + DobbyHook((void *)dlclose, (void *)fake_dlclose, (void **)&orig_dlclose); +} +#endif diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/file_operation_monitor.cc b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/file_operation_monitor.cc new file mode 100644 index 0000000..d4f09ca --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/file_operation_monitor.cc @@ -0,0 +1,97 @@ +#include /* getenv */ +#include +#include + +#include +#include + +#include +#include + +#include + +#include "./dobby_monitor.h" + +std::unordered_map *TracedFopenFileList; + +FILE *(*orig_fopen)(const char *filename, const char *mode); +FILE *fake_fopen(const char *filename, const char *mode) { + FILE *result = NULL; + result = orig_fopen(filename, mode); + if (result != NULL) { + char *traced_filename = (char *)malloc(MAXPATHLEN); + // FIXME: strncpy + strcpy(traced_filename, filename); + std::cout << "[-] trace file: " << filename << std::endl; + TracedFopenFileList->insert(std::make_pair(result, traced_filename)); + } + return result; +} + +static const char *GetFileDescriptorTraced(FILE *stream, bool removed) { + std::unordered_map::iterator it; + it = TracedFopenFileList->find(stream); + if (it != TracedFopenFileList->end()) { + if (removed) + TracedFopenFileList->erase(it); + return it->second; + } + return NULL; +} + +size_t (*orig_fread)(void *ptr, size_t size, size_t count, FILE *stream); +size_t fake_fread(void *ptr, size_t size, size_t count, FILE *stream) { + const char *file_name = GetFileDescriptorTraced(stream, false); + if (file_name) { + LOG("[-] fread: %s, buffer: %p\n", file_name, ptr); + } + return orig_fread(ptr, size, count, stream); +} + +size_t (*orig_fwrite)(const void *ptr, size_t size, size_t count, FILE *stream); +size_t fake_fwrite(void *ptr, size_t size, size_t count, FILE *stream) { + const char *file_name = GetFileDescriptorTraced(stream, false); + if (file_name) { + LOG("[-] fwrite %s\n from %p\n", file_name, ptr); + } + return orig_fwrite(ptr, size, count, stream); +} + +__attribute__((constructor)) void __main() { + + TracedFopenFileList = new std::unordered_map(); + +#if defined(__APPLE__) +#include +#if (TARGET_OS_IPHONE || TARGET_OS_MAC) + std::ifstream file; + file.open("/System/Library/CoreServices/SystemVersion.plist"); + std::cout << file.rdbuf(); +#endif +#endif + + // DobbyHook((void *)fopen, (void *)fake_fopen, (void **)&orig_fopen); + // DobbyHook((void *)fwrite, (void *)fake_fwrite, (void **)&orig_fwrite); + // DobbyHook((void *)fread, (void *)fake_fread, (void **)&orig_fread); + + char *home = getenv("HOME"); + char *subdir = (char *)"/Library/Caches/"; + + std::string filePath = std::string(home) + std::string(subdir) + "temp.log"; + + char buffer[64]; + memset(buffer, 'B', 64); + + FILE *fd = fopen(filePath.c_str(), "w+"); + if (!fd) + std::cout << "[!] open " << filePath << "failed!\n"; + + fwrite(buffer, 64, 1, fd); + fflush(fd); + fseek(fd, 0, SEEK_SET); + memset(buffer, 0, 64); + + fread(buffer, 64, 1, fd); + + return; +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/memory_operation_instrument.cc b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/memory_operation_instrument.cc new file mode 100644 index 0000000..0298275 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/memory_operation_instrument.cc @@ -0,0 +1,58 @@ +#include "./dobby_monitor.h" + +#include +#include +#include + +static uintptr_t getCallFirstArg(DobbyRegisterContext *ctx) { + uintptr_t result; +#if defined(_M_X64) || defined(__x86_64__) +#if defined(_WIN32) + result = ctx->general.regs.rcx; +#else + result = ctx->general.regs.rdi; +#endif +#elif defined(__arm64__) || defined(__aarch64__) + result = ctx->general.regs.x0; +#elif defined(__arm__) + result = ctx->general.regs.r0; +#else +#error "Not Support Architecture." +#endif + return result; +} + +void format_integer_manually(char *buf, uint64_t integer) { + int tmp = 0; + for (tmp = (int)integer; tmp > 0; tmp = (tmp >> 4)) { + buf += (tmp % 16); + buf--; + } +} + +// [ATTENTION]: +// printf will call 'malloc' internally, and will crash in a loop. +// so, use 'puts' is a better choice. +void malloc_handler(DobbyRegisterContext *ctx, const InterceptEntry *info) { + size_t size_ = 0; + size_ = getCallFirstArg(ctx); + char *buffer_ = (char *)"[-] function malloc first arg: 0x00000000.\n"; + format_integer_manually(strchr(buffer_, '.') - 1, size_); + puts(buffer_); +} + +void free_handler(DobbyRegisterContext *ctx, const InterceptEntry *info) { + uintptr_t mem_ptr; + + mem_ptr = getCallFirstArg(ctx); + + char *buffer = (char *)"[-] function free first arg: 0x00000000.\n"; + format_integer_manually(strchr(buffer, '.') - 1, mem_ptr); + puts(buffer); +} + +__attribute__((constructor)) static void ctor() { + // DobbyInstrument((void *)mmap, malloc_handler); + // DobbyInstrument((void *)free, free_handler); + return; +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/posix_file_descriptor_operation_monitor.cc b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/posix_file_descriptor_operation_monitor.cc new file mode 100644 index 0000000..4c0db6c --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/posix_file_descriptor_operation_monitor.cc @@ -0,0 +1,120 @@ +#include /* getenv */ +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include "dobby.h" +#include "dobby/common.h" + +#define LOG_TAG "PosixFileOperationMonitor" + +std::unordered_map *posix_file_descriptors; + +int (*orig_open)(const char *pathname, int flags, ...); +int fake_open(const char *pathname, int flags, ...) { + mode_t mode = 0; + if (flags & O_CREAT) { + va_list args; + va_start(args, flags); + mode = (mode_t)va_arg(args, int); + va_end(args); + } + + int result = orig_open(pathname, flags, mode); + if (result != -1) { + char *traced_filename = (char *)malloc(MAXPATHLEN); + // FIXME: strncpy + strcpy(traced_filename, pathname); + INFO_LOG("[-] trace open handle: %s", pathname); + + if (posix_file_descriptors == NULL) { + posix_file_descriptors = new std::unordered_map(); + } + posix_file_descriptors->insert(std::make_pair(result, (const char *)traced_filename)); + } + return result; +} + +int (*orig___open)(const char *pathname, int flags, int mode); +int fake___open(const char *pathname, int flags, int mode) { + char *traced_filename = NULL; + if (pathname) { + traced_filename = (char *)malloc(MAXPATHLEN); + // FIXME: strncpy + strcpy(traced_filename, pathname); + INFO_LOG("[-] trace open handle: ", pathname); + } + int result = orig___open(pathname, flags, mode); + if (result != -1) { + if (posix_file_descriptors == NULL) { + posix_file_descriptors = new std::unordered_map(); + } + posix_file_descriptors->insert(std::make_pair(result, (const char *)traced_filename)); + } + return result; +} + +static const char *get_traced_filename(int fd, bool removed) { + if (posix_file_descriptors == NULL) + return NULL; + std::unordered_map::iterator it; + it = posix_file_descriptors->find(fd); + if (it != posix_file_descriptors->end()) { + if (removed) + posix_file_descriptors->erase(it); + return it->second; + } + return NULL; +} + +ssize_t (*orig_read)(int fd, void *buf, size_t count); +ssize_t fake_read(int fd, void *buf, size_t count) { + const char *traced_filename = get_traced_filename(fd, false); + if (traced_filename) { + INFO_LOG("[-] read: %s, buffer: %p, size: %zu", traced_filename, buf, count); + } + return orig_read(fd, buf, count); +} + +ssize_t (*orig_write)(int fd, const void *buf, size_t count); +ssize_t fake_write(int fd, const void *buf, size_t count) { + const char *traced_filename = get_traced_filename(fd, false); + if (traced_filename) { + INFO_LOG("[-] write: %s, buffer: %p, size: %zu", traced_filename, buf, count); + } + return orig_write(fd, buf, count); +} +int (*orig_close)(int fd); +int fake_close(int fd) { + const char *traced_filename = get_traced_filename(fd, true); + if (traced_filename) { + INFO_LOG("[-] close: %s", traced_filename); + free((void *)traced_filename); + } + return orig_close(fd); +} + +#if 0 +__attribute__((constructor)) static void ctor() { + DobbyHook((void *)DobbySymbolResolver(NULL, "open"), (void *)fake_open, (void **)&orig_open); + + DobbyHook((void *)DobbySymbolResolver(NULL, "write"), (void *)fake_write, (void **)&orig_write); + + DobbyHook((void *)DobbySymbolResolver(NULL, "read"), (void *)fake_read, (void **)&orig_read); + + DobbyHook((void *)DobbySymbolResolver(NULL, "close"), (void *)fake_close, (void **)&orig_close); +} +#endif diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/posix_socket_network_monitor.cc b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/posix_socket_network_monitor.cc new file mode 100644 index 0000000..a6e6e6c --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ApplicationEventMonitor/posix_socket_network_monitor.cc @@ -0,0 +1,57 @@ +#include /* getenv */ +#include +#include + +#include +#include + +#include + +#include + +#include +#include + +std::unordered_map posix_socket_file_descriptors; + +int (*orig_bind)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +int fake_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { +} + +static const char *get_traced_socket(int fd, bool removed) { + std::unordered_map::iterator it; + it = posix_socket_file_descriptors.find(fd); + if (it != posix_socket_file_descriptors.end()) { + if (removed) + posix_socket_file_descriptors.erase(it); + return it->second; + } + return NULL; +} + +int (*orig_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +int fake_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + const char *traced_socket = get_traced_socket(sockfd, false); + if (traced_socket) { + INFO_LOG("[-] connect: %s\n", traced_socket); + } + return orig_connect(sockfd, addr, addrlen); +} + +ssize_t (*orig_send)(int sockfd, const void *buf, size_t len, int flags); +ssize_t fake_send(int sockfd, const void *buf, size_t len, int flags) { + const char *traced_socket = get_traced_socket(sockfd, false); + if (traced_socket) { + INFO_LOG("[-] send: %s, buf: %p, len: %zu\n", traced_socket, buf, len); + } + return orig_send(sockfd, buf, len, flags); +} + +ssize_t (*orig_recv)(int sockfd, void *buf, size_t len, int flags); +ssize_t fake_recv(int sockfd, void *buf, size_t len, int flags) { + const char *traced_socket = get_traced_socket(sockfd, false); + if (traced_socket) { + INFO_LOG("[-] recv: %s, buf: %p, len: %zu\n", traced_socket, buf, len); + } + return orig_recv(sockfd, buf, len, flags); +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_demo.cc b/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_demo.cc new file mode 100644 index 0000000..f68264c --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_demo.cc @@ -0,0 +1,36 @@ +#include "dobby.h" + +#include "bionic_linker_util.h" + +#include "logging/logging.h" + +#include + +#define LOG_TAG "BionicLinkerUtil" + +__attribute__((constructor)) static void ctor() { + const char *lib = NULL; + +#if defined(__LP64__) + lib = "/system/lib64/libandroid_runtime.so"; +#else + lib = "/system/lib/libandroid_runtime.so"; +#endif + + void *vm = NULL; + + vm = DobbySymbolResolver(lib, "_ZN7android14AndroidRuntime7mJavaVME"); + INFO_LOG("DobbySymbolResolver::vm %p", vm); + +#if 0 + linker_disable_namespace_restriction(); + void *handle = NULL; + handle = dlopen(lib, RTLD_LAZY); + vm = dlsym(handle, "_ZN7android14AndroidRuntime7mJavaVME"); +#else + void *handle = NULL; + handle = linker_dlopen(lib, RTLD_LAZY); + vm = dlsym(handle, "_ZN7android14AndroidRuntime7mJavaVME"); +#endif + INFO_LOG("vm %p", vm); +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_util.cc b/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_util.cc new file mode 100644 index 0000000..8b995e8 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_util.cc @@ -0,0 +1,197 @@ +#include "bionic_linker_util.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "dobby.h" +#include "dobby_symbol_resolver.h" + +#include "dobby/common.h" + +#undef LOG_TAG +#define LOG_TAG "BionicLinkerUtil" + +#undef Q +#define Q 29 +// impl at "dobby_symbol_resolver.cc" +extern void *resolve_elf_internal_symbol(const char *library_name, const char *symbol_name); + +#include +static int get_android_system_version() { + char os_version_str[PROP_VALUE_MAX + 1]; + __system_property_get("ro.build.version.sdk", os_version_str); + int os_version_int = atoi(os_version_str); + return os_version_int; +} + +static const char *get_android_linker_path() { +#if __LP64__ + if (get_android_system_version() >= Q) { + return (const char *)"/apex/com.android.runtime/bin/linker64"; + } else { + return (const char *)"/system/bin/linker64"; + } +#else + if (get_android_system_version() >= Q) { + return (const char *)"/apex/com.android.runtime/bin/linker"; + } else { + return (const char *)"/system/bin/linker"; + } +#endif +} + +PUBLIC void *linker_dlopen(const char *filename, int flag) { + typedef void *(*__loader_dlopen_t)(const char *filename, int flags, const void *caller_addr); + static __loader_dlopen_t __loader_dlopen = NULL; + if (!__loader_dlopen) + __loader_dlopen = (__loader_dlopen_t)DobbySymbolResolver(NULL, "__loader_dlopen"); + + // fake caller address + void *open_ptr = dlsym(RTLD_DEFAULT, "open"); + return __loader_dlopen(filename, flag, (const void *)open_ptr); +} + +std::vector linker_solist; +std::vector linker_get_solist() { + if (!linker_solist.empty()) { + linker_solist.clear(); + } + + static soinfo_t (*solist_get_head)() = NULL; + if (!solist_get_head) + solist_get_head = + (soinfo_t(*)())resolve_elf_internal_symbol(get_android_linker_path(), "__dl__Z15solist_get_headv"); + + static soinfo_t (*solist_get_somain)() = NULL; + if (!solist_get_somain) + solist_get_somain = + (soinfo_t(*)())resolve_elf_internal_symbol(get_android_linker_path(), "__dl__Z17solist_get_somainv"); + + static addr_t *solist_head = NULL; + if (!solist_head) + solist_head = (addr_t *)solist_get_head(); + + static addr_t somain = 0; + if (!somain) + somain = (addr_t)solist_get_somain(); + + // Generate the name for an offset. +#define PARAM_OFFSET(type_, member_) __##type_##__##member_##__offset_ +#define STRUCT_OFFSET PARAM_OFFSET + int STRUCT_OFFSET(solist, next) = 0; + for (size_t i = 0; i < 1024 / sizeof(void *); i++) { + if (*(addr_t *)((addr_t)solist_head + i * sizeof(void *)) == somain) { + STRUCT_OFFSET(solist, next) = i * sizeof(void *); + break; + } + } + + linker_solist.push_back(solist_head); + + addr_t sonext = 0; + sonext = *(addr_t *)((addr_t)solist_head + STRUCT_OFFSET(solist, next)); + while (sonext) { + linker_solist.push_back((void *)sonext); + sonext = *(addr_t *)((addr_t)sonext + STRUCT_OFFSET(solist, next)); + } + + return linker_solist; +} + +char *linker_soinfo_get_realpath(soinfo_t soinfo) { + static char *(*_get_realpath)(soinfo_t) = NULL; + if (!_get_realpath) + _get_realpath = + (char *(*)(soinfo_t))resolve_elf_internal_symbol(get_android_linker_path(), "__dl__ZNK6soinfo12get_realpathEv"); + return _get_realpath(soinfo); +} + +uintptr_t linker_soinfo_to_handle(soinfo_t soinfo) { + static uintptr_t (*_linker_soinfo_to_handle)(soinfo_t) = NULL; + if (!_linker_soinfo_to_handle) + _linker_soinfo_to_handle = + (uintptr_t(*)(soinfo_t))resolve_elf_internal_symbol(get_android_linker_path(), "__dl__ZN6soinfo9to_handleEv"); + return _linker_soinfo_to_handle(soinfo); +} + +typedef void *android_namespace_t; +android_namespace_t linker_soinfo_get_primary_namespace(soinfo_t soinfo) { + static android_namespace_t (*_get_primary_namespace)(soinfo_t) = NULL; + if (!_get_primary_namespace) + _get_primary_namespace = (android_namespace_t(*)(soinfo_t))resolve_elf_internal_symbol( + get_android_linker_path(), "__dl__ZN6soinfo21get_primary_namespaceEv"); + return _get_primary_namespace(soinfo); +} + +void linker_iterate_soinfo(int (*cb)(soinfo_t soinfo)) { + auto solist = linker_get_solist(); + for (auto it = solist.begin(); it != solist.end(); it++) { + int ret = cb(*it); + if (ret != 0) + break; + } +} + +static int iterate_soinfo_cb(soinfo_t soinfo) { + android_namespace_t ns = NULL; + ns = linker_soinfo_get_primary_namespace(soinfo); + INFO_LOG("lib: %s", linker_soinfo_get_realpath(soinfo)); + + // set is_isolated_ as false + // no need for this actually + int STRUCT_OFFSET(android_namespace_t, is_isolated_) = 0x8; + *(uint8_t *)((addr_t)ns + STRUCT_OFFSET(android_namespace_t, is_isolated_)) = false; + + std::vector ld_library_paths = {"/system/lib64", "/sytem/lib"}; + if (get_android_system_version() >= Q) { + ld_library_paths.push_back("/apex/com.android.runtime/lib64"); + ld_library_paths.push_back("/apex/com.android.runtime/lib"); + } + int STRUCT_OFFSET(android_namespace_t, ld_library_paths_) = 0x10; + if (*(void **)((addr_t)ns + STRUCT_OFFSET(android_namespace_t, ld_library_paths_))) { + std::vector orig_ld_library_paths = + *(std::vector *)((addr_t)ns + STRUCT_OFFSET(android_namespace_t, ld_library_paths_)); + orig_ld_library_paths.insert(orig_ld_library_paths.end(), ld_library_paths.begin(), ld_library_paths.end()); + + // remove duplicates + { + std::set paths(orig_ld_library_paths.begin(), orig_ld_library_paths.end()); + orig_ld_library_paths.assign(paths.begin(), paths.end()); + } + } else { + *(std::vector *)((addr_t)ns + STRUCT_OFFSET(android_namespace_t, ld_library_paths_)) = + std::move(ld_library_paths); + } + return 0; +} + +bool (*orig_linker_namespace_is_is_accessible)(android_namespace_t ns, const std::string &file); +bool linker_namespace_is_is_accessible(android_namespace_t ns, const std::string &file) { + INFO_LOG("check %s", file.c_str()); + return true; + return orig_linker_namespace_is_is_accessible(ns, file); +} + +void linker_disable_namespace_restriction() { + linker_iterate_soinfo(iterate_soinfo_cb); + + // no need for this actually + void *linker_namespace_is_is_accessible_ptr = resolve_elf_internal_symbol( + get_android_linker_path(), "__dl__ZN19android_namespace_t13is_accessibleERKNSt3__112basic_" + "stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE"); + DobbyHook(linker_namespace_is_is_accessible_ptr, (void *)linker_namespace_is_is_accessible, + (void **)&orig_linker_namespace_is_is_accessible); + + INFO_LOG("disable namespace restriction done"); +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_util.h b/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_util.h new file mode 100644 index 0000000..55c2911 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/BionicLinkerUtil/bionic_linker_util.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *soinfo_t; + +soinfo_t linker_dlopen(const char *filename, int flag); + +char *linker_soinfo_get_realpath(soinfo_t soinfo); + +uintptr_t linker_soinfo_to_handle(soinfo_t soinfo); + +void linker_iterate_soinfo(int (*cb)(soinfo_t soinfo)); + +void linker_disable_namespace_restriction(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/CMakeLists.txt b/app/src/main/cpp/Dobby/builtin-plugin/CMakeLists.txt new file mode 100644 index 0000000..3730cb9 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/CMakeLists.txt @@ -0,0 +1,15 @@ +if (Plugin.ImportTableReplace AND SYSTEM.Darwin) + message(STATUS "[Dobby] Enable got hook") + include_directories(builtin-plugin/ImportTableReplace) + add_subdirectory(builtin-plugin/ImportTableReplace) +endif () + +if (Plugin.Android.BionicLinkerUtil) + if (NOT SYSTEM.Android) + message(FATAL_ERROR "[!] Plugin.Android.BionicLinkerUtil only works on Android.") + endif () + message(STATUS "[Dobby] Enable Plugin.Android.BionicLinkerUtil") + set(dobby.plugin.SOURCE_FILE_LIST ${dobby.plugin.SOURCE_FILE_LIST} + BionicLinkerUtil/bionic_linker_util.cc + ) +endif () \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/CMakeLists.txt b/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/CMakeLists.txt new file mode 100644 index 0000000..1a2b4b3 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(dobby_import_replace INTERFACE + dobby_import_replace.cc + ) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/dobby_import_replace.cc b/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/dobby_import_replace.cc new file mode 100644 index 0000000..eab1af9 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/dobby_import_replace.cc @@ -0,0 +1,192 @@ +#include "dobby_import_replace.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "dobby/common.h" + +#include "logging/logging.h" + +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#if defined(__LP64__) +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct section_64 section_t; +typedef struct nlist_64 nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct section section_t; +typedef struct nlist nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT +#endif + +static void *iterate_indirect_symtab(char *symbol_name, section_t *section, intptr_t slide, nlist_t *symtab, + char *strtab, uint32_t *indirect_symtab) { + const bool is_data_const = strcmp(section->segname, "__DATA_CONST") == 0; + uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; + void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); + + vm_prot_t old_protection = VM_PROT_READ; + if (is_data_const) { + mprotect(indirect_symbol_bindings, section->size, PROT_READ | PROT_WRITE); + } + + for (uint i = 0; i < section->size / sizeof(void *); i++) { + uint32_t symtab_index = indirect_symbol_indices[i]; + if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || + symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { + continue; + } + uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; + char *local_symbol_name = strtab + strtab_offset; + bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; + if (strcmp(local_symbol_name, symbol_name) == 0) { + return &indirect_symbol_bindings[i]; + } + if (local_symbol_name[0] == '_') { + if (strcmp(symbol_name, &local_symbol_name[1]) == 0) { + return &indirect_symbol_bindings[i]; + } + } + } + + if (is_data_const && 0) { + int protection = 0; + if (old_protection & VM_PROT_READ) { + protection |= PROT_READ; + } + if (old_protection & VM_PROT_WRITE) { + protection |= PROT_WRITE; + } + if (old_protection & VM_PROT_EXECUTE) { + protection |= PROT_EXEC; + } + mprotect(indirect_symbol_bindings, section->size, protection); + } + return NULL; +} + +static void *get_global_offset_table_stub(mach_header_t *header, char *symbol_name) { + segment_command_t *curr_seg_cmd; + segment_command_t *text_segment, *data_segment, *linkedit_segment; + struct symtab_command *symtab_cmd = NULL; + struct dysymtab_command *dysymtab_cmd = NULL; + + uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += curr_seg_cmd->cmdsize) { + curr_seg_cmd = (segment_command_t *)cur; + if (curr_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(curr_seg_cmd->segname, "__LINKEDIT") == 0) { + linkedit_segment = curr_seg_cmd; + } else if (strcmp(curr_seg_cmd->segname, "__DATA") == 0) { + data_segment = curr_seg_cmd; + } else if (strcmp(curr_seg_cmd->segname, "__TEXT") == 0) { + text_segment = curr_seg_cmd; + } + } else if (curr_seg_cmd->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command *)curr_seg_cmd; + } else if (curr_seg_cmd->cmd == LC_DYSYMTAB) { + dysymtab_cmd = (struct dysymtab_command *)curr_seg_cmd; + } + } + + if (!symtab_cmd || !linkedit_segment || !linkedit_segment) { + return NULL; + } + + uintptr_t slide = (uintptr_t)header - (uintptr_t)text_segment->vmaddr; + uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; + nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); + char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); + uint32_t symtab_count = symtab_cmd->nsyms; + + uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); + + cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += curr_seg_cmd->cmdsize) { + curr_seg_cmd = (segment_command_t *)cur; + if (curr_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(curr_seg_cmd->segname, "__DATA") != 0 && strcmp(curr_seg_cmd->segname, "__DATA_CONST") != 0) { + continue; + } + for (uint j = 0; j < curr_seg_cmd->nsects; j++) { + section_t *sect = (section_t *)(cur + sizeof(segment_command_t)) + j; + if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { + void *stub = iterate_indirect_symtab(symbol_name, sect, slide, symtab, strtab, indirect_symtab); + if (stub) + return stub; + } + if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { + void *stub = iterate_indirect_symtab(symbol_name, sect, slide, symtab, strtab, indirect_symtab); + if (stub) + return stub; + } + } + } + } + + return NULL; +} + +PUBLIC int DobbyImportTableReplace(char *image_name, char *symbol_name, void *fake_func, void **orig_func_ptr) { + std::vector ProcessModuleMap = ProcessRuntimeUtility::GetProcessModuleMap(); + + for (auto module : ProcessModuleMap) { + if (image_name != NULL && strstr(module.path, image_name) == NULL) + continue; + + addr_t header = (addr_t)module.load_address; + size_t slide = 0; + +#if 0 + if (header) { + if (((struct mach_header *)header)->magic == MH_MAGIC_64) + slide = macho_kit_get_slide64(header); + } +#endif + +#if 0 + INFO_LOG("resolve image: %s", module.path); +#endif + + uint32_t nlist_count = 0; + nlist_t *nlist_array = 0; + char *string_pool = 0; + + void *stub = get_global_offset_table_stub((mach_header_t *)header, symbol_name); + if (stub) { + void *orig_func; + orig_func = *(void **)stub; +#if __has_feature(ptrauth_calls) + orig_func = ptrauth_strip(orig_func, ptrauth_key_asia); + orig_func = ptrauth_sign_unauthenticated(orig_func, ptrauth_key_asia, 0); +#endif + *orig_func_ptr = orig_func; + +#if __has_feature(ptrauth_calls) + fake_func = (void *)ptrauth_strip(fake_func, ptrauth_key_asia); + fake_func = ptrauth_sign_unauthenticated(fake_func, ptrauth_key_asia, stub); +#endif + *(void **)stub = fake_func; + } + + if (image_name) + return 0; + } + return -1; +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/dobby_import_replace.h b/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/dobby_import_replace.h new file mode 100644 index 0000000..c51b0f7 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/dobby_import_replace.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +// int DobbyImportTableReplace(char *image_name, char *symbol_name, void *fake_func, void **orig_func); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/CMakeLists.txt b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/CMakeLists.txt new file mode 100644 index 0000000..5430d9f --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(objc_runtime_replace + dobby_objc_runtime_replace.mm + ) + +target_link_libraries(objc_runtime_replace + "-framework Foundation" + ) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/dobby_objc_runtime_repalce.h b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/dobby_objc_runtime_repalce.h new file mode 100644 index 0000000..da959e7 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/dobby_objc_runtime_repalce.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +IMP DobbyObjcReplace(Class _class, SEL _selector, IMP replacement); + +void DobbyObjcReplaceEx(const char *class_name, const char *selector_name, void *fake_impl, void **orig_impl); + +void *DobbyObjcResolveMethodImp(const char *class_name, const char *selector_name); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/dobby_objc_runtime_replace.mm b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/dobby_objc_runtime_replace.mm new file mode 100644 index 0000000..d794880 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/dobby_objc_runtime_replace.mm @@ -0,0 +1,54 @@ +#include "dobby_objc_runtime_repalce.h" + +#include +#include + +/* clang -rewrite-objc main.m */ + +IMP DobbyObjcReplace(Class class_, SEL sel_, IMP fake_impl) { + Method method_ = class_getInstanceMethod(class_, sel_); + if (!method_) + method_ = class_getClassMethod(class_, sel_); + + if (!method_) { + // DEBUG_LOG("Not found class: %s, selector: %s method\n", class_getName(class_), sel_getName(sel_)); + return NULL; + } + + return method_setImplementation(method_, (IMP)fake_impl); +} + +void DobbyObjcReplaceEx(const char *class_name, const char *selector_name, void *fake_impl, void **out_orig_impl) { + Class class_ = objc_getClass(class_name); + SEL sel_ = sel_registerName(selector_name); + + Method method_ = class_getInstanceMethod(class_, sel_); + if (!method_) { + method_ = class_getClassMethod(class_, sel_); + if (!method_) { + // ERROR_LOG("Not found class: %s, selector: %s method\n", class_name, selector_name); + return; + } + } + + auto orig_impl = (void *)method_setImplementation(method_, (IMP)fake_impl); + if (out_orig_impl) { + *out_orig_impl = orig_impl; + } + return; +} + +void *DobbyObjcResolveMethodImp(const char *class_name, const char *selector_name) { + Class class_ = objc_getClass(class_name); + SEL sel_ = sel_registerName(selector_name); + + Method method_ = class_getInstanceMethod(class_, sel_); + if (!method_) + method_ = class_getClassMethod(class_, sel_); + + if (!method_) { + // DEBUG_LOG("Not found class: %s, selector: %s method\n", class_name, selector_name); + return NULL; + } + return (void *)method_getImplementation(method_); +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/CMakeLists.txt b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/CMakeLists.txt new file mode 100644 index 0000000..ddddfd7 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/CMakeLists.txt @@ -0,0 +1,23 @@ +add_library(supervisor_call_monitor STATIC + mach_system_call_log_handler.cc + system_call_log_handler.cc + supervisor_call_monitor.cc + sensitive_api_monitor.cc + misc_utility.cc + ) +target_link_libraries(supervisor_call_monitor + misc_helper + dobby + ) + +add_library(test_supervisor_call_monitor SHARED + test_supervisor_call_monitor.cc + ) +target_link_libraries(test_supervisor_call_monitor + supervisor_call_monitor +) + +include_directories( + . +) + diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/README b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/README new file mode 100644 index 0000000..3832eb5 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/README @@ -0,0 +1 @@ +Monitor all supervisor call \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/mach_system_call_log_handler.cc b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/mach_system_call_log_handler.cc new file mode 100644 index 0000000..970a1be --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/mach_system_call_log_handler.cc @@ -0,0 +1,193 @@ +#include "dobby/dobby_internal.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "misc-helper/async_logger.h" +#include "PlatformUtil/ProcessRuntimeUtility.h" +#include "SupervisorCallMonitor/misc_utility.h" +#include "SupervisorCallMonitor/supervisor_call_monitor.h" + +#include "XnuInternal/syscall_sw.c" + +#include "XnuInternal/mach/clock_priv.h" +#include "XnuInternal/mach/clock_reply.h" +#include "XnuInternal/mach/clock.h" +#include "XnuInternal/mach/exc.h" +#include "XnuInternal/mach/host_priv.h" +#include "XnuInternal/mach/host_security.h" +#include "XnuInternal/mach/lock_set.h" +#include "XnuInternal/mach/mach_host.h" +#include "XnuInternal/mach/mach_port.h" +#include "XnuInternal/mach/mach_vm.h" +#include "XnuInternal/mach/mach_voucher.h" +#include "XnuInternal/mach/memory_entry.h" +#include "XnuInternal/mach/processor_set.h" +#include "XnuInternal/mach/processor.h" +#include "XnuInternal/mach/task.h" +#include "XnuInternal/mach/thread_act.h" +#include "XnuInternal/mach/vm_map.h" + +typedef struct { + char *mach_msg_name; + int mach_msg_id; +} mach_msg_entry_t; + +// clang-format off +mach_msg_entry_t mach_msg_array[] = { + subsystem_to_name_map_clock_priv, + subsystem_to_name_map_clock_reply, + subsystem_to_name_map_clock, + subsystem_to_name_map_exc, + subsystem_to_name_map_host_priv, + subsystem_to_name_map_host_security, + subsystem_to_name_map_lock_set, + subsystem_to_name_map_mach_host, + subsystem_to_name_map_mach_port, + subsystem_to_name_map_mach_vm, + subsystem_to_name_map_mach_voucher, + subsystem_to_name_map_memory_entry, + subsystem_to_name_map_processor_set, + subsystem_to_name_map_processor, + subsystem_to_name_map_task, + subsystem_to_name_map_thread_act, + subsystem_to_name_map_vm_map, +}; +// clang-format on + +#define PRIME_NUMBER 8387 +char *mach_msg_name_table[PRIME_NUMBER] = {0}; +static int hash_mach_msg_num_to_ndx(int mach_msg_num) { + return mach_msg_num % PRIME_NUMBER; +} +static void mach_msg_id_hash_table_init() { + static bool initialized = false; + if (initialized == true) { + return; + } + initialized = true; + + int count = sizeof(mach_msg_array) / sizeof(mach_msg_array[0]); + for (size_t i = 0; i < count; i++) { + mach_msg_entry_t entry = mach_msg_array[i]; + int ndx = hash_mach_msg_num_to_ndx(entry.mach_msg_id); + mach_msg_name_table[ndx] = entry.mach_msg_name; + } +} + +const char *mach_syscall_num_to_str(int num) { + return mach_syscall_name_table[0 - num]; +} + +char *mach_msg_id_to_str(int msgh_id) { + int ndx = hash_mach_msg_num_to_ndx(msgh_id); + return mach_msg_name_table[ndx]; +} + +char *mach_msg_to_str(mach_msg_header_t *msg) { + static mach_port_t self_port = MACH_PORT_NULL; + + if (self_port == MACH_PORT_NULL) { + self_port = mach_task_self(); + } + + if (msg->msgh_remote_port == self_port) { + return mach_msg_id_to_str(msg->msgh_id); + } + return NULL; +} + +static addr_t getCallFirstArg(DobbyRegisterContext *ctx) { + addr_t result; +#if defined(_M_X64) || defined(__x86_64__) +#if defined(_WIN32) + result = ctx->general.regs.rcx; +#else + result = ctx->general.regs.rdi; +#endif +#elif defined(__arm64__) || defined(__aarch64__) + result = ctx->general.regs.x0; +#elif defined(__arm__) + result = ctx->general.regs.r0; +#else +#error "Not Support Architecture." +#endif + return result; +} + +static addr_t getRealLr(DobbyRegisterContext *ctx) { + addr_t closure_trampoline_reserved_stack = ctx->sp - sizeof(addr_t); + return *(addr_t *)closure_trampoline_reserved_stack; +} + +static addr_t fast_get_caller_from_main_binary(DobbyRegisterContext *ctx) { + static addr_t text_section_start = 0, text_section_end = 0; + static addr_t slide = 0; + if (text_section_start == 0 || text_section_end == 0) { + auto main = ProcessRuntimeUtility::GetProcessModule("mobilex"); + addr_t main_header = (addr_t)main.load_address; + + auto text_segment = macho_kit_get_segment_by_name((mach_header_t *)main_header, "__TEXT"); + slide = main_header - text_segment->vmaddr; + + auto text_section = macho_kit_get_section_by_name((mach_header_t *)main_header, "__TEXT", "__text"); + text_section_start = main_header + (addr_t)text_section->offset; + text_section_end = text_section_start + text_section->size; + } + + if (ctx == NULL) + return 0; + + addr_t lr = getRealLr(ctx); + if (lr > text_section_start && lr < text_section_end) + return lr - slide; + +#define MAX_STACK_ITERATE_LEVEL 8 + addr_t fp = ctx->fp; + if (fp == 0) + return 0; + for (int i = 0; i < MAX_STACK_ITERATE_LEVEL; i++) { + addr_t lr = *(addr_t *)(fp + sizeof(addr_t)); + if (lr > text_section_start && lr < text_section_end) + return lr - slide; + fp = *(addr_t *)fp; + if (fp == 0) + return 0; + } + return 0; +} + +static void mach_syscall_log_handler(DobbyRegisterContext *ctx, const InterceptEntry *info) { + addr_t caller = fast_get_caller_from_main_binary(ctx); + if (caller == 0) + return; + + char buffer[256] = {0}; + int syscall_rum = ctx->general.regs.x16; + if (syscall_rum == -31) { + // mach_msg_trap + mach_msg_header_t *msg = (typeof(msg))getCallFirstArg(ctx); + char *mach_msg_name = mach_msg_to_str(msg); + if (mach_msg_name) { + sprintf(buffer, "[mach msg svc] %s\n", mach_msg_name); + } else { + buffer[0] = 0; + } + } else if (syscall_rum < 0) { + sprintf(buffer, "[mach svc-%d] %s\n", syscall_rum, mach_syscall_num_to_str(syscall_rum)); + } + async_logger_print(buffer); +} + +void supervisor_call_monitor_register_mach_syscall_call_log_handler() { + mach_msg_id_hash_table_init(); + fast_get_caller_from_main_binary(NULL); + supervisor_call_monitor_register_handler(mach_syscall_log_handler); +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/misc_utility.cc b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/misc_utility.cc new file mode 100644 index 0000000..2a0a05b --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/misc_utility.cc @@ -0,0 +1,44 @@ +#include "misc_utility.h" + +#include + +segment_command_t *macho_kit_get_segment_by_name(mach_header_t *header, const char *segname) { + segment_command_t *curr_seg_cmd = NULL; + + curr_seg_cmd = (segment_command_t *)((addr_t)header + sizeof(mach_header_t)); + for (int i = 0; i < header->ncmds; i++) { + if (curr_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (!strncmp(curr_seg_cmd->segname, segname, sizeof(curr_seg_cmd->segname))) { + break; + } + } + curr_seg_cmd = (segment_command_t *)((addr_t)curr_seg_cmd + curr_seg_cmd->cmdsize); + } + + return curr_seg_cmd; +} + +section_t *macho_kit_get_section_by_name(mach_header_t *header, const char *segname, const char *sectname) { + section_t *section = NULL; + segment_command_t *segment = NULL; + + int i = 0; + + segment = macho_kit_get_segment_by_name(header, segname); + if (!segment) + goto finish; + + section = (section_t *)((addr_t)segment + sizeof(segment_command_t)); + for (i = 0; i < segment->nsects; ++i) { + if (!strncmp(section->sectname, sectname, sizeof(section->sectname))) { + break; + } + section += 1; + } + if (i == segment->nsects) { + section = NULL; + } + +finish: + return section; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/misc_utility.h b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/misc_utility.h new file mode 100644 index 0000000..1c356c0 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/misc_utility.h @@ -0,0 +1,28 @@ +#pragma once + +#include +typedef uintptr_t addr_t; + +#include +#include +#include + +#if defined(__LP64__) +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct section_64 section_t; +typedef struct nlist_64 nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct section section_t; +typedef struct nlist nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT +#endif + +// get macho segment by segment name +segment_command_t *macho_kit_get_segment_by_name(mach_header_t *mach_header, const char *segname); + +// get macho section by segment name and section name +section_t *macho_kit_get_section_by_name(mach_header_t *mach_header, const char *segname, const char *sectname); diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/sensitive_api_monitor.cc b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/sensitive_api_monitor.cc new file mode 100644 index 0000000..6ab0765 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/sensitive_api_monitor.cc @@ -0,0 +1,95 @@ +#include "dobby/dobby_internal.h" + +#include +#include +#include + +#include "SupervisorCallMonitor/supervisor_call_monitor.h" +#include "misc-helper/async_logger.h" + +#define PT_DENY_ATTACH 31 + +static void sensitive_api_handler(DobbyRegisterContext *ctx, const InterceptEntry *info) { + char buffer[256] = {0}; + int syscall_rum = ctx->general.regs.x16; + if (syscall_rum == 0) { + syscall_rum = (int)ctx->general.x[0]; + if (syscall_rum == SYS_ptrace) { + int request = ctx->general.x[1]; + if (request == PT_DENY_ATTACH) { + ctx->general.x[1] = 0; + // INFO_LOG("syscall svc ptrace deny"); + } + } + if (syscall_rum == SYS_exit) { + // INFO_LOG("syscall svc exit"); + } + } else if (syscall_rum > 0) { + if (syscall_rum == SYS_ptrace) { + int request = ctx->general.x[0]; + if (request == PT_DENY_ATTACH) { + ctx->general.x[0] = 0; + // INFO_LOG("svc ptrace deny"); + } + } + if (syscall_rum == SYS_exit) { + // INFO_LOG("svc exit"); + } + } + async_logger_print(buffer); +} + +static int get_func_svc_offset(addr_t func_addr) { + typedef int32_t arm64_instr_t; + for (int i = 0; i < 8; i++) { + arm64_instr_t *insn = (arm64_instr_t *)func_addr + i; + if (*insn == 0xd4001001) { + return i * sizeof(arm64_instr_t); + } + } + return 0; +} + +#include +__typeof(sysctl) *orig_sysctl; +int fake_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { + struct kinfo_proc *info = NULL; + int ret = orig_sysctl(name, namelen, oldp, oldlenp, newp, newlen); + if (name[0] == CTL_KERN && name[1] == KERN_PROC && name[2] == KERN_PROC_PID) { + info = (struct kinfo_proc *)oldp; + info->kp_proc.p_flag &= ~(P_TRACED); + } + return ret; +} + +void supervisor_call_monitor_register_sensitive_api_handler() { + char *sensitive_func_array[] = {"ptrace", "exit"}; + size_t count = sizeof(sensitive_func_array) / sizeof(char *); + for (size_t i = 0; i < count; i++) { + + addr_t func_addr = 0; + + char func_name[64] = {0}; + sprintf(func_name, "__%s", sensitive_func_array[i]); + func_addr = (addr_t)DobbySymbolResolver("libsystem_kernel.dylib", func_name); + if (func_addr == 0) { + func_addr = (addr_t)DobbySymbolResolver("libsystem_kernel.dylib", sensitive_func_array[i]); + } + if (func_addr == 0) { + INFO_LOG("not found func %s", sensitive_func_array[i]); + continue; + } + int func_svc_offset = get_func_svc_offset(func_addr); + if (func_svc_offset == 0) { + INFO_LOG("not found svc %s", sensitive_func_array[i]); + continue; + } + addr_t func_svc_addr = func_addr + func_svc_offset; + supervisor_call_monitor_register_svc(func_svc_addr); + } + + // =============== + DobbyHook((void *)sysctl, (void *)fake_sysctl, (void **)&orig_sysctl); + + supervisor_call_monitor_register_handler(sensitive_api_handler); +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/supervisor_call_monitor.cc b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/supervisor_call_monitor.cc new file mode 100644 index 0000000..c97b111 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/supervisor_call_monitor.cc @@ -0,0 +1,138 @@ +#include "SupervisorCallMonitor/misc_utility.h" +#include "dobby/dobby_internal.h" +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include "misc-helper/async_logger.h" + +#include +std::vector *g_supervisor_call_handlers; + +static const char *fast_get_main_app_bundle_udid() { + static char *main_app_bundle_udid = NULL; + if (main_app_bundle_udid) + return main_app_bundle_udid; + + auto main = ProcessRuntimeUtility::GetProcessModuleMap()[0]; + char main_binary_path[2048] = {0}; + if (realpath(main.path, main_binary_path) == NULL) + return NULL; + + char *bundle_udid_ndx = main_binary_path + strlen("/private/var/containers/Bundle/Application/"); + main_app_bundle_udid = (char *)malloc(36 + 1); + strncpy(main_app_bundle_udid, bundle_udid_ndx, 36); + main_app_bundle_udid[36] = 0; + return main_app_bundle_udid; +} + +static void common_supervisor_call_monitor_handler(DobbyRegisterContext *ctx, const InterceptEntry *info) { + if (g_supervisor_call_handlers == NULL) { + return; + } + for (auto handler : *g_supervisor_call_handlers) { + handler(ctx, info); + } +} + +void supervisor_call_monitor_register_handler(DBICallTy handler) { + if (g_supervisor_call_handlers == NULL) { + g_supervisor_call_handlers = new std::vector(); + } + g_supervisor_call_handlers->push_back(handler); +} + +std::vector *g_svc_addr_array; + +void supervisor_call_monitor_register_svc(addr_t svc_addr) { + if (g_svc_addr_array == NULL) { + g_svc_addr_array = new std::vector(); + } + + if (g_svc_addr_array) { + auto iter = g_svc_addr_array->begin(); + for (; iter != g_svc_addr_array->end(); iter++) { + if (*iter == svc_addr) + return; + } + } + + g_svc_addr_array->push_back(svc_addr); + DobbyInstrument((void *)svc_addr, common_supervisor_call_monitor_handler); + DLOG(2, "register supervisor_call_monitor at %p", svc_addr); +} + +void supervisor_call_monitor_register_image(void *header) { + auto text_section = macho_kit_get_section_by_name((mach_header_t *)header, "__TEXT", "__text"); + + addr_t insn_addr = (addr_t)header + (addr_t)text_section->offset; + addr_t insn_addr_end = insn_addr + text_section->size; + + for (; insn_addr < insn_addr_end; insn_addr += sizeof(uint32_t)) { + if (*(uint32_t *)insn_addr == 0xd4001001) { + supervisor_call_monitor_register_svc((addr_t)insn_addr); + } + } +} + +void supervisor_call_monitor_register_main_app() { + const char *main_bundle_udid = fast_get_main_app_bundle_udid(); + auto module_map = ProcessRuntimeUtility::GetProcessModuleMap(); + for (auto module : module_map) { + if (strstr(module.path, main_bundle_udid)) { + INFO_LOG("[supervisor_call_monitor] %s", module.path); + supervisor_call_monitor_register_image((void *)module.load_address); + } + } +} + +extern "C" int __shared_region_check_np(uint64_t *startaddress); + +struct dyld_cache_header *shared_cache_get_load_addr() { + static struct dyld_cache_header *shared_cache_load_addr = 0; + if (shared_cache_load_addr) + return shared_cache_load_addr; +#if 0 + if (syscall(294, &shared_cache_load_addr) == 0) { +#else + // FIXME: + if (__shared_region_check_np((uint64_t *)&shared_cache_load_addr) != 0) { +#endif + shared_cache_load_addr = 0; +} +return shared_cache_load_addr; +} +void supervisor_call_monitor_register_system_kernel() { + auto libsystem = ProcessRuntimeUtility::GetProcessModule("libsystem_kernel.dylib"); + addr_t libsystem_header = (addr_t)libsystem.load_address; + auto text_section = macho_kit_get_section_by_name((mach_header_t *)libsystem_header, "__TEXT", "__text"); + + addr_t shared_cache_load_addr = (addr_t)shared_cache_get_load_addr(); + addr_t insn_addr = shared_cache_load_addr + (addr_t)text_section->offset; + addr_t insn_addr_end = insn_addr + text_section->size; + + addr_t write_svc_addr = (addr_t)DobbySymbolResolver("libsystem_kernel.dylib", "write"); + write_svc_addr += 4; + + addr_t __psynch_mutexwait_svc_addr = (addr_t)DobbySymbolResolver("libsystem_kernel.dylib", "__psynch_mutexwait"); + __psynch_mutexwait_svc_addr += 4; + + for (; insn_addr < insn_addr_end; insn_addr += sizeof(uint32_t)) { + if (*(uint32_t *)insn_addr == 0xd4001001) { + if (insn_addr == write_svc_addr) + continue; + + if (insn_addr == __psynch_mutexwait_svc_addr) + continue; + supervisor_call_monitor_register_svc((addr_t)insn_addr); + } + } +} + +void supervisor_call_monitor_init() { + // create logger file + char logger_path[1024] = {0}; + sprintf(logger_path, "%s%s", getenv("HOME"), "/Documents/svc_monitor.txt"); + INFO_LOG("HOME: %s", logger_path); + async_logger_init(logger_path); + + dobby_enable_near_branch_trampoline(); +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/supervisor_call_monitor.h b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/supervisor_call_monitor.h new file mode 100644 index 0000000..45bde0a --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/supervisor_call_monitor.h @@ -0,0 +1,24 @@ +#pragma once + +#include +typedef uintptr_t addr_t; + +#include "dobby.h" + +void supervisor_call_monitor_init(); + +void supervisor_call_monitor_register_handler(DBICallTy handler); + +void supervisor_call_monitor_register_svc(addr_t svc_addr); + +void supervisor_call_monitor_register_image(void *header); + +void supervisor_call_monitor_register_main_app(); + +void supervisor_call_monitor_register_system_kernel(); + +void supervisor_call_monitor_register_syscall_call_log_handler(); + +void supervisor_call_monitor_register_mach_syscall_call_log_handler(); + +void supervisor_call_monitor_register_sensitive_api_handler(); \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/system_call_log_handler.cc b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/system_call_log_handler.cc new file mode 100644 index 0000000..d1ca013 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/system_call_log_handler.cc @@ -0,0 +1,98 @@ +#include "dobby/dobby_internal.h" + +#include + +#include "misc-helper/async_logger.h" +#include "PlatformUtil/ProcessRuntimeUtility.h" +#include "SupervisorCallMonitor/misc_utility.h" +#include "SupervisorCallMonitor/supervisor_call_monitor.h" + +#include "XnuInternal/syscalls.c" + +static const char *syscall_num_to_str(int num) { + return syscallnames[num]; +} + +static addr_t getCallFirstArg(DobbyRegisterContext *ctx) { + addr_t result; +#if defined(_M_X64) || defined(__x86_64__) +#if defined(_WIN32) + result = ctx->general.regs.rcx; +#else + result = ctx->general.regs.rdi; +#endif +#elif defined(__arm64__) || defined(__aarch64__) + result = ctx->general.regs.x0; +#elif defined(__arm__) + result = ctx->general.regs.r0; +#else +#error "Not Support Architecture." +#endif + return result; +} + +static addr_t getRealLr(DobbyRegisterContext *ctx) { + addr_t closure_trampoline_reserved_stack = ctx->sp - sizeof(addr_t); + return *(addr_t *)closure_trampoline_reserved_stack; +} + +static addr_t fast_get_caller_from_main_binary(DobbyRegisterContext *ctx) { + static addr_t text_section_start = 0, text_section_end = 0; + static addr_t slide = 0; + if (text_section_start == 0 || text_section_end == 0) { + auto main = ProcessRuntimeUtility::GetProcessModule(""); + addr_t main_header = (addr_t)main.load_address; + + auto text_segment = macho_kit_get_segment_by_name((mach_header_t *)main_header, "__TEXT"); + slide = main_header - text_segment->vmaddr; + + auto text_section = macho_kit_get_section_by_name((mach_header_t *)main_header, "__TEXT", "__text"); + text_section_start = main_header + (addr_t)text_section->offset; + text_section_end = text_section_start + text_section->size; + } + + if (ctx == NULL) + return 0; + + addr_t lr = getRealLr(ctx); + if (lr > text_section_start && lr < text_section_end) + return lr - slide; + +#define MAX_STACK_ITERATE_LEVEL 8 + addr_t fp = ctx->fp; + if (fp == 0) + return 0; + for (int i = 0; i < MAX_STACK_ITERATE_LEVEL; i++) { + addr_t lr = *(addr_t *)(fp + sizeof(addr_t)); + if (lr > text_section_start && lr < text_section_end) + return lr - slide; + fp = *(addr_t *)fp; + if (fp == 0) + return 0; + } + return 0; +} + +static void syscall_log_handler(DobbyRegisterContext *ctx, const InterceptEntry *info) { + addr_t caller = fast_get_caller_from_main_binary(ctx); + if (caller == 0) + return; + + char buffer[2048] = {0}; + int syscall_rum = ctx->general.regs.x16; + if (syscall_rum == 0) { + syscall_rum = (int)getCallFirstArg(ctx); + sprintf(buffer, "[syscall svc-%d] %s\n", syscall_rum, syscall_num_to_str(syscall_rum)); + } else if (syscall_rum > 0) { + sprintf(buffer, "[svc-%d] %s\n", syscall_rum, syscall_num_to_str(syscall_rum)); + if (syscall_rum == 5) { + sprintf(buffer, "[svc-%d] %s:%s\n", syscall_rum, syscall_num_to_str(syscall_rum), (char *)ctx->general.regs.x0); + } + } + async_logger_print(buffer); +} + +void supervisor_call_monitor_register_syscall_call_log_handler() { + fast_get_caller_from_main_binary(NULL); + supervisor_call_monitor_register_handler(syscall_log_handler); +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/test_supervisor_call_monitor.cc b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/test_supervisor_call_monitor.cc new file mode 100644 index 0000000..394a8c8 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/test_supervisor_call_monitor.cc @@ -0,0 +1,16 @@ + +#include "dobby/dobby_internal.h" + +#include "SupervisorCallMonitor/supervisor_call_monitor.h" + +#if 1 +__attribute__((constructor)) static void ctor() { + log_set_level(2); + log_switch_to_syslog(); + + supervisor_call_monitor_init(); + supervisor_call_monitor_register_main_app(); + supervisor_call_monitor_register_syscall_call_log_handler(); + supervisor_call_monitor_register_mach_syscall_call_log_handler(); +} +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/CMakeLists.txt b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/CMakeLists.txt new file mode 100644 index 0000000..6528592 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/CMakeLists.txt @@ -0,0 +1,55 @@ +set(SOURCE_FILE_LIST) + +include_directories( + . +) + +if (NOT DEFINED DOBBY_DIR) + message(FATAL_ERROR "DOBBY_DIR must be set!") +endif () + +if (SYSTEM.Darwin) + add_library(macho_ctx_kit + ${CMAKE_CURRENT_SOURCE_DIR}/macho/macho_ctx.h + ${CMAKE_CURRENT_SOURCE_DIR}/macho/macho_ctx.cc + ) + + add_library(shared_cache_ctx_kit + ${CMAKE_CURRENT_SOURCE_DIR}/macho/shared_cache_ctx.h + ${CMAKE_CURRENT_SOURCE_DIR}/macho/shared_cache_ctx.cpp + ) + + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + macho/macho_ctx.cc + macho/dobby_symbol_resolver.cc + ) + + if (NOT DOBBY_BUILD_KERNEL_MODE) + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + macho/macho_file_symbol_resolver.cpp + macho/shared_cache_ctx.cpp + ${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc + ) + endif () +elseif (SYSTEM.Linux OR SYSTEM.Android) + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + elf/dobby_symbol_resolver.cc + + ${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc + ) +elseif (SYSTEM.Windows) + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + pe/dobby_symbol_resolver.cc + + ${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc + ) +endif () + +get_absolute_path_list(SOURCE_FILE_LIST SOURCE_FILE_LIST_) +set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST_}) + +add_library(dobby_symbol_resolver + ${SOURCE_FILE_LIST} + ) + + diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/dobby_symbol_resolver.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/dobby_symbol_resolver.h new file mode 100644 index 0000000..22dee3c --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/dobby_symbol_resolver.h @@ -0,0 +1,15 @@ +#pragma once + +#if defined(BUILDING_INTERNAL) +#include "macho/dobby_symbol_resolver_priv.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void *DobbySymbolResolver(const char *image_name, const char *symbol_name); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/elf/dobby_symbol_resolver.cc b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/elf/dobby_symbol_resolver.cc new file mode 100644 index 0000000..bf45ea9 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/elf/dobby_symbol_resolver.cc @@ -0,0 +1,238 @@ +#include "SymbolResolver/dobby_symbol_resolver.h" +#include "dobby/common.h" + +#include +#include +#include +#include + +#include "mmap_file_util.h" + +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include + +#undef LOG_TAG +#define LOG_TAG "DobbySymbolResolver" + +typedef struct elf_ctx { + void *header; + + uintptr_t load_bias; + + ElfW(Shdr) * sym_sh_; + ElfW(Shdr) * dynsym_sh_; + + const char *strtab_; + ElfW(Sym) * symtab_; + + const char *dynstrtab_; + ElfW(Sym) * dynsymtab_; + + size_t nbucket_; + size_t nchain_; + uint32_t *bucket_; + uint32_t *chain_; + + size_t gnu_nbucket_; + uint32_t *gnu_bucket_; + uint32_t *gnu_chain_; + uint32_t gnu_maskwords_; + uint32_t gnu_shift2_; + ElfW(Addr) * gnu_bloom_filter_; +} elf_ctx_t; + +static void get_syms(ElfW(Ehdr) * header, ElfW(Sym) * *symtab_ptr, char **strtab_ptr, int *count_ptr) { + ElfW(Shdr) *section_header = NULL; + section_header = (ElfW(Shdr) *)((addr_t)header + header->e_shoff); + + ElfW(Shdr) *section_strtab_section_header = NULL; + section_strtab_section_header = (ElfW(Shdr) *)((addr_t)section_header + header->e_shstrndx * header->e_shentsize); + char *section_strtab = NULL; + section_strtab = (char *)((addr_t)header + section_strtab_section_header->sh_offset); + + for (int i = 0; i < header->e_shnum; ++i) { + const char *section_name = (const char *)(section_strtab + section_header->sh_name); + if (section_header->sh_type == SHT_SYMTAB && strcmp(section_name, ".symtab") == 0) { + *symtab_ptr = (ElfW(Sym) *)((addr_t)header + section_header->sh_offset); + *count_ptr = section_header->sh_size / sizeof(ElfW(Sym)); + } + + if (section_header->sh_type == SHT_STRTAB && strcmp(section_name, ".strtab") == 0) { + *strtab_ptr = (char *)((addr_t)header + section_header->sh_offset); + } + section_header = (ElfW(Shdr) *)((addr_t)section_header + header->e_shentsize); + } +} + +int elf_ctx_init(elf_ctx_t *ctx, void *header_) { + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)header_; + ctx->header = ehdr; + + ElfW(Addr) ehdr_addr = (ElfW(Addr))ehdr; + + // Handle dynamic segment + { + ElfW(Addr) addr = 0; + ElfW(Dyn) *dyn = NULL; + ElfW(Phdr) *phdr = reinterpret_cast(ehdr_addr + ehdr->e_phoff); + for (size_t i = 0; i < ehdr->e_phnum; i++) { + if (phdr[i].p_type == PT_DYNAMIC) { + dyn = reinterpret_cast(ehdr_addr + phdr[i].p_offset); + } else if (phdr[i].p_type == PT_LOAD) { + addr = ehdr_addr + phdr[i].p_offset - phdr[i].p_vaddr; + if (ctx->load_bias == 0) + ctx->load_bias = ehdr_addr - (phdr[i].p_vaddr - phdr[i].p_offset); + } else if (phdr[i].p_type == PT_PHDR) { + ctx->load_bias = (ElfW(Addr))phdr - phdr[i].p_vaddr; + } + } +// ctx->load_bias = +#if 0 + const char *strtab = nullptr; + ElfW(Sym) *symtab = nullptr; + for (ElfW(Dyn) *d = dyn; d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_STRTAB) { + strtab = reinterpret_cast(addr + d->d_un.d_ptr); + } else if (d->d_tag == DT_SYMTAB) { + symtab = reinterpret_cast(addr + d->d_un.d_ptr); + } + } +#endif + } + + // Handle section + { + ElfW(Shdr) * dynsym_sh, *dynstr_sh; + ElfW(Shdr) * sym_sh, *str_sh; + + ElfW(Shdr) *shdr = reinterpret_cast(ehdr_addr + ehdr->e_shoff); + + ElfW(Shdr) *shstr_sh = NULL; + shstr_sh = &shdr[ehdr->e_shstrndx]; + char *shstrtab = NULL; + shstrtab = (char *)((addr_t)ehdr_addr + shstr_sh->sh_offset); + + for (size_t i = 0; i < ehdr->e_shnum; i++) { + if (shdr[i].sh_type == SHT_SYMTAB) { + sym_sh = &shdr[i]; + ctx->sym_sh_ = sym_sh; + ctx->symtab_ = (ElfW(Sym) *)(ehdr_addr + shdr[i].sh_offset); + } else if (shdr[i].sh_type == SHT_STRTAB && strcmp(shstrtab + shdr[i].sh_name, ".strtab") == 0) { + str_sh = &shdr[i]; + ctx->strtab_ = (const char *)(ehdr_addr + shdr[i].sh_offset); + } else if (shdr[i].sh_type == SHT_DYNSYM) { + dynsym_sh = &shdr[i]; + ctx->dynsym_sh_ = dynsym_sh; + ctx->dynsymtab_ = (ElfW(Sym) *)(ehdr_addr + shdr[i].sh_offset); + } else if (shdr[i].sh_type == SHT_STRTAB && strcmp(shstrtab + shdr[i].sh_name, ".dynstr") == 0) { + dynstr_sh = &shdr[i]; + ctx->dynstrtab_ = (const char *)(ehdr_addr + shdr[i].sh_offset); + } + } + } + + return 0; +} + +static void *iterate_symbol_table_impl(const char *symbol_name, ElfW(Sym) * symtab, const char *strtab, int count) { + for (int i = 0; i < count; ++i) { + ElfW(Sym) *sym = symtab + i; + const char *symbol_name_ = strtab + sym->st_name; + if (strcmp(symbol_name_, symbol_name) == 0) { + return (void *)sym->st_value; + } + } + return NULL; +} + +void *elf_ctx_iterate_symbol_table(elf_ctx_t *ctx, const char *symbol_name) { + void *result = NULL; + if (ctx->symtab_ && ctx->strtab_) { + size_t count = ctx->sym_sh_->sh_size / sizeof(ElfW(Sym)); + result = iterate_symbol_table_impl(symbol_name, ctx->symtab_, ctx->strtab_, count); + if (result) + return result; + } + + if (ctx->dynsymtab_ && ctx->dynstrtab_) { + size_t count = ctx->dynsym_sh_->sh_size / sizeof(ElfW(Sym)); + result = iterate_symbol_table_impl(symbol_name, ctx->dynsymtab_, ctx->dynstrtab_, count); + if (result) + return result; + } + return NULL; +} + +void *resolve_elf_internal_symbol(const char *library_name, const char *symbol_name) { + void *result = NULL; + + if (library_name) { + RuntimeModule module = ProcessRuntimeUtility::GetProcessModule(library_name); + + if (module.load_address) { + auto mmapFileMng = MmapFileManager(module.path); + auto file_mem = mmapFileMng.map(); + + elf_ctx_t ctx; + memset(&ctx, 0, sizeof(elf_ctx_t)); + if (file_mem) { + elf_ctx_init(&ctx, file_mem); + result = elf_ctx_iterate_symbol_table(&ctx, symbol_name); + } + + if (result) + result = (void *)((addr_t)result + (addr_t)module.load_address - ((addr_t)file_mem - (addr_t)ctx.load_bias)); + } + } + + if (!result) { + auto ProcessModuleMap = ProcessRuntimeUtility::GetProcessModuleMap(); + for (auto module : ProcessModuleMap) { + + if (module.load_address) { + auto mmapFileMng = MmapFileManager(module.path); + auto file_mem = mmapFileMng.map(); + + elf_ctx_t ctx; + memset(&ctx, 0, sizeof(elf_ctx_t)); + if (file_mem) { + elf_ctx_init(&ctx, file_mem); + result = elf_ctx_iterate_symbol_table(&ctx, symbol_name); + } + + if (result) + result = (void *)((addr_t)result + (addr_t)module.load_address - ((addr_t)file_mem - (addr_t)ctx.load_bias)); + } + + if (result) + break; + } + } + return result; +} + +// impl at "android_restriction.cc" +extern std::vector linker_get_solist(); + +PUBLIC void *DobbySymbolResolver(const char *image_name, const char *symbol_name_pattern) { + void *result = NULL; + +#if 0 + auto solist = linker_get_solist(); + for (auto soinfo : solist) { + uintptr_t handle = linker_soinfo_to_handle(soinfo); + if (image_name == NULL || strstr(linker_soinfo_get_realpath(soinfo), image_name) != 0) { + result = dlsym((void *)handle, symbol_name_pattern); + if (result) + return result; + } + } +#endif + result = dlsym(RTLD_DEFAULT, symbol_name_pattern); + if (result) + return result; + + result = resolve_elf_internal_symbol(image_name, symbol_name_pattern); + return result; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver.cc b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver.cc new file mode 100644 index 0000000..bd5c993 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver.cc @@ -0,0 +1,114 @@ +#include "dobby_symbol_resolver.h" +#include "macho/dobby_symbol_resolver_priv.h" +#include "macho_file_symbol_resolver.h" + +#include "dobby/common.h" + +#include +#include + +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include "macho_ctx.h" +#include "shared_cache_ctx.h" + +#if !defined(BUILDING_KERNEL) +#include +#include +#endif + +#undef LOG_TAG +#define LOG_TAG "DobbySymbolResolver" + +PUBLIC void *DobbySymbolResolver(const char *image_name, const char *symbol_name_pattern) { + uintptr_t result = 0; + auto modules = ProcessRuntimeUtility::GetProcessModuleMap(); + + for (auto iter = modules.begin(); iter != modules.end(); iter++) { + auto module = *iter; + + // image filter + if (image_name && !strstr(module.path, image_name)) + continue; + + // dyld in shared cached at new os version + // ignore dyld, as some functions as own implementation in dyld + if (!image_name && strstr(module.path, "dyld")) + continue; + + auto header = (mach_header_t *)module.load_address; + if (header == nullptr) + continue; + +#if 0 + DEBUG_LOG("resolve image: %s", module.path); +#endif + + nlist_t *symtab = NULL; + uint32_t symtab_count = 0; + char *strtab = NULL; + +#if !defined(BUILDING_KERNEL) +#if defined(__arm__) || defined(__aarch64__) + static int shared_cache_ctx_init_once = 0; + static shared_cache_ctx_t shared_cache_ctx; + if (shared_cache_ctx_init_once == 0) { + shared_cache_ctx_init_once = 1; + shared_cache_ctx_init(&shared_cache_ctx); + shared_cache_load_symbols(&shared_cache_ctx); + } + if (shared_cache_ctx.mmap_shared_cache) { + // shared cache library + if (shared_cache_is_contain(&shared_cache_ctx, (addr_t)header, 0)) { + shared_cache_get_symbol_table(&shared_cache_ctx, header, &symtab, &symtab_count, &strtab); + } + } + if (symtab && strtab) { + result = macho_iterate_symbol_table((char *)symbol_name_pattern, symtab, symtab_count, strtab); + } + if (result) { + result = result + shared_cache_ctx.runtime_slide; + return (void *)result; + } +#endif +#endif + + macho_ctx_t macho_ctx(header); + result = macho_ctx.symbol_resolve(symbol_name_pattern); + if (result) { + return (void *)result; + } + } + +#if !defined(BUILDING_KERNEL) + mach_header_t *dyld_header = NULL; + if (image_name != NULL && strcmp(image_name, "dyld") == 0) { + // task info + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if (task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count)) { + return NULL; + } + + // get dyld load address + const struct dyld_all_image_infos *infos = + (struct dyld_all_image_infos *)(uintptr_t)task_dyld_info.all_image_info_addr; + dyld_header = (mach_header_t *)infos->dyldImageLoadAddress; + macho_ctx_t dyld_ctx(dyld_header); + result = dyld_ctx.symbol_resolve(symbol_name_pattern); + + bool is_dyld_in_cache = ((mach_header_t *)dyld_header)->flags & MH_DYLIB_IN_CACHE; + if (!is_dyld_in_cache && result == 0) { + result = macho_file_symbol_resolve(dyld_header->cputype, dyld_header->cpusubtype, "/usr/lib/dyld", + (char *)symbol_name_pattern); + result += (uintptr_t)dyld_header; + } + } +#endif + + if (result == 0) { + DEBUG_LOG("symbol resolver failed: %s", symbol_name_pattern); + } + + return (void *)result; +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver_priv.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver_priv.h new file mode 100644 index 0000000..c6b4244 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver_priv.h @@ -0,0 +1,6 @@ +#include +#include +#include + +#include "macho_ctx.h" + diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.cc b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.cc new file mode 100644 index 0000000..92188ea --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.cc @@ -0,0 +1,358 @@ +#include "macho_ctx.h" + +#include +#include +#include +#include +#include + +#define ASSERT(x) + +uintptr_t macho_iterate_symbol_table(char *symbol_name_pattern, nlist_t *symtab, uint32_t symtab_count, char *strtab) { + for (uint32_t i = 0; i < symtab_count; i++) { + if (symtab[i].n_value) { + uint32_t strtab_offset = symtab[i].n_un.n_strx; + char *symbol_name = strtab + strtab_offset; +#if 0 + printf("> %s", symbol_name); +#endif + if (strcmp(symbol_name_pattern, symbol_name) == 0) { + return symtab[i].n_value; + } + if (symbol_name[0] == '_') { + if (strcmp(symbol_name_pattern, &symbol_name[1]) == 0) { + return symtab[i].n_value; + } + } + } + } + return 0; +} + +// --- + +void macho_ctx_t::init(mach_header_t *header, bool is_runtime_mode) { + memset(this, 0, sizeof(macho_ctx_t)); + + this->is_runtime_mode = is_runtime_mode; + + this->header = header; + segment_command_t *curr_seg_cmd; + segment_command_t *text_segment = 0, *text_exec_segment = 0, *data_segment = 0, *data_const_segment = 0, + *linkedit_segment = 0; + struct symtab_command *symtab_cmd = 0; + struct dysymtab_command *dysymtab_cmd = 0; + struct dyld_info_command *dyld_info_cmd = 0; + struct linkedit_data_command *exports_trie_cmd = 0; + struct linkedit_data_command *chained_fixups_cmd = NULL; + + curr_seg_cmd = (segment_command_t *)((uintptr_t)header + sizeof(mach_header_t)); + for (int i = 0; i < header->ncmds; i++) { + if (curr_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + // BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB and REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + this->segments[this->segments_count++] = curr_seg_cmd; + + if (strcmp(curr_seg_cmd->segname, "__LINKEDIT") == 0) { + linkedit_segment = curr_seg_cmd; + } else if (strcmp(curr_seg_cmd->segname, "__DATA") == 0) { + data_segment = curr_seg_cmd; + } else if (strcmp(curr_seg_cmd->segname, "__DATA_CONST") == 0) { + data_const_segment = curr_seg_cmd; + } else if (strcmp(curr_seg_cmd->segname, "__TEXT") == 0) { + text_segment = curr_seg_cmd; + } else if (strcmp(curr_seg_cmd->segname, "__TEXT_EXEC") == 0) { + text_exec_segment = curr_seg_cmd; + } + } else if (curr_seg_cmd->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command *)curr_seg_cmd; + } else if (curr_seg_cmd->cmd == LC_DYSYMTAB) { + dysymtab_cmd = (struct dysymtab_command *)curr_seg_cmd; + } else if (curr_seg_cmd->cmd == LC_DYLD_INFO || curr_seg_cmd->cmd == LC_DYLD_INFO_ONLY) { + dyld_info_cmd = (struct dyld_info_command *)curr_seg_cmd; + } else if (curr_seg_cmd->cmd == LC_DYLD_EXPORTS_TRIE) { + exports_trie_cmd = (struct linkedit_data_command *)curr_seg_cmd; + } else if (curr_seg_cmd->cmd == LC_DYLD_CHAINED_FIXUPS) { + chained_fixups_cmd = (struct linkedit_data_command *)curr_seg_cmd; + } + curr_seg_cmd = (segment_command_t *)((uintptr_t)curr_seg_cmd + curr_seg_cmd->cmdsize); + } + + uintptr_t slide = (uintptr_t)header - (uintptr_t)text_segment->vmaddr; + uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; + if (!is_runtime_mode) { + // as mmap, all segment is close + uintptr_t linkedit_segment_vmaddr = linkedit_segment->fileoff; + linkedit_base = (uintptr_t)slide + linkedit_segment_vmaddr - linkedit_segment->fileoff; + } + + vm_region_start = segments[0]->vmaddr; + // skip __LINKEDIT + if (strcmp(segments[0]->segname, "__LINKEDIT") == 0) { + vm_region_start = segments[1]->vmaddr; + } + vm_region_end = segments[segments_count - 1]->vmaddr + segments[segments_count - 1]->vmsize; + vmaddr = vm_region_start; + vmsize = vm_region_end - vm_region_start; + + this->text_seg = text_segment; + this->text_exec_seg = text_exec_segment; + this->data_seg = data_segment; + this->data_const_seg = data_const_segment; + this->linkedit_seg = linkedit_segment; + + this->symtab_cmd = symtab_cmd; + this->dysymtab_cmd = dysymtab_cmd; + this->dyld_info_cmd = dyld_info_cmd; + this->exports_trie_cmd = exports_trie_cmd; + this->chained_fixups_cmd = chained_fixups_cmd; + + this->slide = slide; + this->linkedit_base = linkedit_base; + + this->symtab = (nlist_t *)(this->linkedit_base + this->symtab_cmd->symoff); + this->strtab = (char *)(this->linkedit_base + this->symtab_cmd->stroff); + this->indirect_symtab = (uint32_t *)(this->linkedit_base + this->dysymtab_cmd->indirectsymoff); +} + +uintptr_t macho_ctx_t::iterate_symbol_table(const char *symbol_name_pattern) { + nlist_t *symtab = this->symtab; + uint32_t symtab_count = this->symtab_cmd->nsyms; + char *strtab = this->strtab; + + for (uint32_t i = 0; i < symtab_count; i++) { + if (symtab[i].n_value) { + uint32_t strtab_offset = symtab[i].n_un.n_strx; + char *symbol_name = strtab + strtab_offset; +#if 0 + printf("> %s", symbol_name); +#endif + if (strcmp(symbol_name_pattern, symbol_name) == 0) { + return symtab[i].n_value; + } + if (symbol_name[0] == '_') { + if (strcmp(symbol_name_pattern, &symbol_name[1]) == 0) { + return symtab[i].n_value; + } + } + } + } + return 0; +} + +uintptr_t read_uleb128(const uint8_t **pp, const uint8_t *end) { + uint8_t *p = (uint8_t *)*pp; + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + ASSERT(p == end); + + uint64_t slice = *p & 0x7f; + + if (bit > 63) + ASSERT(bit > 63); + else { + result |= (slice << bit); + bit += 7; + } + } while (*p++ & 0x80); + + *pp = p; + + return (uintptr_t)result; +} + +intptr_t read_sleb128(const uint8_t **pp, const uint8_t *end) { + uint8_t *p = (uint8_t *)*pp; + + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + ASSERT(p == end); + byte = *p++; + result |= (((int64_t)(byte & 0x7f)) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ((byte & 0x40) != 0) + result |= (~0ULL) << bit; + + *pp = p; + + return (intptr_t)result; +} + +// dyld +// bool MachOLoaded::findExportedSymbol +// MachOLoaded::trieWalk +uint8_t *tail_walk(const uint8_t *start, const uint8_t *end, const char *symbol) { + uint32_t visitedNodeOffsets[128]; + int visitedNodeOffsetCount = 0; + visitedNodeOffsets[visitedNodeOffsetCount++] = 0; + const uint8_t *p = start; + while (p < end) { + uint64_t terminalSize = *p++; + if (terminalSize > 127) { + // except for re-export-with-rename, all terminal sizes fit in one byte + --p; + terminalSize = read_uleb128(&p, end); + } + if ((*symbol == '\0') && (terminalSize != 0)) { + return (uint8_t *)p; + } + const uint8_t *children = p + terminalSize; + if (children > end) { + // diag.error("malformed trie node, terminalSize=0x%llX extends past end of trie\n", terminalSize); + return NULL; + } + uint8_t childrenRemaining = *children++; + p = children; + uint64_t nodeOffset = 0; + + for (; childrenRemaining > 0; --childrenRemaining) { + const char *ss = symbol; + bool wrongEdge = false; + // scan whole edge to get to next edge + // if edge is longer than target symbol name, don't read past end of symbol name + char c = *p; + while (c != '\0') { + if (!wrongEdge) { + if (c != *ss) + wrongEdge = true; + ++ss; + } + ++p; + c = *p; + } + if (wrongEdge) { + // advance to next child + ++p; // skip over zero terminator + // skip over uleb128 until last byte is found + while ((*p & 0x80) != 0) + ++p; + ++p; // skip over last byte of uleb128 + if (p > end) { + // diag.error("malformed trie node, child node extends past end of trie\n"); + return nullptr; + } + } else { + // the symbol so far matches this edge (child) + // so advance to the child's node + ++p; + nodeOffset = read_uleb128(&p, end); + if ((nodeOffset == 0) || (&start[nodeOffset] > end)) { + // diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset); + return nullptr; + } + symbol = ss; + break; + } + } + + if (nodeOffset != 0) { + if (nodeOffset > (uint64_t)(end - start)) { + // diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset); + return NULL; + } + for (int i = 0; i < visitedNodeOffsetCount; ++i) { + if (visitedNodeOffsets[i] == nodeOffset) { + // diag.error("malformed trie child, cycle to nodeOffset=0x%llX\n", nodeOffset); + return NULL; + } + } + visitedNodeOffsets[visitedNodeOffsetCount++] = (uint32_t)nodeOffset; + p = &start[nodeOffset]; + } else + p = end; + } + return NULL; +} + +uintptr_t macho_ctx_t::iterate_exported_symbol(const char *symbol_name, uint64_t *out_flags) { + if (this->text_seg == NULL || this->linkedit_seg == NULL) { + return 0; + } + + struct dyld_info_command *dyld_info_cmd = this->dyld_info_cmd; + struct linkedit_data_command *exports_trie_cmd = this->exports_trie_cmd; + if (exports_trie_cmd == NULL && dyld_info_cmd == NULL) + return 0; + + uint32_t trieFileOffset = dyld_info_cmd ? dyld_info_cmd->export_off : exports_trie_cmd->dataoff; + uint32_t trieFileSize = dyld_info_cmd ? dyld_info_cmd->export_size : exports_trie_cmd->datasize; + + void *exports = (void *)(this->linkedit_base + trieFileOffset); + if (exports == NULL) + return 0; + + uint8_t *exports_start = (uint8_t *)exports; + uint8_t *exports_end = exports_start + trieFileSize; + uint8_t *node = (uint8_t *)tail_walk(exports_start, exports_end, symbol_name); + if (node == NULL) + return 0; + const uint8_t *p = node; + const uintptr_t flags = read_uleb128(&p, exports_end); + if (out_flags) + *out_flags = flags; + if (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { + const uint64_t ordinal = read_uleb128(&p, exports_end); + const char *importedName = (const char *)p; + if (importedName[0] == '\0') { + importedName = symbol_name; + return 0; + } + // trick + // printf("reexported symbol: %s\n", importedName); + return (uintptr_t)importedName; + } + uint64_t trieValue = read_uleb128(&p, exports_end); + return trieValue; +#if 0 + if (off == (void *)0) { + if (symbol_name[0] != '_' && strlen(&symbol_name[1]) >= 1) { + char _symbol_name[1024] = {0}; + _symbol_name[0] = '_'; + strcpy(&_symbol_name[1], symbol_name); + off = (void *)walk_exported_trie((const uint8_t *)exports, (const uint8_t *)exports + trieFileSize, _symbol_name); + } + } +#endif +} + +uintptr_t macho_ctx_t::symbol_resolve_options(const char *symbol_name_pattern, resolve_symbol_type_t type) { + if (type & RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE) { + uintptr_t result = iterate_symbol_table(symbol_name_pattern); + if (result) { + result = result + (this->is_runtime_mode ? this->slide : 0); + return result; + } + } + + if (type & RESOLVE_SYMBOL_TYPE_EXPORTED) { + // binary exported table(uleb128) + uint64_t flags; + uintptr_t result = iterate_exported_symbol(symbol_name_pattern, &flags); + if (result) { + switch (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: { + result += (uintptr_t)this->header; + } break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: { + result += (uintptr_t)this->header; + } break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: { + } break; + default: + break; + } + return result; + } + } + return 0; +} + +uintptr_t macho_ctx_t::symbol_resolve(const char *symbol_name_pattern) { + return symbol_resolve_options(symbol_name_pattern, RESOLVE_SYMBOL_TYPE_ALL); +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.h new file mode 100644 index 0000000..82f41cd --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +#if defined(__LP64__) +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct section_64 section_t; +typedef struct nlist_64 nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct section section_t; +typedef struct nlist nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT +#endif + +intptr_t read_sleb128(const uint8_t **pp, const uint8_t *end); + +uintptr_t read_uleb128(const uint8_t **pp, const uint8_t *end); + +typedef enum { + RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE = 1 << 0, + RESOLVE_SYMBOL_TYPE_EXPORTED = 1 << 1, + RESOLVE_SYMBOL_TYPE_ALL = RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE | RESOLVE_SYMBOL_TYPE_EXPORTED +} resolve_symbol_type_t; + +struct macho_ctx_t { + bool is_runtime_mode; + + mach_header_t *header; + + uintptr_t vmaddr; + size_t vmsize; + uintptr_t vm_region_start; + uintptr_t vm_region_end; + + uintptr_t slide; + uintptr_t linkedit_base; + + segment_command_t *segments[64]; + int segments_count; + + segment_command_t *text_seg; + segment_command_t *data_seg; + segment_command_t *text_exec_seg; + segment_command_t *data_const_seg; + segment_command_t *linkedit_seg; + + struct symtab_command *symtab_cmd; + struct dysymtab_command *dysymtab_cmd; + struct dyld_info_command *dyld_info_cmd; + struct linkedit_data_command *exports_trie_cmd; + struct linkedit_data_command *chained_fixups_cmd; + + nlist_t *symtab; + char *strtab; + uint32_t *indirect_symtab; + + explicit macho_ctx_t(mach_header_t *header, bool is_runtime_mode = true) { + init(header, is_runtime_mode); + } + + void init(mach_header_t *header, bool is_runtime_mode); + + uintptr_t iterate_symbol_table(const char *symbol_name_pattern); + + uintptr_t iterate_exported_symbol(const char *symbol_name, uint64_t *out_flags); + + uintptr_t symbol_resolve_options(const char *symbol_name_pattern, resolve_symbol_type_t type); + + uintptr_t symbol_resolve(const char *symbol_name_pattern); +}; + +#ifdef __cplusplus +extern "C" { +#endif + +uintptr_t macho_iterate_symbol_table(char *name_pattern, nlist_t *symtab, uint32_t symtab_count, char *strtab); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_file_symbol_resolver.cpp b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_file_symbol_resolver.cpp new file mode 100644 index 0000000..541afd2 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_file_symbol_resolver.cpp @@ -0,0 +1,68 @@ +#include "macho_file_symbol_resolver.h" + +#include "SymbolResolver/mmap_file_util.h" + +#include +#include +#include + +uintptr_t macho_file_memory_symbol_resolve(cpu_type_t in_cputype, cpu_subtype_t in_cpusubtype, const uint8_t *file_mem, + char *symbol_name_pattern) { + + mach_header_t *header = (mach_header_t *)file_mem; + struct fat_header *fh = (struct fat_header *)file_mem; + if (fh->magic == OSSwapBigToHostInt32(FAT_MAGIC)) { + const struct fat_arch *archs = (struct fat_arch *)(((uintptr_t)fh) + sizeof(fat_header)); + mach_header_t *header_arm64 = NULL; + mach_header_t *header_arm64e = NULL; + mach_header_t *header_x64 = NULL; + for (size_t i = 0; i < OSSwapBigToHostInt32(fh->nfat_arch); i++) { + uint64_t offset; + uint64_t len; + cpu_type_t cputype = (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype); + cpu_subtype_t cpusubtype = (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype); + offset = OSSwapBigToHostInt32(archs[i].offset); + len = OSSwapBigToHostInt32(archs[i].size); + if (cputype == CPU_TYPE_X86_64) { + header_x64 = (mach_header_t *)&file_mem[offset]; + } else if (cputype == CPU_TYPE_ARM64 && (cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) { + header_arm64e = (mach_header_t *)&file_mem[offset]; + } else if (cputype == CPU_TYPE_ARM64) { + header_arm64 = (mach_header_t *)&file_mem[offset]; + } + + if ((cputype == in_cputype) && ((cpusubtype & in_cpusubtype) == in_cpusubtype)) { + header = (mach_header_t *)&file_mem[offset]; + break; + } + } + + if (header == (mach_header_t *)file_mem) { + if (in_cputype == 0 && in_cpusubtype == 0) { +#if defined(__arm64__) || defined(__aarch64__) + header = header_arm64e ? header_arm64e : header_arm64; +#else + header = header_x64; +#endif + } + } + } + + macho_ctx_t ctx(header, false); + return ctx.symbol_resolve_options(symbol_name_pattern, RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE); +} + +uintptr_t macho_file_symbol_resolve(cpu_type_t cpu, cpu_subtype_t subtype, const char *file, + char *symbol_name_pattern) { + +#if defined(COMPILE_WITH_NO_STDLIB) + return 0; +#endif + MmapFileManager mng(file); + auto mmap_buffer = mng.map(); + if (!mmap_buffer) { + return 0; + } + + return macho_file_memory_symbol_resolve(cpu, subtype, mmap_buffer, symbol_name_pattern); +} diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_file_symbol_resolver.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_file_symbol_resolver.h new file mode 100644 index 0000000..81700c9 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_file_symbol_resolver.h @@ -0,0 +1,8 @@ +#pragma once + +#include "macho_ctx.h" + +uintptr_t macho_file_memory_symbol_resolve(cpu_type_t cpu, cpu_subtype_t subtype, const uint8_t *file_mem, + char *symbol_name_pattern); + +uintptr_t macho_file_symbol_resolve(cpu_type_t cpu, cpu_subtype_t subtype, const char *file, char *symbol_name_pattern); diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared-cache/dyld_cache_format.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared-cache/dyld_cache_format.h new file mode 100644 index 0000000..9209293 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared-cache/dyld_cache_format.h @@ -0,0 +1,560 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __DYLD_CACHE_FORMAT__ +#define __DYLD_CACHE_FORMAT__ + +#include +#include +#include + + +struct dyld_cache_header +{ + char magic[16]; // e.g. "dyld_v0 i386" + uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info + uint32_t mappingCount; // number of dyld_cache_mapping_info entries + uint32_t imagesOffsetOld; // UNUSED: moved to imagesOffset to prevent older dsc_extarctors from crashing + uint32_t imagesCountOld; // UNUSED: moved to imagesCount to prevent older dsc_extarctors from crashing + uint64_t dyldBaseAddress; // base address of dyld when cache was built + uint64_t codeSignatureOffset; // file offset of code signature blob + uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) + uint64_t slideInfoOffsetUnused; // unused. Used to be file offset of kernel slid info + uint64_t slideInfoSizeUnused; // unused. Used to be size of kernel slid info + uint64_t localSymbolsOffset; // file offset of where local symbols are stored + uint64_t localSymbolsSize; // size of local symbols information + uint8_t uuid[16]; // unique value for each shared cache file + uint64_t cacheType; // 0 for development, 1 for production, 2 for multi-cache + uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses + uint32_t branchPoolsCount; // number of uint64_t entries + uint64_t dyldInCacheMH; // (unslid) address of mach_header of dyld in cache + uint64_t dyldInCacheEntry; // (unslid) address of entry point (_dyld_start) of dyld in cache + uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info + uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries + uint64_t patchInfoAddr; // (unslid) address of dyld_cache_patch_info + uint64_t patchInfoSize; // Size of all of the patch information pointed to via the dyld_cache_patch_info + uint64_t otherImageGroupAddrUnused; // unused + uint64_t otherImageGroupSizeUnused; // unused + uint64_t progClosuresAddr; // (unslid) address of list of program launch closures + uint64_t progClosuresSize; // size of list of program launch closures + uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures + uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures + uint32_t platform; // platform number (macOS=1, etc) + uint32_t formatVersion : 8, // dyld3::closure::kFormatVersion + dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid + simulator : 1, // for simulator of specified platform + locallyBuiltCache : 1, // 0 for B&I built cache, 1 for locally built cache + builtFromChainedFixups : 1, // some dylib in cache was built using chained fixups, so patch tables must be used for overrides + padding : 20; // TBD + uint64_t sharedRegionStart; // base load address of cache if not slid + uint64_t sharedRegionSize; // overall size required to map the cache and all subCaches, if any + uint64_t maxSlide; // runtime slide of cache can be between zero and this value + uint64_t dylibsImageArrayAddr; // (unslid) address of ImageArray for dylibs in this cache + uint64_t dylibsImageArraySize; // size of ImageArray for dylibs in this cache + uint64_t dylibsTrieAddr; // (unslid) address of trie of indexes of all cached dylibs + uint64_t dylibsTrieSize; // size of trie of cached dylib paths + uint64_t otherImageArrayAddr; // (unslid) address of ImageArray for dylibs and bundles with dlopen closures + uint64_t otherImageArraySize; // size of ImageArray for dylibs and bundles with dlopen closures + uint64_t otherTrieAddr; // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures + uint64_t otherTrieSize; // size of trie of dylibs and bundles with dlopen closures + uint32_t mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info + uint32_t mappingWithSlideCount; // number of dyld_cache_mapping_and_slide_info entries + uint64_t dylibsPBLStateArrayAddrUnused; // unused + uint64_t dylibsPBLSetAddr; // (unslid) address of PrebuiltLoaderSet of all cached dylibs + uint64_t programsPBLSetPoolAddr; // (unslid) address of pool of PrebuiltLoaderSet for each program + uint64_t programsPBLSetPoolSize; // size of pool of PrebuiltLoaderSet for each program + uint64_t programTrieAddr; // (unslid) address of trie mapping program path to PrebuiltLoaderSet + uint32_t programTrieSize; + uint32_t osVersion; // OS Version of dylibs in this cache for the main platform + uint32_t altPlatform; // e.g. iOSMac on macOS + uint32_t altOsVersion; // e.g. 14.0 for iOSMac + uint64_t swiftOptsOffset; // VM offset from cache_header* to Swift optimizations header + uint64_t swiftOptsSize; // size of Swift optimizations header + uint32_t subCacheArrayOffset; // file offset to first dyld_subcache_entry + uint32_t subCacheArrayCount; // number of subCache entries + uint8_t symbolFileUUID[16]; // unique value for the shared cache file containing unmapped local symbols + uint64_t rosettaReadOnlyAddr; // (unslid) address of the start of where Rosetta can add read-only/executable data + uint64_t rosettaReadOnlySize; // maximum size of the Rosetta read-only/executable region + uint64_t rosettaReadWriteAddr; // (unslid) address of the start of where Rosetta can add read-write data + uint64_t rosettaReadWriteSize; // maximum size of the Rosetta read-write region + uint32_t imagesOffset; // file offset to first dyld_cache_image_info + uint32_t imagesCount; // number of dyld_cache_image_info entries + uint32_t cacheSubType; // 0 for development, 1 for production, when cacheType is multi-cache(2) + uint64_t objcOptsOffset; // VM offset from cache_header* to ObjC optimizations header + uint64_t objcOptsSize; // size of ObjC optimizations header + uint64_t cacheAtlasOffset; // VM offset from cache_header* to embedded cache atlas for process introspection + uint64_t cacheAtlasSize; // size of embedded cache atlas + uint64_t dynamicDataOffset; // VM offset from cache_header* to the location of dyld_cache_dynamic_data_header + uint64_t dynamicDataMaxSize; // maximum size of space reserved from dynamic data +}; + +// Uncomment this and check the build errors for the current mapping offset to check against when adding new fields. +// template class A { int x[-size]; }; A a; + + +struct dyld_cache_mapping_info { + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint32_t maxProt; + uint32_t initProt; +}; + +// Contains the flags for the dyld_cache_mapping_and_slide_info flgs field +enum { + DYLD_CACHE_MAPPING_AUTH_DATA = 1 << 0U, + DYLD_CACHE_MAPPING_DIRTY_DATA = 1 << 1U, + DYLD_CACHE_MAPPING_CONST_DATA = 1 << 2U, + DYLD_CACHE_MAPPING_TEXT_STUBS = 1 << 3U, + DYLD_CACHE_DYNAMIC_CONFIG_DATA = 1 << 4U, +}; + +struct dyld_cache_mapping_and_slide_info { + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint64_t slideInfoFileOffset; + uint64_t slideInfoFileSize; + uint64_t flags; + uint32_t maxProt; + uint32_t initProt; +}; + +struct dyld_cache_image_info +{ + uint64_t address; + uint64_t modTime; + uint64_t inode; + uint32_t pathFileOffset; + uint32_t pad; +}; + +struct dyld_cache_image_info_extra +{ + uint64_t exportsTrieAddr; // address of trie in unslid cache + uint64_t weakBindingsAddr; + uint32_t exportsTrieSize; + uint32_t weakBindingsSize; + uint32_t dependentsStartArrayIndex; + uint32_t reExportsStartArrayIndex; +}; + + +struct dyld_cache_accelerator_info +{ + uint32_t version; // currently 1 + uint32_t imageExtrasCount; // does not include aliases + uint32_t imagesExtrasOffset; // offset into this chunk of first dyld_cache_image_info_extra + uint32_t bottomUpListOffset; // offset into this chunk to start of 16-bit array of sorted image indexes + uint32_t dylibTrieOffset; // offset into this chunk to start of trie containing all dylib paths + uint32_t dylibTrieSize; // size of trie containing all dylib paths + uint32_t initializersOffset; // offset into this chunk to start of initializers list + uint32_t initializersCount; // size of initializers list + uint32_t dofSectionsOffset; // offset into this chunk to start of DOF sections list + uint32_t dofSectionsCount; // size of initializers list + uint32_t reExportListOffset; // offset into this chunk to start of 16-bit array of re-exports + uint32_t reExportCount; // size of re-exports + uint32_t depListOffset; // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward) + uint32_t depListCount; // size of dependencies + uint32_t rangeTableOffset; // offset into this chunk to start of ss + uint32_t rangeTableCount; // size of dependencies + uint64_t dyldSectionAddr; // address of libdyld's __dyld section in unslid cache +}; + +struct dyld_cache_accelerator_initializer +{ + uint32_t functionOffset; // address offset from start of cache mapping + uint32_t imageIndex; +}; + +struct dyld_cache_range_entry +{ + uint64_t startAddress; // unslid address of start of region + uint32_t size; + uint32_t imageIndex; +}; + +struct dyld_cache_accelerator_dof +{ + uint64_t sectionAddress; // unslid address of start of region + uint32_t sectionSize; + uint32_t imageIndex; +}; + +struct dyld_cache_image_text_info +{ + uuid_t uuid; + uint64_t loadAddress; // unslid address of start of __TEXT + uint32_t textSegmentSize; + uint32_t pathOffset; // offset from start of cache file +}; + + +// The rebasing info is to allow the kernel to lazily rebase DATA pages of the +// dyld shared cache. Rebasing is adding the slide to interior pointers. +struct dyld_cache_slide_info +{ + uint32_t version; // currently 1 + uint32_t toc_offset; + uint32_t toc_count; + uint32_t entries_offset; + uint32_t entries_count; + uint32_t entries_size; // currently 128 + // uint16_t toc[toc_count]; + // entrybitmap entries[entries_count]; +}; + +struct dyld_cache_slide_info_entry { + uint8_t bits[4096/(8*4)]; // 128-byte bitmap +}; + + +// The version 2 of the slide info uses a different compression scheme. Since +// only interior pointers (pointers that point within the cache) are rebased +// (slid), we know the possible range of the pointers and thus know there are +// unused bits in each pointer. We use those bits to form a linked list of +// locations needing rebasing in each page. +// +// Definitions: +// +// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size +// pageStarts[] = info + info->page_starts_offset +// pageExtras[] = info + info->page_extras_offset +// valueMask = ~(info->delta_mask) +// deltaShift = __builtin_ctzll(info->delta_mask) - 2 +// +// There are three cases: +// +// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE +// The page contains no values that need rebasing. +// +// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 +// All rebase locations are in one linked list. The offset of the first +// rebase location in the page is pageStarts[pageIndex] * 4. +// +// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA +// Multiple linked lists are needed for all rebase locations in a page. +// The pagesExtras array contains 2 or more entries each of which is the +// start of a new linked list in the page. The first is at: +// extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF) +// The next is at extrasStartIndex+1. The last is denoted by +// having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[] +// set. +// +// For 64-bit architectures, there is always enough free bits to encode all +// possible deltas. The info->delta_mask field shows where the delta is located +// in the pointer. That value must be masked off (valueMask) before the slide +// is added to the pointer. +// +// For 32-bit architectures, there are only three bits free (the three most +// significant bits). To extract the delta, you must first subtract value_add +// from the pointer value, then AND with delta_mask, then shift by deltaShift. +// That still leaves a maximum delta to the next rebase location of 28 bytes. +// To reduce the number or chains needed, an optimization was added. Turns +// out zero is common in the DATA region. A zero can be turned into a +// non-rebasing entry in the linked list. The can be done because nothing +// in the shared cache should point out of its dylib to the start of the shared +// cache. +// +// The code for processing a linked list (chain) is: +// +// uint32_t delta = 1; +// while ( delta != 0 ) { +// uint8_t* loc = pageStart + pageOffset; +// uintptr_t rawValue = *((uintptr_t*)loc); +// delta = ((rawValue & deltaMask) >> deltaShift); +// uintptr_t newValue = (rawValue & valueMask); +// if ( newValue != 0 ) { +// newValue += valueAdd; +// newValue += slideAmount; +// } +// *((uintptr_t*)loc) = newValue; +// pageOffset += delta; +// } +// +// +struct dyld_cache_slide_info2 +{ + uint32_t version; // currently 2 + uint32_t page_size; // currently 4096 (may also be 16384) + uint32_t page_starts_offset; + uint32_t page_starts_count; + uint32_t page_extras_offset; + uint32_t page_extras_count; + uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location + uint64_t value_add; + //uint16_t page_starts[page_starts_count]; + //uint16_t page_extras[page_extras_count]; +}; +#define DYLD_CACHE_SLIDE_PAGE_ATTRS 0xC000 // high bits of uint16_t are flags +#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array) +#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing +#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page + + + +// The version 3 of the slide info uses a different compression scheme. Since +// only interior pointers (pointers that point within the cache) are rebased +// (slid), we know the possible range of the pointers and thus know there are +// unused bits in each pointer. We use those bits to form a linked list of +// locations needing rebasing in each page. +// +// Definitions: +// +// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size +// pageStarts[] = info + info->page_starts_offset +// +// There are two cases: +// +// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE +// The page contains no values that need rebasing. +// +// 2) otherwise... +// All rebase locations are in one linked list. The offset of the first +// rebase location in the page is pageStarts[pageIndex]. +// +// A pointer is one of of the variants in dyld_cache_slide_pointer3 +// +// The code for processing a linked list (chain) is: +// +// uint32_t delta = pageStarts[pageIndex]; +// dyld_cache_slide_pointer3* loc = pageStart; +// do { +// loc += delta; +// delta = loc->offsetToNextPointer; +// if ( loc->auth.authenticated ) { +// newValue = loc->offsetFromSharedCacheBase + results->slide + auth_value_add; +// newValue = sign_using_the_various_bits(newValue); +// } +// else { +// uint64_t value51 = loc->pointerValue; +// uint64_t top8Bits = value51 & 0x0007F80000000000ULL; +// uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFFULL; +// uint64_t targetValue = ( top8Bits << 13 ) | bottom43Bits; +// newValue = targetValue + results->slide; +// } +// loc->raw = newValue; +// } while (delta != 0); +// +// +struct dyld_cache_slide_info3 +{ + uint32_t version; // currently 3 + uint32_t page_size; // currently 4096 (may also be 16384) + uint32_t page_starts_count; + uint64_t auth_value_add; + uint16_t page_starts[/* page_starts_count */]; +}; + +#define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE 0xFFFF // page has no rebasing + +union dyld_cache_slide_pointer3 +{ + uint64_t raw; + struct { + uint64_t pointerValue : 51, + offsetToNextPointer : 11, + unused : 2; + } plain; + + struct { + uint64_t offsetFromSharedCacheBase : 32, + diversityData : 16, + hasAddressDiversity : 1, + key : 2, + offsetToNextPointer : 11, + unused : 1, + authenticated : 1; // = 1; + } auth; +}; + + + +// The version 4 of the slide info is optimized for 32-bit caches up to 1GB. +// Since only interior pointers (pointers that point within the cache) are rebased +// (slid), we know the possible range of the pointers takes 30 bits. That +// gives us two bits to use to chain to the next rebase. +// +// Definitions: +// +// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size +// pageStarts[] = info + info->page_starts_offset +// pageExtras[] = info + info->page_extras_offset +// valueMask = ~(info->delta_mask) +// deltaShift = __builtin_ctzll(info->delta_mask) - 2 +// +// There are three cases: +// +// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE +// The page contains no values that need rebasing. +// +// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0 +// All rebase locations are in one linked list. The offset of the first +// rebase location in the page is pageStarts[pageIndex] * 4. +// +// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA +// Multiple chains are needed for all rebase locations in a page. +// The pagesExtras array contains 2 or more entries each of which is the +// start of a new chain in the page. The first is at: +// extrasStartIndex = (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_INDEX) +// The next is at extrasStartIndex+1. The last is denoted by +// having the high bit (DYLD_CACHE_SLIDE4_PAGE_EXTRA_END) of the pageExtras[]. +// +// For 32-bit architectures, there are only two bits free (the two most +// significant bits). To extract the delta, you must first subtract value_add +// from the pointer value, then AND with delta_mask, then shift by deltaShift. +// That still leaves a maximum delta to the next rebase location of 12 bytes. +// To reduce the number or chains needed, an optimization was added. Turns +// most of the non-rebased data are small values and can be co-opt'ed into +// being used in the chain. The can be done because nothing +// in the shared cache should point to the first 64KB which are in the shared +// cache header information. So if the resulting pointer points to the +// start of the cache +/-32KB, then it is actually a small number that should +// not be rebased, but just reconstituted. +// +// The code for processing a linked list (chain) is: +// +// uint32_t delta = 1; +// while ( delta != 0 ) { +// uint8_t* loc = pageStart + pageOffset; +// uint32_t rawValue = *((uint32_t*)loc); +// delta = ((rawValue & deltaMask) >> deltaShift); +// uintptr_t newValue = (rawValue & valueMask); +// if ( (newValue & 0xFFFF8000) == 0 ) { +// // small positive non-pointer, use as-is +// } +// else if ( (newValue & 0x3FFF8000) == 0x3FFF8000 ) { +// // small negative non-pointer +// newValue |= 0xC0000000; +// } +// else { +// // pointer that needs rebasing +// newValue += valueAdd; +// newValue += slideAmount; +// } +// *((uint32_t*)loc) = newValue; +// pageOffset += delta; +// } +// +// +struct dyld_cache_slide_info4 +{ + uint32_t version; // currently 4 + uint32_t page_size; // currently 4096 (may also be 16384) + uint32_t page_starts_offset; + uint32_t page_starts_count; + uint32_t page_extras_offset; + uint32_t page_extras_count; + uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location (0xC0000000) + uint64_t value_add; // base address of cache + //uint16_t page_starts[page_starts_count]; + //uint16_t page_extras[page_extras_count]; +}; +#define DYLD_CACHE_SLIDE4_PAGE_NO_REBASE 0xFFFF // page has no rebasing +#define DYLD_CACHE_SLIDE4_PAGE_INDEX 0x7FFF // mask of page_starts[] values +#define DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA 0x8000 // index is into extras array (not a chain start offset) +#define DYLD_CACHE_SLIDE4_PAGE_EXTRA_END 0x8000 // last chain entry for page + + +struct dyld_cache_local_symbols_info +{ + uint32_t nlistOffset; // offset into this chunk of nlist entries + uint32_t nlistCount; // count of nlist entries + uint32_t stringsOffset; // offset into this chunk of string pool + uint32_t stringsSize; // byte count of string pool + uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry + uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array +}; + +struct dyld_cache_local_symbols_entry +{ + uint32_t dylibOffset; // offset in cache file of start of dylib + uint32_t nlistStartIndex; // start index of locals for this dylib + uint32_t nlistCount; // number of local symbols for this dylib +}; + +struct dyld_cache_local_symbols_entry_64 +{ + uint64_t dylibOffset; // offset in cache buffer of start of dylib + uint32_t nlistStartIndex; // start index of locals for this dylib + uint32_t nlistCount; // number of local symbols for this dylib +}; + +struct dyld_subcache_entry_v1 +{ + uint8_t uuid[16]; // The UUID of the subCache file + uint64_t cacheVMOffset; // The offset of this subcache from the main cache base address +}; + +struct dyld_subcache_entry +{ + uint8_t uuid[16]; // The UUID of the subCache file + uint64_t cacheVMOffset; // The offset of this subcache from the main cache base address + char fileSuffix[32]; // The file name suffix of the subCache file e.g. ".25.data", ".03.development" +}; + +// This struct is a small piece of dynamic data that can be included in the shared region, and contains configuration +// data about the shared cache in use by the process. It is located +struct dyld_cache_dynamic_data_header +{ + char magic[16]; // e.g. "dyld_data v0" + uint64_t fsId; // The fsid_t of the shared cache being used by a process + uint64_t fsObjId; // The fs_obj_id_t of the shared cache being used by a process +}; + +// This is the location of the macOS shared cache on macOS 11.0 and later +#define MACOSX_MRM_DYLD_SHARED_CACHE_DIR "/System/Library/dyld/" + +// This is old define for the old location of the dyld cache +#define MACOSX_DYLD_SHARED_CACHE_DIR MACOSX_MRM_DYLD_SHARED_CACHE_DIR + +#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" + +#define DRIVERKIT_DYLD_SHARED_CACHE_DIR "/System/DriverKit/System/Library/dyld/" + +#if !TARGET_OS_SIMULATOR + #define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" +#else + #define DYLD_SHARED_CACHE_BASE_NAME "dyld_sim_shared_cache_" +#endif +#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development" + +#define DYLD_SHARED_CACHE_DYNAMIC_DATA_MAGIC "dyld_data v0" + +static const char* cryptexPrefixes[] = { + "/System/Volumes/Preboot/Cryptexes/OS/", + "/private/preboot/Cryptexes/OS/", + "/System/Cryptexes/OS" +}; + +static const uint64_t kDyldSharedCacheTypeDevelopment = 0; +static const uint64_t kDyldSharedCacheTypeProduction = 1; +static const uint64_t kDyldSharedCacheTypeUniversal = 2; + + + + + +#endif // __DYLD_CACHE_FORMAT__ + + diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_ctx.cpp b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_ctx.cpp new file mode 100644 index 0000000..2c9db23 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_ctx.cpp @@ -0,0 +1,183 @@ +#include "shared_cache_ctx.h" + +#include +#include +#include + +#include "logging/logging.h" + +#include "mmap_file_util.h" + +typedef uintptr_t addr_t; + +extern "C" { +extern const char *dyld_shared_cache_file_path(); +extern int __shared_region_check_np(uint64_t *startaddress); +} + +const char *shared_cache_get_file_path() { + return dyld_shared_cache_file_path(); +} + +struct dyld_cache_header *shared_cache_get_load_addr() { + addr_t shared_cache_base = 0; + if (__shared_region_check_np((uint64_t *)&shared_cache_base) != 0) { + WARN_LOG("__shared_region_check_np failed"); + } + + if (shared_cache_base) + return (struct dyld_cache_header *)shared_cache_base; + + // task info + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t ret = task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count); + if (ret != KERN_SUCCESS) { + ERROR_LOG("task_info failed, ret: %d", ret); + return NULL; + } + + // get dyld load address + auto *infos = (struct dyld_all_image_infos *)(uintptr_t)task_dyld_info.all_image_info_addr; + auto *shared_cache = (struct dyld_cache_header *)infos->sharedCacheBaseAddress; + return shared_cache; +} + +int shared_cache_load_symbols(shared_cache_ctx_t *ctx) { + uint64_t localSymbolsOffset = 0; + + bool latest_shared_cache_format = true; + + const char *shared_cache_path = shared_cache_get_file_path(); + char shared_cache_symbols_path[4096] = {0}; + { + strcat(shared_cache_symbols_path, shared_cache_path); + strcat(shared_cache_symbols_path, ".symbols"); + } + + auto mmapSharedCacheSymbolsMng = new MmapFileManager(shared_cache_symbols_path); + auto mmap_buffer = mmapSharedCacheSymbolsMng->map(); + if (mmap_buffer) { // iphoneos >= 15.0, which has .symbols file + ctx->mmap_shared_cache = (struct dyld_cache_header *)mmap_buffer; + + localSymbolsOffset = ctx->mmap_shared_cache->localSymbolsOffset; + } else { + // iphoneos < 15.0, which has no .symbols file + auto mmapSharedCacheMng = new MmapFileManager(shared_cache_symbols_path); + + auto runtime_shared_cache = ctx->runtime_shared_cache; + uint64_t mmap_length = runtime_shared_cache->localSymbolsSize; + uint64_t mmap_offset = runtime_shared_cache->localSymbolsOffset; + + if (mmap_length == 0) + return -1; + + auto mmap_buffer = mmapSharedCacheMng->map_options(mmap_length, mmap_offset); + if (!mmap_buffer) { + return -1; + } + + // fake shared cache header + auto mmap_shared_cache = + (struct dyld_cache_header *)((addr_t)mmap_buffer - runtime_shared_cache->localSymbolsOffset); + ctx->mmap_shared_cache = mmap_shared_cache; + + localSymbolsOffset = runtime_shared_cache->localSymbolsOffset; + + latest_shared_cache_format = false; + } + ctx->latest_shared_cache_format = latest_shared_cache_format; + + { + auto mmap_shared_cache = ctx->mmap_shared_cache; + auto localInfo = (struct dyld_cache_local_symbols_info *)((char *)mmap_shared_cache + localSymbolsOffset); + ctx->local_symbols_info = localInfo; + + if (ctx->latest_shared_cache_format) { + auto localEntries_64 = (struct dyld_cache_local_symbols_entry_64 *)((char *)localInfo + localInfo->entriesOffset); + ctx->local_symbols_entries_64 = localEntries_64; + } else { + auto localEntries = (struct dyld_cache_local_symbols_entry *)((char *)localInfo + localInfo->entriesOffset); + ctx->local_symbols_entries = localEntries; + } + + ctx->symtab = (nlist_t *)((char *)localInfo + localInfo->nlistOffset); + ctx->strtab = ((char *)localInfo) + localInfo->stringsOffset; + } + + return 0; +} + +int shared_cache_ctx_init(shared_cache_ctx_t *ctx) { + memset(ctx, 0, sizeof(shared_cache_ctx_t)); + + auto runtime_shared_cache = shared_cache_get_load_addr(); + if (!runtime_shared_cache) { + return -1; + } + ctx->runtime_shared_cache = runtime_shared_cache; + + // shared cache slide + auto mappings = + (struct dyld_cache_mapping_info *)((char *)runtime_shared_cache + runtime_shared_cache->mappingOffset); + uintptr_t slide = (uintptr_t)runtime_shared_cache - (uintptr_t)(mappings[0].address); + ctx->runtime_slide = slide; + + return 0; +} + +// refer: dyld +bool shared_cache_is_contain(shared_cache_ctx_t *ctx, addr_t addr, size_t length) { + struct dyld_cache_header *runtime_shared_cache; + if (ctx) { + runtime_shared_cache = ctx->runtime_shared_cache; + } else { + runtime_shared_cache = shared_cache_get_load_addr(); + } + + addr_t region_start = runtime_shared_cache->sharedRegionStart + ctx->runtime_slide; + addr_t region_end = region_start + runtime_shared_cache->sharedRegionSize; + if (addr >= region_start && addr < region_end) + return true; + + return false; +} + +int shared_cache_get_symbol_table(shared_cache_ctx_t *ctx, mach_header_t *image_header, nlist_t **out_symtab, + uint32_t *out_symtab_count, char **out_strtab) { + uint64_t textOffsetInCache = (uint64_t)image_header - (uint64_t)ctx->runtime_shared_cache; + + nlist_t *localNlists = NULL; + uint32_t localNlistCount = 0; + const char *localStrings = NULL; + + const uint32_t entriesCount = ctx->local_symbols_info->entriesCount; + for (uint32_t i = 0; i < entriesCount; ++i) { + if (ctx->latest_shared_cache_format) { + if (ctx->local_symbols_entries_64[i].dylibOffset == textOffsetInCache) { + uint32_t localNlistStart = ctx->local_symbols_entries_64[i].nlistStartIndex; + localNlistCount = ctx->local_symbols_entries_64[i].nlistCount; + localNlists = &ctx->symtab[localNlistStart]; + break; + } + } else { + if (ctx->local_symbols_entries[i].dylibOffset == textOffsetInCache) { + uint32_t localNlistStart = ctx->local_symbols_entries[i].nlistStartIndex; + localNlistCount = ctx->local_symbols_entries[i].nlistCount; + localNlists = &ctx->symtab[localNlistStart]; + break; + } + } + +#if 0 + static struct dyld_cache_image_info *imageInfos = NULL; + imageInfos = (struct dyld_cache_image_info *)((addr_t)g_mmap_shared_cache + g_mmap_shared_cache->imagesOffset); + char *image_name = (char *)g_mmap_shared_cache + imageInfos[i].pathFileOffset; + INFO_LOG("dyld image: %s", image_name); +#endif + } + *out_symtab = localNlists; + *out_symtab_count = (uint32_t)localNlistCount; + *out_strtab = (char *)ctx->strtab; + return 0; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_ctx.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_ctx.h new file mode 100644 index 0000000..a17fdc5 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_ctx.h @@ -0,0 +1,45 @@ +#include +#include +#include + +#include "shared-cache/dyld_cache_format.h" + +#if defined(__LP64__) +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct section_64 section_t; +typedef struct nlist_64 nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct section section_t; +typedef struct nlist nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT +#endif + +typedef uintptr_t addr_t; + +typedef struct shared_cache_ctx { + struct dyld_cache_header *runtime_shared_cache; + struct dyld_cache_header *mmap_shared_cache; + + uintptr_t runtime_slide; + + bool latest_shared_cache_format; + struct dyld_cache_local_symbols_info *local_symbols_info; + struct dyld_cache_local_symbols_entry *local_symbols_entries; + struct dyld_cache_local_symbols_entry_64 *local_symbols_entries_64; + + nlist_t *symtab; + char *strtab; +} shared_cache_ctx_t; + +int shared_cache_ctx_init(shared_cache_ctx_t *ctx); + +int shared_cache_load_symbols(shared_cache_ctx_t *ctx); + +bool shared_cache_is_contain(shared_cache_ctx_t *ctx, addr_t addr, size_t length); + +int shared_cache_get_symbol_table(shared_cache_ctx_t *ctx, mach_header_t *image_header, nlist_t **out_symtab, + uint32_t *out_symtab_count, char **out_strtab); \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/mmap_file_util.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/mmap_file_util.h new file mode 100644 index 0000000..c5b0506 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/mmap_file_util.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +struct MmapFileManager { + const char *file_; + uint8_t *mmap_buffer; + size_t mmap_buffer_size; + + explicit MmapFileManager(const char *file) : file_(file), mmap_buffer(nullptr) { + } + + ~MmapFileManager() { + if (mmap_buffer) { + munmap((void *)mmap_buffer, mmap_buffer_size); + } + } + + uint8_t *map() { + size_t file_size = 0; + { + struct stat s; + int rt = stat(file_, &s); + if (rt != 0) { + // printf("mmap %s failed\n", file_); + return NULL; + } + file_size = s.st_size; + } + + return map_options(file_size, 0); + } + + uint8_t *map_options(size_t in_map_size, off_t in_map_off) { + if (!mmap_buffer) { + int fd = open(file_, O_RDONLY, 0); + if (fd < 0) { + // printf("%s open failed\n", file_); + return NULL; + } + + // auto align + auto mmap_buffer = (uint8_t *)mmap(0, in_map_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, in_map_off); + if (mmap_buffer == MAP_FAILED) { + // printf("mmap %s failed\n", file_); + return NULL; + } + + close(fd); + + this->mmap_buffer = mmap_buffer; + this->mmap_buffer_size = in_map_size; + } + return mmap_buffer; + } +}; diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/pe/dobby_symbol_resolver.cc b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/pe/dobby_symbol_resolver.cc new file mode 100644 index 0000000..e1d0502 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/pe/dobby_symbol_resolver.cc @@ -0,0 +1,26 @@ +#include "SymbolResolver/dobby_symbol_resolver.h" +#include "dobby/common.h" + +#include + +#include +#include + +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include + +#undef LOG_TAG +#define LOG_TAG "DobbySymbolResolver" + +PUBLIC void *DobbySymbolResolver(const char *image_name, const char *symbol_name_pattern) { + void *result = NULL; + + HMODULE hMod = LoadLibraryExA(image_name, NULL, DONT_RESOLVE_DLL_REFERENCES); + result = GetProcAddress(hMod, symbol_name_pattern); + if (result) + return result; + + //result = resolve_elf_internal_symbol(image_name, symbol_name_pattern); + return result; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/cmake/Macros.cmake b/app/src/main/cpp/Dobby/cmake/Macros.cmake new file mode 100644 index 0000000..5774bef --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/Macros.cmake @@ -0,0 +1,3 @@ +macro(SET_OPTION option value) + set(${option} ${value} CACHE INTERNAL "" FORCE) +endmacro() \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/cmake/Util.cmake b/app/src/main/cpp/Dobby/cmake/Util.cmake new file mode 100644 index 0000000..fba658e --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/Util.cmake @@ -0,0 +1,29 @@ +# Check files list exist +function(check_files_exist CHECK_FILES) + foreach (file ${CHECK_FILES}) + if (NOT EXISTS "${file}") + message(FATAL_ERROR "${file} NOT EXISTS!") + endif () + endforeach () +endfunction(check_files_exist CHECK_FILES) + +# Search suffix files +function(search_suffix_files suffix INPUT_VARIABLE OUTPUT_VARIABLE) + set(ResultFiles) + foreach (filePath ${${INPUT_VARIABLE}}) + # message(STATUS "[*] searching *.${suffix} from ${filePath}") + file(GLOB files ${filePath}/*.${suffix}) + set(ResultFiles ${ResultFiles} ${files}) + endforeach () + set(${OUTPUT_VARIABLE} ${ResultFiles} PARENT_SCOPE) +endfunction() + + +function(get_absolute_path_list input_list output_list) + set(absolute_list) + foreach (file ${${input_list}}) + get_filename_component(absolute_file ${file} ABSOLUTE) + list(APPEND absolute_list ${absolute_file}) + endforeach () + set(${output_list} ${absolute_list} PARENT_SCOPE) +endfunction() \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/cmake/auto_source_group.cmake b/app/src/main/cpp/Dobby/cmake/auto_source_group.cmake new file mode 100644 index 0000000..f563836 --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/auto_source_group.cmake @@ -0,0 +1,32 @@ +function (auto_source_group _folder _base _pattern) + if (ARGC GREATER 3) + set(_exclude ${ARGN}) + else () + set(_exclude) + endif () + file (GLOB _files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/ ${_folder}/*) + set (folder_files) + foreach (_fname ${_files}) + if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${_fname}) + auto_source_group ("${_fname}" "${_base}" "${_pattern}" "${_exclude}") + elseif (_fname MATCHES ${_pattern}) + if(_exclude) + if (NOT _fname MATCHES ${_exclude}) + set(folder_files ${folder_files} ${_fname}) + endif () + else () + set(folder_files ${folder_files} ${_fname}) + endif () + endif () + endforeach () + + string(REPLACE "./" "" _folder2 ${_folder}) + string(REPLACE "/" "\\" _folder2 ${_folder2}) + if (_folder2 STREQUAL ".") + source_group(${_base} FILES ${folder_files}) + else () + source_group(${_base}\\${_folder2} FILES ${folder_files}) + endif () + + set(AUTO_FILES_RESULT ${AUTO_FILES_RESULT} ${folder_files} PARENT_SCOPE) +endfunction () \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/cmake/build_environment_check.cmake b/app/src/main/cpp/Dobby/cmake/build_environment_check.cmake new file mode 100644 index 0000000..790309b --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/build_environment_check.cmake @@ -0,0 +1,86 @@ +if(__BUILD_ENVIRONMENT_CHECK) + return() +endif() +set(__BUILD_ENVIRONMENT_CHECK TRUE) + +message(STATUS "") +message(STATUS "********* build environment check ***********") + + +# The Compiler ID +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(COMPILER.Clang 1) +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(COMPILER.Gcc 1) +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(COMPILER.MSVC 1) +else() + message (FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER_ID} not configured") +endif() +message(STATUS "\tCompiler: \t ${CMAKE_CXX_COMPILER_ID}") + +if(MSVC) + string(TOLOWER ${MSVC_CXX_ARCHITECTURE_ID} CMAKE_SYSTEM_PROCESSOR) + set(CMAKE_SYSTEM_PROCESSOR ${MSVC_CXX_ARCHITECTURE_ID}) +endif() + + +if(DOBBY_BUILD_SILICON) + set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_OSX_ARCHITECTURES}) +endif() + +string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} CMAKE_SYSTEM_PROCESSOR) + +# The Processor +if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*|x64.*") + set(PROCESSOR.X86_64 1) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*|amd64.*|AMD64.*") + set(PROCESSOR.X86 1) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm64.*") + set(PROCESSOR.AARCH64 1) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)") + set(PROCESSOR.AARCH64 1) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)") + set(PROCESSOR.ARM 1) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le") + message(STATUS "NOT SUPPORT ${CMAKE_SYSTEM_PROCESSOR}") + set(PROCESSOR.PPC64LE 1) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64") + message(STATUS "NOT SUPPORT ${CMAKE_SYSTEM_PROCESSOR}") + set(PROCESSOR.PPC64 1) +else() + message (FATAL_ERROR "Processor ${CMAKE_SYSTEM_PROCESSOR} not configured") +endif() +message(STATUS "\tProcessor:\t ${CMAKE_SYSTEM_PROCESSOR}") + +# The System +if(CMAKE_SYSTEM_NAME MATCHES "^Android") + set(SYSTEM.Android 1) +elseif(CMAKE_SYSTEM_NAME MATCHES "^Windows") + set(SYSTEM.Windows 1) +elseif(CMAKE_SYSTEM_NAME MATCHES "^Linux") + set(SYSTEM.Linux 1) +elseif(CMAKE_SYSTEM_NAME MATCHES "^iOS") + set(SYSTEM.iOS 1) + set(SYSTEM.Darwin 1) +elseif(CMAKE_SYSTEM_NAME MATCHES "^macOS") + set(SYSTEM.macOS 1) + set(SYSTEM.Darwin 1) +elseif(CMAKE_SYSTEM_NAME MATCHES "^Darwin") + if(PROCESSOR.AARCH64 OR PROCESSOR.ARM) + set(CMAKE_SYSTEM_NAME "iOS or Silicon") + set(SYSTEM.iOS 1) + set(SYSTEM.Silicon 1) + elseif(PROCESSOR.X86 OR PROCESSOR.X86_64) + set(CMAKE_SYSTEM_NAME "macOS") + set(SYSTEM.macOS 1) + endif() + set(SYSTEM.Darwin 1) +else() + message (FATAL_ERROR "System ${CMAKE_SYSTEM_NAME} not configured") +endif() +message(STATUS "\tSystem: \t ${CMAKE_SYSTEM_NAME}") + +message(STATUS "***************************************") +message(STATUS "") diff --git a/app/src/main/cpp/Dobby/cmake/compiler_and_linker.cmake b/app/src/main/cpp/Dobby/cmake/compiler_and_linker.cmake new file mode 100644 index 0000000..dd60f3e --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/compiler_and_linker.cmake @@ -0,0 +1,55 @@ +# :< You Shall Not Pass! +if (0) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror") +endif () + +set(linker_flags "") +if (NOT DOBBY_DEBUG) + set(linker_flags "${linker_flags} -Wl,-x -Wl,-S") +endif () + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-stack-protector") + +if (SYSTEM.Darwin) + # set(compiler_flags "${compiler_flags} -nostdinc++") +elseif (SYSTEM.Android) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer") + if (NOT DOBBY_DEBUG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections") + set(linker_flags "${linker_flags} -Wl,--gc-sections -Wl,--exclude-libs,ALL") + endif () +elseif (SYSTEM.Linux) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + if (COMPILER.Clang) + if (PROCESSOR.ARM) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=armv7-unknown-linux-gnueabihf") + elseif (PROCESSOR.ARM64) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=aarch64-unknown-linux-gnu") + elseif (PROCESSOR.X86) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=i686-unknown-linux-gnu") + elseif (PROCESSOR.X86_64) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=x86_64-unknown-linux-gnu") + endif () + endif () +elseif (SYSTEM.Windows) +endif () + +if (NOT DOBBY_DEBUG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden") +endif () + +if (PROCESSOR.ARM) + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -arch armv7 -x assembler-with-cpp") +elseif (PROCESSOR.AARCH64) + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -arch arm64 -x assembler-with-cpp") +endif () + +# sync cxx with c flags +set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS}") + +message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}") +message(STATUS "CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}") +message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") +message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") +message(STATUS "CMAKE_SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}") diff --git a/app/src/main/cpp/Dobby/cmake/dobby.xcode.source.cmake b/app/src/main/cpp/Dobby/cmake/dobby.xcode.source.cmake new file mode 100644 index 0000000..64fb0e0 --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/dobby.xcode.source.cmake @@ -0,0 +1,94 @@ +set(dobby.SOURCE_FILE_LIST + # cpu + source/core/arch/CpuFeature.cc + source/core/arch/CpuRegister.cc + + # cpu - x86 + source/core/arch/x86/cpu-x86.cc + + # assembler + source/core/assembler/assembler.cc + source/core/assembler/assembler-arm.cc + source/core/assembler/assembler-arm64.cc + source/core/assembler/assembler-ia32.cc + source/core/assembler/assembler-x64.cc + + # codegen + source/core/codegen/codegen-arm.cc + source/core/codegen/codegen-arm64.cc + source/core/codegen/codegen-ia32.cc + source/core/codegen/codegen-x64.cc + + # executable memory - code buffer + source/MemoryAllocator/CodeBuffer/CodeBufferBase.cc + source/MemoryAllocator/CodeBuffer/code-buffer-x86.cc + + # executable memory + source/MemoryAllocator/AssemblyCodeBuilder.cc + source/MemoryAllocator/MemoryArena.cc + + # instruction relocation + source/InstructionRelocation/arm/InstructionRelocationARM.cc + source/InstructionRelocation/arm64/InstructionRelocationARM64.cc + source/InstructionRelocation/x86/X86InstructionRelocation.cc + source/InstructionRelocation/x64/InstructionRelocationX64.cc + + source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c + + # intercept routing + source/InterceptRouting/InterceptRouting.cpp + + # intercept routing trampoline + source/TrampolineBridge/Trampoline/arm/trampoline-arm.cc + source/TrampolineBridge/Trampoline/arm64/trampoline-arm64.cc + source/TrampolineBridge/Trampoline/x86/trampoline-x86.cc + source/TrampolineBridge/Trampoline/x64/trampoline-x64.cc + + # intercept routing plugin (buildin) + source/InterceptRouting/Routing/FunctionInlineReplace/function-inline-replace.cc + source/InterceptRouting/Routing/FunctionInlineReplace/FunctionInlineReplaceExport.cc + + # plugin register + source/InterceptRouting/RoutingPlugin/RoutingPlugin.cc + + # unified interface + + # platform util + source/UserMode/PlatformUtil/${platform2}/ProcessRuntimeUtility.cc + + # user mode - platform interface + source/UserMode/UnifiedInterface/platform-${platform1}.cc + + # user mode - executable memory + source/UserMode/ExecMemory/code-patch-tool-${platform1}.cc + source/UserMode/ExecMemory/clear-cache-tool-all.c + + # main + source/dobby.cpp + source/Interceptor.cpp + source/InterceptEntry.cpp + ) + +if(FunctionWrapper OR DynamicBinaryInstrument) + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + # closure trampoline bridge + source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.cc + + source/TrampolineBridge/ClosureTrampolineBridge/arm/helper-arm.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm/closure-bridge-arm.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm/ClosureTrampolineARM.cc + + source/TrampolineBridge/ClosureTrampolineBridge/arm64/helper-arm64.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm64/closure-bridge-arm64.cc + source/TrampolineBridge/ClosureTrampolineBridge/arm64/ClosureTrampolineARM64.cc + + source/TrampolineBridge/ClosureTrampolineBridge/x64/helper-x64.cc + source/TrampolineBridge/ClosureTrampolineBridge/x64/closure-bridge-x64.cc + source/TrampolineBridge/ClosureTrampolineBridge/x64/ClosureTrampolineX64.cc + + # user mode - multi thread support + source/UserMode/MultiThreadSupport/ThreadSupport.cpp + source/UserMode/Thread/PlatformThread.cc + source/UserMode/Thread/platform-thread-${platform1}.cc + ) +endif() \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/cmake/platform/platform-darwin.cmake b/app/src/main/cpp/Dobby/cmake/platform/platform-darwin.cmake new file mode 100644 index 0000000..826c418 --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/platform/platform-darwin.cmake @@ -0,0 +1,30 @@ +# set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE) +set(CMAKE_INSTALL_NAME_DIR "@rpath") +set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") +add_library(DobbyX ${DOBBY_LIBRARY_TYPE} ${dobby.HEADER_FILE_LIST} ${dobby.SOURCE_FILE_LIST} ${logging.SOURCE_FILE_LIST} ${misc_helper.SOURCE_FILE_LIST} ${dobby.plugin.SOURCE_FILE_LIST}) + +set_target_properties(DobbyX + PROPERTIES + LINK_FLAGS "${linker_flags}" + COMPILE_FLAGS "${compiler_flags}" + ) + +# set framework property +set_target_properties(DobbyX PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION A + MACOSX_FRAMEWORK_IDENTIFIER "com.dobby.dobby" + # MACOSX_FRAMEWORK_INFO_PLIST Info.plist + VERSION 1.0.0 # current version + SOVERSION 1.0.0 # compatibility version + PUBLIC_HEADER include/dobby.h + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Development" + ) + +if ((SYSTEM.Darwin AND BUILDING_PLUGIN) AND (NOT DOBBY_BUILD_KERNEL_MODE)) +add_subdirectory(builtin-plugin/Dyld2HideLibrary) +add_subdirectory(builtin-plugin/ObjcRuntimeHook) +if (PROCESSOR.AARCH64) + add_subdirectory(builtin-plugin/SupervisorCallMonitor) +endif () +endif() \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/cmake/xcode_generator_helper.cmake b/app/src/main/cpp/Dobby/cmake/xcode_generator_helper.cmake new file mode 100644 index 0000000..243593d --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/xcode_generator_helper.cmake @@ -0,0 +1,9 @@ +if(CMAKE_GENERATOR STREQUAL Xcode) + message(STATUS "[*] Detect Xcode Project") + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/build/Debug) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/build/Release) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/build/Debug) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/build/Release) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/build/Debug) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/build/Release) +endif() \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/docs/compile.md b/app/src/main/cpp/Dobby/docs/compile.md new file mode 100644 index 0000000..002dba4 --- /dev/null +++ b/app/src/main/cpp/Dobby/docs/compile.md @@ -0,0 +1,89 @@ +# Build + +## CMake build options + +``` +option(DOBBY_GENERATE_SHARED "Build shared library" ON) + +option(DOBBY_DEBUG "Enable debug logging" OFF) + +option(NearBranch "Enable near branch trampoline" ON) + +option(FullFloatingPointRegisterPack "Save and pack all floating-point registers" OFF) + +option(Plugin.SymbolResolver "Enable symbol resolver" ON) + +option(Plugin.ImportTableReplace "Enable import table replace " OFF) + +option(Plugin.Android.BionicLinkerUtil "Enable android bionic linker util" OFF) + +option(DOBBY_BUILD_EXAMPLE "Build example" OFF) + +option(DOBBY_BUILD_TEST "Build test" OFF) +``` + +## Build with `scripts/platform_builder.py` + +#### Build for iphoneos + +```shell +python3 scripts/platform_builder.py --platform=iphoneos --arch=all +``` + +#### Build for macos + +``` +python3 scripts/platform_builder.py --platform=macos --arch=all +``` + +#### Build for linux + +``` +# prepare and download cmake/llvm +sh scripts/setup_linux_cross_compile.sh +python3 scripts/platform_builder.py --platform=linux --arch=all --cmake_dir=$HOME/opt/cmake-3.25.2 --llvm_dir=$HOME/opt/llvm-15.0.6 +``` + +#### Build for android + +``` +# prepare and download cmake/llvm/ndk +sh scripts/setup_linux_cross_compile.sh +python3 scripts/platform_builder.py --platform=android --arch=all --cmake_dir=$HOME/opt/cmake-3.25.2 --llvm_dir=$HOME/opt/llvm-15.0.6 --android_ndk_dir=$HOME/opt/ndk-r25b +``` + +## Build with CMake + +#### Build for host + +```shell +cd Dobby && mkdir cmake-build-host && cd cmake-build-host +cmake .. +make -j4 +``` + +## Build with Android Studio CMake + +``` +if(NOT TARGET dobby) +set(DOBBY_DIR /Users/jmpews/Workspace/Project.wrk/Dobby) +macro(SET_OPTION option value) + set(${option} ${value} CACHE INTERNAL "" FORCE) +endmacro() +SET_OPTION(DOBBY_DEBUG OFF) +SET_OPTION(DOBBY_GENERATE_SHARED OFF) +add_subdirectory(${DOBBY_DIR} dobby) +get_property(DOBBY_INCLUDE_DIRECTORIES + TARGET dobby + PROPERTY INCLUDE_DIRECTORIES) +include_directories( + . + ${DOBBY_INCLUDE_DIRECTORIES} + $ +) +endif() + +add_library(native-lib SHARED + ${DOBBY_DIR}/examples/socket_example.cc + native-lib.cpp) +``` diff --git a/app/src/main/cpp/Dobby/examples/CMakeLists.txt b/app/src/main/cpp/Dobby/examples/CMakeLists.txt new file mode 100644 index 0000000..b461a1c --- /dev/null +++ b/app/src/main/cpp/Dobby/examples/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(socket_example + main.cc + socket_example.cc + ) + +target_link_libraries(socket_example + dobby + logging + ) + + +add_library(socket_example_lib SHARED + socket_example.cc + ) + +target_link_libraries(socket_example_lib + dobby + ) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/examples/main.cc b/app/src/main/cpp/Dobby/examples/main.cc new file mode 100644 index 0000000..33eeddf --- /dev/null +++ b/app/src/main/cpp/Dobby/examples/main.cc @@ -0,0 +1,14 @@ +#include +#include +#include +#include +#include +#include + +int main(int argc, char const *argv[]) { + + std::cout << "Start..." << std::endl; + + sleep(100); + return 0; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/examples/socket_example.cc b/app/src/main/cpp/Dobby/examples/socket_example.cc new file mode 100644 index 0000000..1377fa3 --- /dev/null +++ b/app/src/main/cpp/Dobby/examples/socket_example.cc @@ -0,0 +1,212 @@ +#include "dobby.h" + +#include "logging/logging.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +std::map *func_map; + +// clang-format off +const char *func_array[] = { +// "__loader_dlopen", + + "dlsym", + "dlclose", + + "open", + "write", + "read", + "close", + + "socket", + "connect", + "bind", + "listen", + "accept", + "send", + "recv", + + // "pthread_create" +}; + +const char *func_short_array[] = { + "accept", +}; +// clang-format on + +#define pac_strip(symbol) +#if defined(__APPLE__) && __arm64e__ +#if __has_feature(ptrauth_calls) +#define pac_strip(symbol) +//#define pac_strip(symbol) *(void **)&symbol = (void *)ptrauth_sign_unauthenticated((void *)symbol, ptrauth_key_asia, 0) +#endif +#endif + +#define install_hook(name, fn_ret_t, fn_args_t...) \ + fn_ret_t (*orig_##name)(fn_args_t); \ + fn_ret_t fake_##name(fn_args_t); \ + /* __attribute__((constructor)) */ static void install_hook_##name() { \ + void *sym_addr = DobbySymbolResolver(NULL, #name); \ + DobbyHook(sym_addr, (dobby_dummy_func_t)fake_##name, (dobby_dummy_func_t *)&orig_##name); \ + pac_strip(orig_##name); \ + printf("install hook %s:%p:%p\n", #name, sym_addr, orig_##name); \ + } \ + fn_ret_t fake_##name(fn_args_t) + +install_hook(pthread_create, int, pthread_t *thread, const pthread_attr_t *attrs, void *(*start_routine)(void *), + void *arg, unsigned int create_flags) { + INFO_LOG("pthread_create: %p", start_routine); + return orig_pthread_create(thread, attrs, start_routine, arg, create_flags); +} + +void common_handler(void *address, DobbyRegisterContext *ctx) { + auto iter = func_map->find(address); + if (iter != func_map->end()) { + INFO_LOG("func %s:%p invoke", iter->second, iter->first); + } +} + +uint64_t socket_demo_server(void *ctx); + +uint64_t socket_demo_client(void *ctx); + +#if 1 + +__attribute__((constructor)) static void ctor() { + logger_set_options(0, 0, 0, LOG_LEVEL_DEBUG, false, false); + + void *func = NULL; + func_map = new std::map(); + for (int i = 0; i < sizeof(func_array) / sizeof(char *); ++i) { + func = DobbySymbolResolver(NULL, func_array[i]); + if (func == NULL) { + INFO_LOG("func %s not resolve", func_array[i]); + continue; + } + func_map->insert(std::pair(func, func_array[i])); + } + + for (auto iter = func_map->begin(), e = func_map->end(); iter != e; iter++) { + bool is_short = false; + for (int i = 0; i < sizeof(func_short_array) / sizeof(char *); ++i) { + if (strcmp(func_short_array[i], iter->second) == 0) { + is_short = true; + break; + } + } + if (is_short) { + dobby_enable_near_branch_trampoline(); + DobbyInstrument(iter->first, common_handler); + dobby_disable_near_branch_trampoline(); + } else { + DobbyInstrument(iter->first, common_handler); + } + } + +#if defined(__APPLE__) + // DobbyImportTableReplace(NULL, "_pthread_create", (void *)fake_pthread_create, (void **)&orig_pthread_create); +#endif + + // install_hook_pthread_create(); + + pthread_t socket_server; + pthread_create(&socket_server, NULL, (void *(*)(void *))socket_demo_server, NULL); + + usleep(10000); + pthread_t socket_client; + pthread_create(&socket_client, NULL, (void *(*)(void *))socket_demo_client, NULL); + + // pthread_join(socket_client, 0); + // pthread_join(socket_server, 0); +} + +#include +#include +#include + +#define PORT 49494 + +uint64_t socket_demo_server(void *ctx) { + int server_fd, new_socket; + struct sockaddr_in address; + int opt = 1; + int addrlen = sizeof(address); + char buffer[1024] = {0}; + char *hello = "Hello from server"; + + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + ERROR_LOG("socket failed: %s", strerror(errno)); + return -1; + } + + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + ERROR_LOG("setsockopt: %s", strerror(errno)); + return -1; + } + + address.sin_family = AF_INET; + address.sin_port = htons(PORT); + address.sin_addr.s_addr = INADDR_ANY; + + if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { + ERROR_LOG("bind failed: %s", strerror(errno)); + return -1; + } + if (listen(server_fd, 3) < 0) { + ERROR_LOG("listen failed: %s", strerror(errno)); + return -1; + } + if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { + ERROR_LOG("accept failed: %s", strerror(errno)); + return -1; + } + + int ret = recv(new_socket, buffer, 1024, 0); + INFO_LOG("[server] %s", buffer); + + send(new_socket, hello, strlen(hello), 0); + INFO_LOG("[server] Hello message sent"); + return 0; +} + +uint64_t socket_demo_client(void *ctx) { + int sock = 0; + struct sockaddr_in serv_addr; + char *hello = "Hello from client"; + char buffer[1024] = {0}; + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ERROR_LOG("socket failed"); + return -1; + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(PORT); + + // Convert IPv4 and IPv6 addresses from text to binary form + if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { + ERROR_LOG("inet_pton failed"); + return -1; + } + + if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + ERROR_LOG("connect failed"); + return -1; + } + + send(sock, hello, strlen(hello), 0); + INFO_LOG("[client] Hello message sent"); + + int ret = recv(sock, buffer, 1024, 0); + INFO_LOG("[client] %s", buffer); + return 0; +} + +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/README b/app/src/main/cpp/Dobby/external/TINYSTL/README new file mode 100644 index 0000000..b6d5879 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/README @@ -0,0 +1 @@ +ref: https://github.com/mendsley/tinystl \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/algorithm.h b/app/src/main/cpp/Dobby/external/TINYSTL/algorithm.h new file mode 100644 index 0000000..7b9637e --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/algorithm.h @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/allocator.h b/app/src/main/cpp/Dobby/external/TINYSTL/allocator.h new file mode 100644 index 0000000..685d98c --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/allocator.h @@ -0,0 +1,49 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_ALLOCATOR_H +#define TINYSTL_ALLOCATOR_H + +#include + +namespace tinystl { + + struct allocator { + static void* static_allocate(size_t bytes) { + return operator new(bytes); + } + + static void static_deallocate(void* ptr, size_t /*bytes*/) { + operator delete(ptr); + } + }; +} + +#ifndef TINYSTL_ALLOCATOR +# define TINYSTL_ALLOCATOR ::tinystl::allocator +#endif + +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/buffer.h b/app/src/main/cpp/Dobby/external/TINYSTL/buffer.h new file mode 100644 index 0000000..7ec5d19 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/buffer.h @@ -0,0 +1,310 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_BUFFER_H +#define TINYSTL_BUFFER_H + +#include +#include +#include + +namespace tinystl { + + template + struct buffer { + T* first; + T* last; + T* capacity; + }; + + template + static inline void buffer_destroy_range_traits(T* first, T* last, pod_traits) { + for (; first < last; ++first) + first->~T(); + } + + template + static inline void buffer_destroy_range_traits(T*, T*, pod_traits) { + } + + template + static inline void buffer_destroy_range(T* first, T* last) { + buffer_destroy_range_traits(first, last, pod_traits()); + } + + template + static inline void buffer_fill_urange_traits(T* first, T* last, pod_traits) { + for (; first < last; ++first) + new(placeholder(), first) T(); + } + + template + static inline void buffer_fill_urange_traits(T* first, T* last, pod_traits) { + for (; first < last; ++first) + *first = T(); + } + + template + static inline void buffer_fill_urange_traits(T* first, T* last, const T& value, pod_traits) { + for (; first < last; ++first) + new(placeholder(), first) T(value); + } + + template + static inline void buffer_fill_urange_traits(T* first, T* last, const T& value, pod_traits) { + for (; first < last; ++first) + *first = value; + } + + template + static inline void buffer_move_urange_traits(T* dest, T* first, T* last, pod_traits) { + for (T* it = first; it != last; ++it, ++dest) + move_construct(dest, *it); + buffer_destroy_range(first, last); + } + + template + static inline void buffer_move_urange_traits(T* dest, T* first, T* last, pod_traits) { + for (; first != last; ++first, ++dest) + *dest = *first; + } + + template + static inline void buffer_bmove_urange_traits(T* dest, T* first, T* last, pod_traits) { + dest += (last - first); + for (T* it = last; it != first; --it, --dest) { + move_construct(dest - 1, *(it - 1)); + buffer_destroy_range(it - 1, it); + } + } + + template + static inline void buffer_bmove_urange_traits(T* dest, T* first, T* last, pod_traits) { + dest += (last - first); + for (T* it = last; it != first; --it, --dest) + *(dest - 1) = *(it - 1); + } + + template + static inline void buffer_move_urange(T* dest, T* first, T* last) { + buffer_move_urange_traits(dest, first, last, pod_traits()); + } + + template + static inline void buffer_bmove_urange(T* dest, T* first, T* last) { + buffer_bmove_urange_traits(dest, first, last, pod_traits()); + } + + template + static inline void buffer_fill_urange(T* first, T* last) { + buffer_fill_urange_traits(first, last, pod_traits()); + } + + template + static inline void buffer_fill_urange(T* first, T* last, const T& value) { + buffer_fill_urange_traits(first, last, value, pod_traits()); + } + + template + static inline void buffer_init(buffer* b) { + b->first = b->last = b->capacity = 0; + } + + template + static inline void buffer_destroy(buffer* b) { + buffer_destroy_range(b->first, b->last); + Alloc::static_deallocate(b->first, (size_t)((char*)b->capacity - (char*)b->first)); + } + + template + static inline void buffer_reserve(buffer* b, size_t capacity) { + if (b->first + capacity <= b->capacity) + return; + + typedef T* pointer; + const size_t size = (size_t)(b->last - b->first); + pointer newfirst = (pointer)Alloc::static_allocate(sizeof(T) * capacity); + buffer_move_urange(newfirst, b->first, b->last); + Alloc::static_deallocate(b->first, sizeof(T) * capacity); + + b->first = newfirst; + b->last = newfirst + size; + b->capacity = newfirst + capacity; + } + + template + static inline void buffer_resize(buffer* b, size_t size) { + buffer_reserve(b, size); + + buffer_fill_urange(b->last, b->first + size); + buffer_destroy_range(b->first + size, b->last); + b->last = b->first + size; + } + + template + static inline void buffer_resize(buffer* b, size_t size, const T& value) { + buffer_reserve(b, size); + + buffer_fill_urange(b->last, b->first + size, value); + buffer_destroy_range(b->first + size, b->last); + b->last = b->first + size; + } + + template + static inline void buffer_shrink_to_fit(buffer* b) { + if (b->capacity != b->last) { + if (b->last == b->first) { + const size_t capacity = (size_t)(b->capacity - b->first); + Alloc::static_deallocate(b->first, sizeof(T)*capacity); + b->capacity = b->first = b->last = nullptr; + } else { + const size_t capacity = (size_t)(b->capacity - b->first); + const size_t size = (size_t)(b->last - b->first); + T* newfirst = (T*)Alloc::static_allocate(sizeof(T) * size); + buffer_move_urange(newfirst, b->first, b->last); + Alloc::static_deallocate(b->first, sizeof(T) * capacity); + b->first = newfirst; + b->last = newfirst + size; + b->capacity = b->last; + } + } + } + + template + static inline void buffer_clear(buffer* b) { + buffer_destroy_range(b->first, b->last); + b->last = b->first; + } + + template + static inline T* buffer_insert_common(buffer* b, T* where, size_t count) { + const size_t offset = (size_t)(where - b->first); + const size_t newsize = (size_t)((b->last - b->first) + count); + if (b->first + newsize > b->capacity) + buffer_reserve(b, (newsize * 3) / 2); + + where = b->first + offset; + + if (where != b->last) + buffer_bmove_urange(where + count, where, b->last); + + b->last = b->first + newsize; + + return where; + } + + template + static inline void buffer_insert(buffer* b, T* where, const Param* first, const Param* last) { + typedef const char* pointer; + const size_t count = last - first; + const bool frombuf = ((pointer)b->first <= (pointer)first && (pointer)b->last >= (pointer)last); + size_t offset; + if (frombuf) { + offset = (pointer)first - (pointer)b->first; + if ((pointer)where <= (pointer)first) + offset += count * sizeof(T); + where = buffer_insert_common(b, where, count); + first = (Param*)((pointer)b->first + offset); + last = first + count; + } + else { + where = buffer_insert_common(b, where, count); + } + for (; first != last; ++first, ++where) + new(placeholder(), where) T(*first); + } + + template + static inline void buffer_insert(buffer* b, T* where, size_t count) { + where = buffer_insert_common(b, where, count); + for (T* end = where+count; where != end; ++where) + new(placeholder(), where) T(); + } + + template + static inline void buffer_append(buffer* b, const Param* param) { + if (b->capacity != b->last) { + new(placeholder(), b->last) T(*param); + ++b->last; + } else { + buffer_insert(b, b->last, param, param + 1); + } + } + + template + static inline void buffer_append(buffer* b) { + if (b->capacity != b->last) { + new(placeholder(), b->last) T(); + ++b->last; + } else { + buffer_insert(b, b->last, 1); + } + } + + template + static inline T* buffer_erase(buffer* b, T* first, T* last) { + typedef T* pointer; + const size_t count = (last - first); + for (pointer it = last, end = b->last, dest = first; it != end; ++it, ++dest) + move(*dest, *it); + + buffer_destroy_range(b->last - count, b->last); + + b->last -= count; + return first; + } + + template + static inline T* buffer_erase_unordered(buffer* b, T* first, T* last) { + typedef T* pointer; + const size_t count = (last - first); + const size_t tail = (b->last - last); + pointer it = b->last - ((count < tail) ? count : tail); + for (pointer end = b->last, dest = first; it != end; ++it, ++dest) + move(*dest, *it); + + buffer_destroy_range(b->last - count, b->last); + + b->last -= count; + return first; + } + + template + static inline void buffer_swap(buffer* b, buffer* other) { + typedef T* pointer; + const pointer tfirst = b->first, tlast = b->last, tcapacity = b->capacity; + b->first = other->first, b->last = other->last, b->capacity = other->capacity; + other->first = tfirst, other->last = tlast, other->capacity = tcapacity; + } + + template + static inline void buffer_move(buffer* dst, buffer* src) { + dst->first = src->first, dst->last = src->last, dst->capacity = src->capacity; + src->first = src->last = src->capacity = nullptr; + } +} + +#endif //TINYSTL_BUFFER_H diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/function.h b/app/src/main/cpp/Dobby/external/TINYSTL/function.h new file mode 100644 index 0000000..fd8c6de --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/function.h @@ -0,0 +1,48 @@ +#pragma once + +namespace tinystl { + +template class function; + +template class function { +public: + function() { + } + + template function(T functor) { + m_func = [](const void *user, Args... args) -> Return { + const T &func = *static_cast(user); + return func(static_cast(args)...); + }; + + m_dtor = [](void *user) { + T &func = *static_cast(user); + func.~T(); + }; + + new (tinystl::placeholder(), m_storage) T(static_cast(functor)); + } + + ~function() { + if (m_dtor) + m_dtor(m_storage); + } + + Return operator()(Args... args) const { + return m_func(m_storage, static_cast(args)...); + } + + explicit operator bool() { + return m_func != nullptr; + } + + using Func = Return (*)(const void *, Args...); + Func m_func = nullptr; + using Dtor = void (*)(void *); + Dtor m_dtor = nullptr; + union { + void *m_storage[8]; + }; +}; + +} // namespace tinystl \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/hash.h b/app/src/main/cpp/Dobby/external/TINYSTL/hash.h new file mode 100644 index 0000000..c03b326 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/hash.h @@ -0,0 +1,53 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_STRINGHASH_H +#define TINYSTL_STRINGHASH_H + +#include + +namespace tinystl { + + static inline size_t hash_string(const char* str, size_t len) { + // Implementation of sdbm a public domain string hash from Ozan Yigit + // see: http://www.eecs.harvard.edu/margo/papers/usenix91/paper.ps + + size_t hash = 0; + typedef const char* pointer; + for (pointer it = str, end = str + len; it != end; ++it) + hash = *it + (hash << 6) + (hash << 16) - hash; + + return hash; + } + + template + inline size_t hash(const T& value) { + const size_t asint = (size_t)value; + return hash_string((const char*)&asint, sizeof(asint)); + } +} + +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/hash_base.h b/app/src/main/cpp/Dobby/external/TINYSTL/hash_base.h new file mode 100644 index 0000000..30f449b --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/hash_base.h @@ -0,0 +1,292 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_HASH_BASE_H +#define TINYSTL_HASH_BASE_H + +#include +#include + +namespace tinystl { + + template + struct pair { + pair(); + pair(const pair& other); + pair(pair&& other); + pair(const Key& key, const Value& value); + pair(Key&& key, Value&& value); + + pair& operator=(const pair& other); + pair& operator=(pair&& other); + + Key first; + Value second; + }; + + template + inline pair::pair() { + } + + template + inline pair::pair(const pair& other) + : first(other.first) + , second(other.second) + { + } + + template + inline pair::pair(pair&& other) + : first(static_cast(other.first)) + , second(static_cast(other.second)) + { + } + + template + inline pair::pair(const Key& key, const Value& value) + : first(key) + , second(value) + { + } + + template + inline pair::pair(Key&& key, Value&& value) + : first(static_cast(key)) + , second(static_cast(value)) + { + } + + template + inline pair& pair::operator=(const pair& other) { + first = other.first; + second = other.second; + return *this; + } + + template + inline pair& pair::operator=(pair&& other) { + first = static_cast(other.first); + second = static_cast(other.second); + return *this; + } + + template + static inline pair::type, typename remove_reference::type> + make_pair(Key&& key, Value&& value) { + return pair::type, typename remove_reference::type>( + static_cast(key) + , static_cast(value) + ); + } + + + template + struct unordered_hash_node { + unordered_hash_node(const Key& key, const Value& value); + unordered_hash_node(Key&& key, Value&& value); + + const Key first; + Value second; + unordered_hash_node* next; + unordered_hash_node* prev; + + private: + unordered_hash_node& operator=(const unordered_hash_node&); + }; + + template + inline unordered_hash_node::unordered_hash_node(const Key& key, const Value& value) + : first(key) + , second(value) + { + } + + template + inline unordered_hash_node::unordered_hash_node(Key&& key, Value&& value) + : first(static_cast(key)) + , second(static_cast(value)) + { + } + + template + struct unordered_hash_node { + explicit unordered_hash_node(const Key& key); + explicit unordered_hash_node(Key&& key); + + const Key first; + unordered_hash_node* next; + unordered_hash_node* prev; + + private: + unordered_hash_node& operator=(const unordered_hash_node&); + }; + + template + inline unordered_hash_node::unordered_hash_node(const Key& key) + : first(key) + { + } + + template + inline unordered_hash_node::unordered_hash_node(Key&& key) + : first(static_cast(key)) + { + } + + template + static inline void unordered_hash_node_insert(unordered_hash_node* node, size_t hash, unordered_hash_node** buckets, size_t nbuckets) { + size_t bucket = hash & (nbuckets - 1); + + unordered_hash_node* it = buckets[bucket + 1]; + node->next = it; + if (it) { + node->prev = it->prev; + it->prev = node; + if (node->prev) + node->prev->next = node; + } else { + size_t newbucket = bucket; + while (newbucket && !buckets[newbucket]) + --newbucket; + + unordered_hash_node* prev = buckets[newbucket]; + while (prev && prev->next) + prev = prev->next; + + node->prev = prev; + if (prev) + prev->next = node; + } + + // propagate node through buckets + for (; it == buckets[bucket]; --bucket) { + buckets[bucket] = node; + if (!bucket) + break; + } + } + + template + static inline void unordered_hash_node_erase(const unordered_hash_node* where, size_t hash, unordered_hash_node** buckets, size_t nbuckets) { + size_t bucket = hash & (nbuckets - 1); + + unordered_hash_node* next = where->next; + for (; buckets[bucket] == where; --bucket) { + buckets[bucket] = next; + if (!bucket) + break; + } + + if (where->prev) + where->prev->next = where->next; + if (next) + next->prev = where->prev; + } + + template + struct unordered_hash_iterator { + Node* operator->() const; + Node& operator*() const; + Node* node; + }; + + template + struct unordered_hash_iterator { + + unordered_hash_iterator() {} + unordered_hash_iterator(unordered_hash_iterator other) + : node(other.node) + { + } + + const Node* operator->() const; + const Node& operator*() const; + const Node* node; + }; + + template + struct unordered_hash_iterator > { + const Key* operator->() const; + const Key& operator*() const; + unordered_hash_node* node; + }; + + template + static inline bool operator==(const unordered_hash_iterator& lhs, const unordered_hash_iterator& rhs) { + return lhs.node == rhs.node; + } + + template + static inline bool operator!=(const unordered_hash_iterator& lhs, const unordered_hash_iterator& rhs) { + return lhs.node != rhs.node; + } + + template + static inline void operator++(unordered_hash_iterator& lhs) { + lhs.node = lhs.node->next; + } + + template + inline Node* unordered_hash_iterator::operator->() const { + return node; + } + + template + inline Node& unordered_hash_iterator::operator*() const { + return *node; + } + + template + inline const Node* unordered_hash_iterator::operator->() const { + return node; + } + + template + inline const Node& unordered_hash_iterator::operator*() const { + return *node; + } + + template + inline const Key* unordered_hash_iterator >::operator->() const { + return &node->first; + } + + template + inline const Key& unordered_hash_iterator >::operator*() const { + return node->first; + } + + template + static inline Node unordered_hash_find(const Key& key, Node* buckets, size_t nbuckets) { + const size_t bucket = hash(key) & (nbuckets - 2); + for (Node it = buckets[bucket], end = buckets[bucket+1]; it != end; it = it->next) + if (it->first == key) + return it; + + return 0; + } +} +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/new.h b/app/src/main/cpp/Dobby/external/TINYSTL/new.h new file mode 100644 index 0000000..cca0016 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/new.h @@ -0,0 +1,44 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_NEW_H +#define TINYSTL_NEW_H + +#include + +namespace tinystl { + +struct placeholder {}; +} // namespace tinystl + +inline void *operator new(size_t, tinystl::placeholder, void *ptr) { + return ptr; +} + +inline void operator delete(void *, tinystl::placeholder, void *) throw() { +} + +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/stddef.h b/app/src/main/cpp/Dobby/external/TINYSTL/stddef.h new file mode 100644 index 0000000..a31dd34 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/stddef.h @@ -0,0 +1,43 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_STDDEF_H +#define TINYSTL_STDDEF_H + +#if defined(_WIN64) + typedef long long unsigned int size_t; + typedef long long int ptrdiff_t; +#elif defined(_WIN32) + typedef unsigned int size_t; + typedef int ptrdiff_t; +#elif defined (__linux__) && defined(__SIZE_TYPE__) && defined(__PTRDIFF_TYPE__) + typedef __SIZE_TYPE__ size_t; + typedef __PTRDIFF_TYPE__ ptrdiff_t; +#else +# include +#endif + +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/string.h b/app/src/main/cpp/Dobby/external/TINYSTL/string.h new file mode 100644 index 0000000..3fe45d1 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/string.h @@ -0,0 +1,295 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_STRING_H +#define TINYSTL_STRING_H + +#include +#include +#include + +namespace tinystl { + + template + class basic_string { + public: + basic_string(); + basic_string(const basic_string& other); + basic_string(basic_string&& other); + basic_string(const char* sz); + basic_string(const char* sz, size_t len); + ~basic_string(); + + basic_string& operator=(const basic_string& other); + basic_string& operator=(basic_string&& other); + + const char* c_str() const; + size_t size() const; + + void reserve(size_t size); + void resize(size_t size); + + void clear(); + void append(const char* first, const char* last); + void assign(const char* s, size_t n); + + void shrink_to_fit(); + void swap(basic_string& other); + + private: + typedef char* pointer; + pointer m_first; + pointer m_last; + pointer m_capacity; + + static const size_t c_nbuffer = 12; + char m_buffer[12]; + }; + + template + inline basic_string::basic_string() + : m_first(m_buffer) + , m_last(m_buffer) + , m_capacity(m_buffer + c_nbuffer) + { + resize(0); + } + + template + inline basic_string::basic_string(const basic_string& other) + : m_first(m_buffer) + , m_last(m_buffer) + , m_capacity(m_buffer + c_nbuffer) + { + reserve(other.size()); + append(other.m_first, other.m_last); + } + + template + inline basic_string::basic_string(basic_string&& other) + { + if (other.m_first == other.m_buffer) { + m_first = m_buffer; + m_last = m_buffer; + m_capacity = m_buffer + c_nbuffer; + reserve(other.size()); + append(other.m_first, other.m_last); + } else { + m_first = other.m_first; + m_last = other.m_last; + m_capacity = other.m_capacity; + } + other.m_first = other.m_last = other.m_buffer; + other.m_capacity = other.m_buffer + c_nbuffer; + other.resize(0); + } + + template + inline basic_string::basic_string(const char* sz) + : m_first(m_buffer) + , m_last(m_buffer) + , m_capacity(m_buffer + c_nbuffer) + { + size_t len = 0; + for (const char* it = sz; *it; ++it) + ++len; + + reserve(len); + append(sz, sz + len); + } + + template + inline basic_string::basic_string(const char* sz, size_t len) + : m_first(m_buffer) + , m_last(m_buffer) + , m_capacity(m_buffer + c_nbuffer) + { + reserve(len); + append(sz, sz + len); + } + + template + inline basic_string::~basic_string() { + if (m_first != m_buffer) + allocator::static_deallocate(m_first, m_capacity - m_first); + } + + template + inline basic_string& basic_string::operator=(const basic_string& other) { + basic_string(other).swap(*this); + return *this; + } + + template + inline basic_string& basic_string::operator=(basic_string&& other) { + basic_string(static_cast(other)).swap(*this); + return *this; + } + + template + inline const char* basic_string::c_str() const { + return m_first; + } + + template + inline size_t basic_string::size() const + { + return (size_t)(m_last - m_first); + } + + template + inline void basic_string::reserve(size_t capacity) { + if (m_first + capacity + 1 <= m_capacity) + return; + + const size_t size = (size_t)(m_last - m_first); + + pointer newfirst = (pointer)allocator::static_allocate(capacity + 1); + for (pointer it = m_first, newit = newfirst, end = m_last; it != end; ++it, ++newit) + *newit = *it; + if (m_first != m_buffer) + allocator::static_deallocate(m_first, m_capacity - m_first); + + m_first = newfirst; + m_last = newfirst + size; + m_capacity = m_first + capacity; + } + + template + inline void basic_string::resize(size_t size) { + const size_t prevSize = m_last-m_first; + reserve(size); + if (size > prevSize) + for (pointer it = m_last, end = m_first + size + 1; it < end; ++it) + *it = 0; + else if (m_last != m_first) + m_first[size] = 0; + + m_last = m_first + size; + } + + template + inline void basic_string::clear() { + resize(0); + } + + template + inline void basic_string::append(const char* first, const char* last) { + const size_t newsize = (size_t)((m_last - m_first) + (last - first) + 1); + if (m_first + newsize > m_capacity) + reserve((newsize * 3) / 2); + + for (; first != last; ++m_last, ++first) + *m_last = *first; + *m_last = 0; + } + + template + inline void basic_string::assign(const char* sz, size_t n) { + clear(); + append(sz, sz+n); + } + + template + inline void basic_string::shrink_to_fit() { + if (m_first == m_buffer) { + } else if (m_last == m_first) { + const size_t capacity = (size_t)(m_capacity - m_first); + if (capacity) + allocator::static_deallocate(m_first, capacity+1); + m_capacity = m_first; + } else if (m_capacity != m_last) { + const size_t size = (size_t)(m_last - m_first); + char* newfirst = (pointer)allocator::static_allocate(size+1); + for (pointer in = m_first, out = newfirst; in != m_last + 1; ++in, ++out) + *out = *in; + if (m_first != m_capacity) + allocator::static_deallocate(m_first, m_capacity+1-m_first); + m_first = newfirst; + m_last = newfirst+size; + m_capacity = m_last; + } + } + + template + inline void basic_string::swap(basic_string& other) { + const pointer tfirst = m_first, tlast = m_last, tcapacity = m_capacity; + m_first = other.m_first, m_last = other.m_last, m_capacity = other.m_capacity; + other.m_first = tfirst, other.m_last = tlast, other.m_capacity = tcapacity; + + char tbuffer[c_nbuffer]; + + if (m_first == other.m_buffer) + for (pointer it = other.m_buffer, end = m_last, out = tbuffer; it != end; ++it, ++out) + *out = *it; + + if (other.m_first == m_buffer) { + other.m_last = other.m_last - other.m_first + other.m_buffer; + other.m_first = other.m_buffer; + other.m_capacity = other.m_buffer + c_nbuffer; + + for (pointer it = other.m_first, end = other.m_last, in = m_buffer; it != end; ++it, ++in) + *it = *in; + *other.m_last = 0; + } + + if (m_first == other.m_buffer) { + m_last = m_last - m_first + m_buffer; + m_first = m_buffer; + m_capacity = m_buffer + c_nbuffer; + + for (pointer it = m_first, end = m_last, in = tbuffer; it != end; ++it, ++in) + *it = *in; + *m_last = 0; + } + } + + template + inline bool operator==(const basic_string& lhs, const basic_string& rhs) { + typedef const char* pointer; + + const size_t lsize = lhs.size(), rsize = rhs.size(); + if (lsize != rsize) + return false; + + pointer lit = lhs.c_str(), rit = rhs.c_str(); + pointer lend = lit + lsize; + while (lit != lend) + if (*lit++ != *rit++) + return false; + + return true; + } + + template + static inline size_t hash(const basic_string& value) { + return hash_string(value.c_str(), value.size()); + } + + typedef basic_string string; +} + +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/string_view.h b/app/src/main/cpp/Dobby/external/TINYSTL/string_view.h new file mode 100644 index 0000000..beae9c4 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/string_view.h @@ -0,0 +1,147 @@ +/*- + * Copyright 2012-1017 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_STRING_VIEW_H +#define TINYSTL_STRING_VIEW_H + +#include + +namespace tinystl { + + class string_view + { + public: + typedef char value_type; + typedef char* pointer; + typedef const char* const_pointer; + typedef char& reference; + typedef const char& const_reference; + typedef const_pointer iterator; + typedef const_pointer const_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + static constexpr size_type npos = size_type(-1); + + constexpr string_view(); + constexpr string_view(const char* s, size_type count); + constexpr string_view(const char* s); + constexpr string_view(const string_view&) = default; + string_view& operator=(const string_view&) = default; + + constexpr const char* data() const; + constexpr char operator[](size_type pos) const; + constexpr size_type size() const; + constexpr bool empty() const; + constexpr iterator begin() const; + constexpr const_iterator cbegin() const; + constexpr iterator end() const; + constexpr const_iterator cend() const; + constexpr string_view substr(size_type pos = 0, size_type count = npos) const; + constexpr void swap(string_view& v); + + private: + string_view(decltype(nullptr)) = delete; + + static constexpr size_type strlen(const char*); + + const char* m_str; + size_type m_size; + }; + + constexpr string_view::string_view() + : m_str(nullptr) + , m_size(0) + { + } + + constexpr string_view::string_view(const char* s, size_type count) + : m_str(s) + , m_size(count) + { + } + + constexpr string_view::string_view(const char* s) + : m_str(s) + , m_size(strlen(s)) + { + } + + constexpr const char* string_view::data() const { + return m_str; + } + + constexpr char string_view::operator[](size_type pos) const { + return m_str[pos]; + } + + constexpr string_view::size_type string_view::size() const { + return m_size; + } + + constexpr bool string_view::empty() const { + return 0 == m_size; + } + + constexpr string_view::iterator string_view::begin() const { + return m_str; + } + + constexpr string_view::const_iterator string_view::cbegin() const { + return m_str; + } + + constexpr string_view::iterator string_view::end() const { + return m_str + m_size; + } + + constexpr string_view::const_iterator string_view::cend() const { + return m_str + m_size; + } + + constexpr string_view string_view::substr(size_type pos, size_type count) const { + return string_view(m_str + pos, npos == count ? m_size - pos : count); + } + + constexpr void string_view::swap(string_view& v) { + const char* strtmp = m_str; + size_type sizetmp = m_size; + m_str = v.m_str; + m_size = v.m_size; + v.m_str = strtmp; + v.m_size = sizetmp; + } + + constexpr string_view::size_type string_view::strlen(const char* s) { + for (size_t len = 0; ; ++len) { + if (0 == s[len]) { + return len; + } + } + } +} + +#endif // TINYSTL_STRING_VIEW_H diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/traits.h b/app/src/main/cpp/Dobby/external/TINYSTL/traits.h new file mode 100644 index 0000000..84574ee --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/traits.h @@ -0,0 +1,100 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_TRAITS_H +#define TINYSTL_TRAITS_H + +#include + +#if defined(__GNUC__) +# define TINYSTL_TRY_POD_OPTIMIZATION(t) __is_pod(t) +#elif defined(_MSC_VER) +# define TINYSTL_TRY_POD_OPTIMIZATION(t) (!__is_class(t) || __is_pod(t)) +#else +# define TINYSTL_TRY_POD_OPTIMIZATION(t) false +#endif + +namespace tinystl { + template struct pod_traits {}; + + template struct swap_holder; + + template + static inline void move_impl(T& a, T& b, ...) { + a = b; + } + + template + static inline void move_impl(T& a, T& b, T*, swap_holder* = 0) { + a.swap(b); + } + + template + static inline void move(T& a, T&b) { + move_impl(a, b, (T*)0); + } + + template + static inline void move_construct_impl(T* a, T& b, ...) { + new(placeholder(), a) T(b); + } + + template + static inline void move_construct_impl(T* a, T& b, void*, swap_holder* = 0) { + // If your type T does not have a default constructor, simply insert: + // struct tinystl_nomove_construct; + // in the class definition + new(placeholder(), a) T; + a->swap(b); + } + + template + static inline void move_construct_impl(T* a, T& b, T*, typename T::tinystl_nomove_construct* = 0) { + new(placeholder(), a) T(b); + } + + template + static inline void move_construct(T* a, T& b) { + move_construct_impl(a, b, (T*)0); + } + + template + struct remove_reference { + typedef T type; + }; + + template + struct remove_reference { + typedef T type; + }; + + template + struct remove_reference { + typedef T type; + }; +} + +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/unordered_map.h b/app/src/main/cpp/Dobby/external/TINYSTL/unordered_map.h new file mode 100644 index 0000000..4be6a78 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/unordered_map.h @@ -0,0 +1,289 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_UNORDERED_MAP_H +#define TINYSTL_UNORDERED_MAP_H + +#include +#include +#include +#include + +namespace tinystl { + + template + class unordered_map { + public: + unordered_map(); + unordered_map(const unordered_map& other); + unordered_map(unordered_map&& other); + ~unordered_map(); + + unordered_map& operator=(const unordered_map& other); + unordered_map& operator=(unordered_map&& other); + + typedef pair value_type; + + typedef unordered_hash_iterator > const_iterator; + typedef unordered_hash_iterator > iterator; + + iterator begin(); + iterator end(); + + const_iterator begin() const; + const_iterator end() const; + + void clear(); + bool empty() const; + size_t size() const; + + const_iterator find(const Key& key) const; + iterator find(const Key& key); + pair insert(const pair& p); + pair emplace(pair&& p); + void erase(const_iterator where); + + Value& operator[](const Key& key); + + void swap(unordered_map& other); + + private: + + void rehash(size_t nbuckets); + + typedef unordered_hash_node* pointer; + + size_t m_size; + tinystl::buffer m_buckets; + }; + + template + inline unordered_map::unordered_map() + : m_size(0) + { + buffer_init(&m_buckets); + buffer_resize(&m_buckets, 9, 0); + } + + template + inline unordered_map::unordered_map(const unordered_map& other) + : m_size(other.m_size) + { + const size_t nbuckets = (size_t)(other.m_buckets.last - other.m_buckets.first); + buffer_init(&m_buckets); + buffer_resize(&m_buckets, nbuckets, 0); + + for (pointer it = *other.m_buckets.first; it; it = it->next) { + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(it->first, it->second); + newnode->next = newnode->prev = 0; + + unordered_hash_node_insert(newnode, hash(it->first), m_buckets.first, nbuckets - 1); + } + } + + template + inline unordered_map::unordered_map(unordered_map&& other) + : m_size(other.m_size) + { + buffer_move(&m_buckets, &other.m_buckets); + other.m_size = 0; + } + + template + inline unordered_map::~unordered_map() { + if (m_buckets.first != m_buckets.last) + clear(); + buffer_destroy(&m_buckets); + } + + template + inline unordered_map& unordered_map::operator=(const unordered_map& other) { + unordered_map(other).swap(*this); + return *this; + } + + template + inline unordered_map& unordered_map::operator=(unordered_map&& other) { + unordered_map(static_cast(other)).swap(*this); + return *this; + } + + template + inline typename unordered_map::iterator unordered_map::begin() { + iterator it; + it.node = *m_buckets.first; + return it; + } + + template + inline typename unordered_map::iterator unordered_map::end() { + iterator it; + it.node = 0; + return it; + } + + template + inline typename unordered_map::const_iterator unordered_map::begin() const { + const_iterator cit; + cit.node = *m_buckets.first; + return cit; + } + + template + inline typename unordered_map::const_iterator unordered_map::end() const { + const_iterator cit; + cit.node = 0; + return cit; + } + + template + inline bool unordered_map::empty() const { + return m_size == 0; + } + + template + inline size_t unordered_map::size() const { + return m_size; + } + + template + inline void unordered_map::clear() { + pointer it = *m_buckets.first; + while (it) { + const pointer next = it->next; + it->~unordered_hash_node(); + Alloc::static_deallocate(it, sizeof(unordered_hash_node)); + + it = next; + } + + m_buckets.last = m_buckets.first; + buffer_resize(&m_buckets, 9, 0); + m_size = 0; + } + + template + inline typename unordered_map::iterator unordered_map::find(const Key& key) { + iterator result; + result.node = unordered_hash_find(key, m_buckets.first, (size_t)(m_buckets.last - m_buckets.first)); + return result; + } + + template + inline typename unordered_map::const_iterator unordered_map::find(const Key& key) const { + iterator result; + result.node = unordered_hash_find(key, m_buckets.first, (size_t)(m_buckets.last - m_buckets.first)); + return result; + } + + template + inline void unordered_map::rehash(size_t nbuckets) { + if (m_size + 1 > 4 * nbuckets) { + pointer root = *m_buckets.first; + + const size_t newnbuckets = ((size_t)(m_buckets.last - m_buckets.first) - 1) * 8; + m_buckets.last = m_buckets.first; + buffer_resize(&m_buckets, newnbuckets + 1, 0); + unordered_hash_node** buckets = m_buckets.first; + + while (root) { + const pointer next = root->next; + root->next = root->prev = 0; + unordered_hash_node_insert(root, hash(root->first), buckets, newnbuckets); + root = next; + } + } + } + + template + inline pair::iterator, bool> unordered_map::insert(const pair& p) { + pair result; + result.second = false; + + result.first = find(p.first); + if (result.first.node != 0) + return result; + + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(p.first, p.second); + newnode->next = newnode->prev = 0; + + const size_t nbuckets = (size_t)(m_buckets.last - m_buckets.first); + unordered_hash_node_insert(newnode, hash(p.first), m_buckets.first, nbuckets - 1); + + ++m_size; + rehash(nbuckets); + + result.first.node = newnode; + result.second = true; + return result; + } + + template + inline pair::iterator, bool> unordered_map::emplace(pair&& p) { + pair result; + result.second = false; + + result.first = find(p.first); + if (result.first.node != 0) + return result; + + const size_t keyhash = hash(p.first); + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(static_cast(p.first), static_cast(p.second)); + newnode->next = newnode->prev = 0; + + const size_t nbuckets = (size_t)(m_buckets.last - m_buckets.first); + unordered_hash_node_insert(newnode, keyhash, m_buckets.first, nbuckets - 1); + + ++m_size; + rehash(nbuckets); + + result.first.node = newnode; + result.second = true; + return result; + } + + template + inline void unordered_map::erase(const_iterator where) { + unordered_hash_node_erase(where.node, hash(where->first), m_buckets.first, (size_t)(m_buckets.last - m_buckets.first) - 1); + + where->~unordered_hash_node(); + Alloc::static_deallocate((void*)where.node, sizeof(unordered_hash_node)); + --m_size; + } + + template + inline Value& unordered_map::operator[](const Key& key) { + return insert(pair(key, Value())).first->second; + } + + template + inline void unordered_map::swap(unordered_map& other) { + size_t tsize = other.m_size; + other.m_size = m_size, m_size = tsize; + buffer_swap(&m_buckets, &other.m_buckets); + } +} +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/unordered_set.h b/app/src/main/cpp/Dobby/external/TINYSTL/unordered_set.h new file mode 100644 index 0000000..450fbc5 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/unordered_set.h @@ -0,0 +1,265 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_UNORDERED_SET_H +#define TINYSTL_UNORDERED_SET_H + +#include +#include +#include +#include + +namespace tinystl { + + template + class unordered_set { + public: + unordered_set(); + unordered_set(const unordered_set& other); + unordered_set(unordered_set&& other); + ~unordered_set(); + + unordered_set& operator=(const unordered_set& other); + unordered_set& operator=(unordered_set&& other); + + typedef unordered_hash_iterator > const_iterator; + typedef const_iterator iterator; + + iterator begin() const; + iterator end() const; + + void clear(); + bool empty() const; + size_t size() const; + + iterator find(const Key& key) const; + pair insert(const Key& key); + pair emplace(Key&& key); + void erase(iterator where); + size_t erase(const Key& key); + + void swap(unordered_set& other); + + private: + + void rehash(size_t nbuckets); + + typedef unordered_hash_node* pointer; + + size_t m_size; + tinystl::buffer m_buckets; + }; + + template + inline unordered_set::unordered_set() + : m_size(0) + { + buffer_init(&m_buckets); + buffer_resize(&m_buckets, 9, 0); + } + + template + inline unordered_set::unordered_set(const unordered_set& other) + : m_size(other.m_size) + { + const size_t nbuckets = (size_t)(other.m_buckets.last - other.m_buckets.first); + buffer_init(&m_buckets); + buffer_resize(&m_buckets, nbuckets, 0); + + for (pointer it = *other.m_buckets.first; it; it = it->next) { + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(*it); + newnode->next = newnode->prev = 0; + unordered_hash_node_insert(newnode, hash(it->first), m_buckets.first, nbuckets - 1); + } + } + + template + inline unordered_set::unordered_set(unordered_set&& other) + : m_size(other.m_size) + { + buffer_move(&m_buckets, &other.m_buckets); + other.m_size = 0; + } + + template + inline unordered_set::~unordered_set() { + if (m_buckets.first != m_buckets.last) + clear(); + buffer_destroy(&m_buckets); + } + + template + inline unordered_set& unordered_set::operator=(const unordered_set& other) { + unordered_set(other).swap(*this); + return *this; + } + + template + inline unordered_set& unordered_set::operator=(unordered_set&& other) { + unordered_set(static_cast(other)).swap(*this); + return *this; + } + + template + inline typename unordered_set::iterator unordered_set::begin() const { + iterator cit; + cit.node = *m_buckets.first; + return cit; + } + + template + inline typename unordered_set::iterator unordered_set::end() const { + iterator cit; + cit.node = 0; + return cit; + } + + template + inline bool unordered_set::empty() const { + return m_size == 0; + } + + template + inline size_t unordered_set::size() const { + return m_size; + } + + template + inline void unordered_set::clear() { + pointer it = *m_buckets.first; + while (it) { + const pointer next = it->next; + it->~unordered_hash_node(); + Alloc::static_deallocate(it, sizeof(unordered_hash_node)); + + it = next; + } + + m_buckets.last = m_buckets.first; + buffer_resize(&m_buckets, 9, 0); + m_size = 0; + } + + template + inline typename unordered_set::iterator unordered_set::find(const Key& key) const { + iterator result; + result.node = unordered_hash_find(key, m_buckets.first, (size_t)(m_buckets.last - m_buckets.first)); + return result; + } + + template + inline void unordered_set::rehash(size_t nbuckets) { + if (m_size + 1 > 4 * nbuckets) { + pointer root = *m_buckets.first; + + const size_t newnbuckets = ((size_t)(m_buckets.last - m_buckets.first) - 1) * 8; + m_buckets.last = m_buckets.first; + buffer_resize(&m_buckets, newnbuckets + 1, 0); + unordered_hash_node** buckets = m_buckets.first; + + while (root) { + const pointer next = root->next; + root->next = root->prev = 0; + unordered_hash_node_insert(root, hash(root->first), buckets, newnbuckets); + root = next; + } + } + } + + template + inline pair::iterator, bool> unordered_set::insert(const Key& key) { + pair result; + result.second = false; + + result.first = find(key); + if (result.first.node != 0) + return result; + + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(key); + newnode->next = newnode->prev = 0; + + const size_t nbuckets = (size_t)(m_buckets.last - m_buckets.first); + unordered_hash_node_insert(newnode, hash(key), m_buckets.first, nbuckets - 1); + + ++m_size; + rehash(nbuckets); + + result.first.node = newnode; + result.second = true; + return result; + } + + template + inline pair::iterator, bool> unordered_set::emplace(Key&& key) { + pair result; + result.second = false; + + result.first = find(key); + if (result.first.node != 0) + return result; + + const size_t keyhash = hash(key); + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(static_cast(key)); + newnode->next = newnode->prev = 0; + + const size_t nbuckets = (size_t)(m_buckets.last - m_buckets.first); + unordered_hash_node_insert(newnode, keyhash, m_buckets.first, nbuckets - 1); + + ++m_size; + rehash(nbuckets); + + result.first.node = newnode; + result.second = true; + return result; + } + + template + inline void unordered_set::erase(iterator where) { + unordered_hash_node_erase(where.node, hash(where.node->first), m_buckets.first, (size_t)(m_buckets.last - m_buckets.first) - 1); + + where.node->~unordered_hash_node(); + Alloc::static_deallocate((void*)where.node, sizeof(unordered_hash_node)); + --m_size; + } + + template + inline size_t unordered_set::erase(const Key& key) { + const iterator it = find(key); + if (it.node == 0) + return 0; + + erase(it); + return 1; + } + + template + void unordered_set::swap(unordered_set& other) { + size_t tsize = other.m_size; + other.m_size = m_size, m_size = tsize; + buffer_swap(&m_buckets, &other.m_buckets); + } +} +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/vector.h b/app/src/main/cpp/Dobby/external/TINYSTL/vector.h new file mode 100644 index 0000000..6e3b0e1 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/vector.h @@ -0,0 +1,353 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_VECTOR_H +#define TINYSTL_VECTOR_H + +#include +#include +#include +#include + +namespace tinystl { +template class vector { + using iterator = T *; + +public: + vector(); + vector(const vector &other); + vector(vector &&other); + vector(size_t size); + vector(size_t size, const T &value); + vector(const T *first, const T *last); + ~vector(); + + vector &operator=(const vector &other); + vector &operator=(vector &&other); + + void assign(const T *first, const T *last); + + const T *data() const; + T *data(); + size_t size() const; + size_t capacity() const; + bool empty() const; + + T &operator[](size_t idx); + const T &operator[](size_t idx) const; + + const T &front() const; + T &front(); + const T &back() const; + T &back(); + + void resize(size_t size); + void resize(size_t size, const T &value); + void clear(); + void reserve(size_t capacity); + + void push_back(const T &t); + void pop_back(); + + void emplace_back(); + template void emplace_back(const Param ¶m); + + void shrink_to_fit(); + + void swap(vector &other); + + typedef T value_type; + + iterator begin(); + iterator end(); + + typedef const T *const_iterator; + const_iterator begin() const; + const_iterator end() const; + + void insert(iterator where); + void insert(iterator where, const T &value); + void insert(iterator where, const T *first, const T *last); + + template void emplace(iterator where, const Param ¶m); + + iterator erase(iterator where); + iterator erase(iterator first, iterator last); + + iterator erase_unordered(iterator where); + iterator erase_unordered(iterator first, iterator last); + + iterator find(const T &other) const; + void sort(int (*compare)(const T &elem0, const T &elem1)); + void sort(unsigned begin, unsigned end, int (*compare)(const T &elem0, const T &elem1)); + +private: + int partition(int (*compare)(const T &elem0, const T &elem1), int p, int r); + void quick_sort(int (*compare)(const T &elem0, const T &elem1), int p, int r); + +private: + buffer m_buffer; +}; + +template inline vector::vector() { + buffer_init(&m_buffer); +} + +template inline vector::vector(const vector &other) { + buffer_init(&m_buffer); + buffer_reserve(&m_buffer, other.size()); + buffer_insert(&m_buffer, m_buffer.last, other.m_buffer.first, other.m_buffer.last); +} + +template inline vector::vector(vector &&other) { + buffer_move(&m_buffer, &other.m_buffer); +} + +template inline vector::vector(size_t size) { + buffer_init(&m_buffer); + buffer_resize(&m_buffer, size); +} + +template inline vector::vector(size_t size, const T &value) { + buffer_init(&m_buffer); + buffer_resize(&m_buffer, size, value); +} + +template inline vector::vector(const T *first, const T *last) { + buffer_init(&m_buffer); + buffer_insert(&m_buffer, m_buffer.last, first, last); +} + +template inline vector::~vector() { + buffer_destroy(&m_buffer); +} + +template inline vector &vector::operator=(const vector &other) { + vector(other).swap(*this); + return *this; +} + +template vector &vector::operator=(vector &&other) { + buffer_destroy(&m_buffer); + buffer_move(&m_buffer, &other.m_buffer); + return *this; +} + +template inline void vector::assign(const T *first, const T *last) { + buffer_clear(&m_buffer); + buffer_insert(&m_buffer, m_buffer.last, first, last); +} + +template inline const T *vector::data() const { + return m_buffer.first; +} + +template inline T *vector::data() { + return m_buffer.first; +} + +template inline size_t vector::size() const { + return (size_t)(m_buffer.last - m_buffer.first); +} + +template inline size_t vector::capacity() const { + return (size_t)(m_buffer.capacity - m_buffer.first); +} + +template inline bool vector::empty() const { + return m_buffer.last == m_buffer.first; +} + +template inline T &vector::operator[](size_t idx) { + return m_buffer.first[idx]; +} + +template inline const T &vector::operator[](size_t idx) const { + return m_buffer.first[idx]; +} + +template inline const T &vector::front() const { + return m_buffer.first[0]; +} + +template inline T &vector::front() { + return m_buffer.first[0]; +} + +template inline const T &vector::back() const { + return m_buffer.last[-1]; +} + +template inline T &vector::back() { + return m_buffer.last[-1]; +} + +template inline void vector::resize(size_t size) { + buffer_resize(&m_buffer, size); +} + +template inline void vector::resize(size_t size, const T &value) { + buffer_resize(&m_buffer, size, value); +} + +template inline void vector::clear() { + buffer_clear(&m_buffer); +} + +template inline void vector::reserve(size_t capacity) { + buffer_reserve(&m_buffer, capacity); +} + +template inline void vector::push_back(const T &t) { + buffer_append(&m_buffer, &t); +} + +template inline void vector::emplace_back() { + buffer_append(&m_buffer); +} + +template +template +inline void vector::emplace_back(const Param ¶m) { + buffer_append(&m_buffer, ¶m); +} + +template inline void vector::pop_back() { + buffer_erase(&m_buffer, m_buffer.last - 1, m_buffer.last); +} + +template inline void vector::shrink_to_fit() { + buffer_shrink_to_fit(&m_buffer); +} + +template inline void vector::swap(vector &other) { + buffer_swap(&m_buffer, &other.m_buffer); +} + +template inline typename vector::iterator vector::begin() { + return m_buffer.first; +} + +template inline typename vector::iterator vector::end() { + return m_buffer.last; +} + +template inline typename vector::const_iterator vector::begin() const { + return m_buffer.first; +} + +template inline typename vector::const_iterator vector::end() const { + return m_buffer.last; +} + +template inline void vector::insert(typename vector::iterator where) { + buffer_insert(&m_buffer, where, 1); +} + +template inline void vector::insert(iterator where, const T &value) { + buffer_insert(&m_buffer, where, &value, &value + 1); +} + +template +inline void vector::insert(iterator where, const T *first, const T *last) { + buffer_insert(&m_buffer, where, first, last); +} + +template +inline typename vector::iterator vector::erase(iterator where) { + return buffer_erase(&m_buffer, where, where + 1); +} + +template +inline typename vector::iterator vector::erase(iterator first, iterator last) { + return buffer_erase(&m_buffer, first, last); +} + +template +inline typename vector::iterator vector::erase_unordered(iterator where) { + return buffer_erase_unordered(&m_buffer, where, where + 1); +} + +template +inline typename vector::iterator vector::erase_unordered(iterator first, iterator last) { + return buffer_erase_unordered(&m_buffer, first, last); +} + +template +template +void vector::emplace(typename vector::iterator where, const Param ¶m) { + buffer_insert(&m_buffer, where, ¶m, ¶m + 1); +} + +template +inline typename vector::iterator vector::find(const T &other) const { + for (unsigned i = 0; i < size(); ++i) + if (m_buffer.first[i] == other) + return &m_buffer.first[i]; + + return m_buffer.last; +} + +template +inline int vector::partition(int (*compare)(const T &elem0, const T &elem1), int p, int r) { + T tmp, pivot = m_buffer.first[p]; + int left = p; + + for (int i = p + 1; i <= r; i++) { + if (compare(m_buffer.first[i], pivot) < 0) { + left++; + tmp = m_buffer.first[i]; + m_buffer.first[i] = m_buffer.first[left]; + m_buffer.first[left] = tmp; + } + } + tmp = m_buffer.first[p]; + m_buffer.first[p] = m_buffer.first[left]; + m_buffer.first[left] = tmp; + return left; +} + +template +inline void vector::quick_sort(int (*compare)(const T &elem0, const T &elem1), int p, int r) { + if (p < r) { + int q = partition(compare, p, r); + quick_sort(compare, p, q - 1); + quick_sort(compare, q + 1, r); + } +} + +template +inline void vector::sort(int (*compare)(const T &elem0, const T &elem1)) { + quick_sort(compare, 0, (int)size() - 1); +} + +template +inline void vector::sort(unsigned begin, unsigned end, int (*compare)(const T &elem0, const T &elem1)) { + quick_sort(compare, (int)begin, (int)end); +} +} // namespace tinystl + +#endif // TINYSTL_VECTOR_H diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/CMakeLists.txt b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/CMakeLists.txt new file mode 100644 index 0000000..fca13b6 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories(.) + +if(NOT DOBBY_BUILD_KERNEL_MODE) + set(SOURCE_FILE_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/variable_cache.c + ${CMAKE_CURRENT_SOURCE_DIR}/async_logger.cc + ${CMAKE_CURRENT_SOURCE_DIR}/format_printer.cc + ) +else() + set(SOURCE_FILE_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/format_printer.cc + ) +endif() + +add_library(misc_helper + ${SOURCE_FILE_LIST} + ${SOURCE_HEADER_LIST} +) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/async_logger.cc b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/async_logger.cc new file mode 100644 index 0000000..49b11cb --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/async_logger.cc @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define aync_logger_buffer_size (20 * 1024 * 1024) +int async_logger_buffer_cursor = 0; +char async_logger_buffer[aync_logger_buffer_size]; + +static pthread_mutex_t async_logger_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int output_fd = -1; + +void async_logger_print(char *str) { + pthread_mutex_lock(&async_logger_mutex); +#if 0 + { + write(STDOUT_FILENO, str, strlen(str) + 1); + } +#endif + memcpy(async_logger_buffer + async_logger_buffer_cursor, str, strlen(str)); + async_logger_buffer_cursor += strlen(str); + pthread_mutex_unlock(&async_logger_mutex); + return; +} + +static void *async_logger_print_impl(void *ctx) { + while (1) { + pthread_mutex_lock(&async_logger_mutex); + if (async_logger_buffer_cursor > 0) { + write(output_fd, async_logger_buffer, async_logger_buffer_cursor); + async_logger_buffer_cursor = 0; + } + pthread_mutex_unlock(&async_logger_mutex); + sleep(1); + } +} + +void async_logger_init(char *logger_path) { + static int async_logger_initialized = 0; + if (async_logger_initialized) + return; + async_logger_initialized = 1; + + // init stdout write lock + pthread_mutex_t write_mutex; + pthread_mutex_init(&write_mutex, NULL); + + output_fd = STDOUT_FILENO; + if (logger_path) { + int fd = open(logger_path, O_CREAT | O_WRONLY | O_TRUNC, 0644); + output_fd = fd; + } + + // init async logger + pthread_mutex_init(&async_logger_mutex, NULL); + pthread_t async_logger_thread; + int ret = pthread_create(&async_logger_thread, NULL, async_logger_print_impl, NULL); +} diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/pthread_helper.cc b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/pthread_helper.cc new file mode 100644 index 0000000..bc080f6 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/pthread_helper.cc @@ -0,0 +1,129 @@ +#include "pthread_helper.h" +#include +#ifdef _WIN32 + +typedef void (*windows_thread)(void *); + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { + uintptr_t handle = _beginthread((windows_thread)start_routine, 0, arg); + thread->handle = (HANDLE)handle; + if (thread->handle == (HANDLE)-1) { + return 1; + } else { + return 0; + } +} + +int pthread_detach(pthread_t thread) { + /* Do nothing */ + return 0; +} + +void pthread_exit(void *value_ptr) { + _endthread(); +} + +int pthread_join(pthread_t thread, void **value_ptr) { + DWORD retvalue = WaitForSingleObject(thread.handle, INFINITE); + if (retvalue == WAIT_OBJECT_0) { + return 0; + } else { + return EINVAL; + } +} + +pthread_t pthread_self(void) { + pthread_t pt; + pt.handle = GetCurrentThread(); + return pt; +} + +int pthread_cancel(pthread_t thread) { + fprintf(stderr, "DO NOT USE THIS FUNCTION. pthread_cancel\n"); + abort(); + return 0; +} + +/* --------------------- MUTEX --------------------*/ + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { + /* do nothing */ + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) { + /* do nothing */ + return 0; +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) { + return !CloseHandle(mutex->handle); +} + +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { + HANDLE handle = CreateMutex(NULL, FALSE, NULL); + if (handle != NULL) { + mutex->handle = handle; + return 0; + } else { + return 1; + } +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) { + DWORD retvalue = WaitForSingleObject(mutex->handle, INFINITE); + if (retvalue == WAIT_OBJECT_0) { + return 0; + } else { + return EINVAL; + } +} + +int pthread_mutex_trylock(pthread_mutex_t *mutex) { + DWORD retvalue = WaitForSingleObject(mutex->handle, 0); + if (retvalue == WAIT_OBJECT_0) { + return 0; + } else if (retvalue == WAIT_TIMEOUT) { + return EBUSY; + } else { + return EINVAL; + } +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) { + return !ReleaseMutex(mutex->handle); +} + +/* ------------------- Thead Specific Data ------------------ */ + +int pthread_key_create(pthread_key_t *key, void (*destr_function)(void *)) { + DWORD dkey = TlsAlloc(); + if (dkey != 0xFFFFFFFF) { + *key = dkey; + return 0; + } else { + return EAGAIN; + } +} + +int pthread_key_delete(pthread_key_t key) { + if (TlsFree(key)) { + return 0; + } else { + return EINVAL; + } +} + +int pthread_setspecific(pthread_key_t key, const void *pointer) { + if (TlsSetValue(key, (LPVOID)pointer)) { + return 0; + } else { + return EINVAL; + } +} + +void *pthread_getspecific(pthread_key_t key) { + return TlsGetValue(key); +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/pthread_helper.h b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/pthread_helper.h new file mode 100644 index 0000000..3ed01f6 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/pthread_helper.h @@ -0,0 +1,85 @@ +/* + * light weight pthread compatible library for Windows + * (C) 2009 Okamura Yasunobu + * + * WARNING This library does NOT support all future of pthread + * + */ + +#ifndef CROSS_THREAD_H +#define CROSS_THREAD_H + +#ifdef _WIN32 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef struct pthread_tag { + HANDLE handle; +} pthread_t; + +typedef struct pthread_mutex_tag { + HANDLE handle; +} pthread_mutex_t; + +/* stub */ +typedef struct pthread_attr_tag { + int attr; +} pthread_attr_t; + +typedef struct pthread_mutexattr_tag { + int attr; +} pthread_mutexattr_t; + +typedef DWORD pthread_key_t; + +/* ignore attribute */ +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); + +/* ignore value_ptr */ +void pthread_exit(void *value_ptr); + +/* ignore value_ptr */ +int pthread_join(pthread_t thread, void **value_ptr); + +pthread_t pthread_self(void); + +/* do nothing */ +int pthread_detach(pthread_t thread); + +/* DO NOT USE */ +int pthread_cancel(pthread_t thread); + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); /* do nothing */ +int pthread_mutexattr_init(pthread_mutexattr_t *attr); /* do nothing */ + +int pthread_mutex_destroy(pthread_mutex_t *mutex); +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); +int pthread_mutex_lock(pthread_mutex_t *mutex); +int pthread_mutex_trylock(pthread_mutex_t *mutex); +int pthread_mutex_unlock(pthread_mutex_t *mutex); + +/* ignore deconstructor */ +int pthread_key_create(pthread_key_t *key, void (*destr_function)(void *)); +int pthread_key_delete(pthread_key_t key); +int pthread_setspecific(pthread_key_t key, const void *pointer); +void *pthread_getspecific(pthread_key_t key); + +#define sleep(num) Sleep(1000 * (num)) + +#ifdef __cplusplus +} +#endif + +#else +#include +#include +#define Sleep(num) usleep(num * 1000) +#endif + +#endif /* CROSS_THREAD_H */ diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/unistd_helper.h b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/unistd_helper.h new file mode 100644 index 0000000..d990fa3 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/deprecated/unistd_helper.h @@ -0,0 +1,30 @@ +#ifdef _WIN32 + +#include +#define open _open +#define read _read +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_CREAT _O_CREAT +#define O_TRUNC _O_TRUNC + +#define ssize_t int + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +/* should be in some equivalent to */ +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#else + +#include + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/format_printer.cc b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/format_printer.cc new file mode 100644 index 0000000..47c8b8f --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/format_printer.cc @@ -0,0 +1,11 @@ +#include "misc-helper/format_printer.h" + +void hexdump(const uint8_t *bytes, size_t len) { + size_t ix; + for (ix = 0; ix < len; ++ix) { + if (ix != 0 && !(ix % 16)) + LOG_FUNCTION_IMPL(0, "\n"); + LOG_FUNCTION_IMPL(0, "%02X ", bytes[ix]); + } + LOG_FUNCTION_IMPL(0, "\n"); +} diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/async_logger.h b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/async_logger.h new file mode 100644 index 0000000..03e0d4e --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/async_logger.h @@ -0,0 +1,8 @@ +#ifndef ASYNC_LOGGER_H +#define ASYNC_LOGGER_H + +void async_logger_print(char *str); + +void async_logger_init(char *logger_path); + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/format_printer.h b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/format_printer.h new file mode 100644 index 0000000..1123908 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/format_printer.h @@ -0,0 +1,3 @@ +#include "dobby/common.h" + +void hexdump(const uint8_t *bytes, size_t len); \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/variable_cache.h b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/variable_cache.h new file mode 100644 index 0000000..bf490fd --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/variable_cache.h @@ -0,0 +1,17 @@ +#ifndef VARIABLE_CACHE_H +#define VARIABLE_CACHE_H + +#include + +#define cache_set stash +void cache_set(const char *name, uint64_t value); + +#define cache_get(x) cache(x) +#define assert_cache(x) (assert(cache(x)), cache(x)) +uint64_t cache_get(const char *name); + +int serialized_to_file(const char *filepath); + +int unserialized_from_file(const char *filepath); + +#endif diff --git a/app/src/main/cpp/Dobby/external/deprecated/misc-helper/variable_cache.c b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/variable_cache.c new file mode 100644 index 0000000..895a2cb --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/variable_cache.c @@ -0,0 +1,118 @@ +#include "misc-helper/variable_cache.h" + +#include +#include +#include + +#include +#include "deprecated/unistd_helper.h" + +#include + +typedef struct queue_entry_t { + struct queue_entry *next; + struct queue_entry *prev; +} queue_entry_t; + +/* TODO: add a property or attribute indicate not serialized */ +typedef struct var_entry_t { + queue_entry_t entry_; + char key[128]; + uint64_t value; +} var_entry_t; + +var_entry_t *root = NULL; + +static var_entry_t *cache_get_entry_internal(const char *name) { + var_entry_t *entry; + entry = root; + while (entry != NULL) { + if (strcmp(name, entry->key) == 0) { + return entry; + } + entry = (var_entry_t *)entry->entry_.next; + } + return NULL; +} + +void cache_set(const char *name, uint64_t value) { + var_entry_t *entry = cache_get_entry_internal(name); + if (entry) { + entry->value = value; + return; + } + + entry = (var_entry_t *)malloc(sizeof(var_entry_t)); + strcpy(entry->key, name); + entry->value = value; + + entry->entry_.next = (struct queue_entry *)root; + root = entry; +} + +uint64_t cache_get(const char *name) { + var_entry_t *entry = cache_get_entry_internal(name); + if (entry) { + return entry->value; + } + return 0; +} + +typedef struct entry_block { + int key_length; + int value_length; +} entry_block_t; + +int serialized_to_file(const char *filepath) { + int fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0660); + if (fd == -1) { + printf("open %s failed: %s\n", filepath, strerror(errno)); + return -1; + } + + var_entry_t *entry; + entry = root; + while (entry != NULL) { + entry_block_t block = {0}; + { + block.key_length = strlen(entry->key) + 1; + block.value_length = sizeof(uint64_t); + write(fd, &block, sizeof(block)); + } + + write(fd, entry->key, block.key_length); + write(fd, &entry->value, block.value_length); + + entry = (var_entry_t *)entry->entry_.next; + } + close(fd); + return 0; +} + +int unserialized_from_file(const char *filepath) { + int fd = open(filepath, O_RDONLY); + if (fd == -1) { + printf("open %s failed: %s\n", filepath, strerror(errno)); + return -1; + } + + entry_block_t block = {0}; + while (read(fd, &block, sizeof(block)) > 0) { + char key[128] = {0}; + uint64_t value = 0; + + read(fd, (void *)&key, block.key_length); + read(fd, (void *)&value, block.value_length); + + { + var_entry_t *entry = (var_entry_t *)malloc(sizeof(var_entry_t)); + strcpy(entry->key, key); + entry->value = value; + + entry->entry_.next = (struct queue_entry *)root; + root = entry; + } + } + + return 0; +} diff --git a/app/src/main/cpp/Dobby/external/logging/CMakeLists.txt b/app/src/main/cpp/Dobby/external/logging/CMakeLists.txt new file mode 100644 index 0000000..aa1a0b7 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories(.) + +set(SOURCE_FILE_LIST + logging.cc + ) + +if (DOBBY_BUILD_KERNEL_MODE) + set(SOURCE_FILE_LIST + logging_kern.cc + ) +endif () + +get_absolute_path_list(SOURCE_FILE_LIST SOURCE_FILE_LIST_) +set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST_}) + +add_library(logging + ${SOURCE_FILE_LIST} + ) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/logging/logging.cc b/app/src/main/cpp/Dobby/external/logging/logging.cc new file mode 100644 index 0000000..fb57c0e --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/logging.cc @@ -0,0 +1,137 @@ +#include "logging/logging.h" + +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) || defined(__APPLE__) +#include +#include +#include +#include +#include +#endif + +#if defined(__APPLE__) +#include +#include +#include +#include +#include +#include "./priv_headers/_simple.h" +#endif + +#if defined(_WIN32) +#define PUBLIC +#else +#define PUBLIC __attribute__((visibility("default"))) +#define INTERNAL __attribute__((visibility("internal"))) +#endif + +#if defined(__ANDROID__) +#include +#endif + +#pragma clang diagnostic ignored "-Wformat" + +Logger *Logger::g_logger = nullptr; + +void Logger::logv(LogLevel level, const char *_fmt, va_list ap) { + if (level < log_level_) + return; + + char fmt_buffer[4096] = {0}; + + if (log_tag_ != nullptr) { + snprintf(fmt_buffer + strlen(fmt_buffer), sizeof(fmt_buffer) - strlen(fmt_buffer), "%s ", log_tag_); + } + + if (enable_time_tag_) { + time_t now = time(NULL); + struct tm *tm = localtime(&now); + snprintf(fmt_buffer + strlen(fmt_buffer), sizeof(fmt_buffer) - strlen(fmt_buffer), "%04d-%02d-%02d %02d:%02d:%02d ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + } + + snprintf(fmt_buffer + strlen(fmt_buffer), sizeof(fmt_buffer) - strlen(fmt_buffer), "%s\n", _fmt); + + if (enable_syslog_) { +#if defined(__APPLE__) + extern void *_os_log_default; + static void (*os_log_with_args)(void *oslog, char type, const char *format, va_list args, void *ret_addr) = 0; + if (!os_log_with_args) + os_log_with_args = (__typeof(os_log_with_args))dlsym((void *)-2, "os_log_with_args"); + // os_log_with_args(&_os_log_default, 0x10, fmt_buffer, ap, (void *)&os_log_with_args); + vsyslog(LOG_ALERT, fmt_buffer, ap); + + static int _logDescriptor = 0; + if (_logDescriptor == 0) { + _logDescriptor = socket(AF_UNIX, SOCK_DGRAM, 0); + if (_logDescriptor != -1) { + fcntl(_logDescriptor, F_SETFD, FD_CLOEXEC); + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, _PATH_LOG, sizeof(addr.sun_path)); + if (connect(_logDescriptor, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + close(_logDescriptor); + _logDescriptor = -1; + ERROR_LOG("Failed to connect to syslogd: %s", strerror(errno)); + } + } + } + if (_logDescriptor > 0) { + vdprintf(_logDescriptor, fmt_buffer, ap); + } +#elif defined(_POSIX_VERSION) + vsyslog(LOG_ERR, fmt_buffer, ap); +#endif + } + + if (log_file_ != nullptr) { + char buffer[0x4000] = {0}; + vsnprintf(buffer, sizeof(buffer) - 1, fmt_buffer, ap); +#if defined(USER_CXX_FILESTREAM) + log_file_stream_->write(buffer, strlen(buffer)); + log_file_stream_->flush(); +#else + fwrite(buffer, strlen(buffer), 1, log_file_stream_); + fflush(log_file_stream_); +#endif + } + + if (1 || !enable_syslog_ && log_file_ == nullptr) { +#if defined(__ANDROID__) + __android_log_vprint(ANDROID_LOG_INFO, NULL, fmt_buffer, ap); +#else + vprintf(fmt_buffer, ap); +#endif + } +} + +#pragma clang diagnostic warning "-Wformat" + +void *logger_create(const char *tag, const char *file, LogLevel level, bool enable_time_tag, bool enable_syslog) { + Logger *logger = new Logger(tag, file, level, enable_time_tag, enable_syslog); + return logger; +} + +void logger_set_options(void *logger, const char *tag, const char *file, LogLevel level, bool enable_time_tag, + bool enable_syslog) { + if (logger == nullptr) { + logger = Logger::Shared(); + } + ((Logger *)logger)->setOptions(tag, file, level, enable_time_tag, enable_syslog); +} + +void logger_log_impl(void *logger, LogLevel level, const char *fmt, ...) { + if (logger == nullptr) { + logger = Logger::Shared(); + } + va_list ap; + va_start(ap, fmt); + ((Logger *)logger)->logv(level, fmt, ap); + va_end(ap); +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/logging/logging/check_logging.h b/app/src/main/cpp/Dobby/external/logging/logging/check_logging.h new file mode 100644 index 0000000..571c2c7 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/logging/check_logging.h @@ -0,0 +1,87 @@ + +#ifndef CHECK_LOGGING_H_ +#define CHECK_LOGGING_H_ + +#include "logging.h" + +#define CHECK_WITH_MSG(condition, message) \ + do { \ + if (!(condition)) { \ + FATAL_LOG("Check failed: %s.\n", message); \ + } \ + } while (0) +#define CHECK(condition) CHECK_WITH_MSG(condition, #condition) + +#ifdef LOGGING_DEBUG + +#define DCHECK_WITH_MSG(condition, message) \ + do { \ + if (!(condition)) { \ + FATAL_LOG("%s", message); \ + } \ + } while (0) +#define DCHECK(condition) DCHECK_WITH_MSG(condition, #condition) + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. +#define CHECK_OP(name, op, lhs, rhs) \ + do { \ + if (!(lhs op rhs)) { \ + FATAL_LOG(" Check failed: %s.\n", #lhs " " #op " " #rhs); \ + } \ + } while (0) + +#define DCHECK_OP(name, op, lhs, rhs) \ + do { \ + if (!((lhs)op(rhs))) { \ + FATAL_LOG("%s", ""); \ + } \ + } while (0) + +#else + +// Make all CHECK functions discard their log strings to reduce code +// bloat for official release builds. +#define CHECK_OP(name, op, lhs, rhs) \ + do { \ + bool _cond = lhs op rhs; \ + CHECK_WITH_MSG(_cond, #lhs " " #op " " #rhs "\n"); \ + } while (0) + +#define DCHECK_WITH_MSG(condition, msg) void(0); + +#endif + +#define CHECK_EQ(lhs, rhs) CHECK_OP(EQ, ==, lhs, rhs) +#define CHECK_NE(lhs, rhs) CHECK_OP(NE, !=, lhs, rhs) +#define CHECK_LE(lhs, rhs) CHECK_OP(LE, <=, lhs, rhs) +#define CHECK_LT(lhs, rhs) CHECK_OP(LT, <, lhs, rhs) +#define CHECK_GE(lhs, rhs) CHECK_OP(GE, >=, lhs, rhs) +#define CHECK_GT(lhs, rhs) CHECK_OP(GT, >, lhs, rhs) +#define CHECK_NULL(val) CHECK((val) == NULL) +#define CHECK_NOT_NULL(val) CHECK((val) != NULL) + +#ifdef LOGGING_DEBUG +#define DCHECK_EQ(lhs, rhs) DCHECK_OP(EQ, ==, lhs, rhs) +#define DCHECK_NE(lhs, rhs) DCHECK_OP(NE, !=, lhs, rhs) +#define DCHECK_GT(lhs, rhs) DCHECK_OP(GT, >, lhs, rhs) +#define DCHECK_GE(lhs, rhs) DCHECK_OP(GE, >=, lhs, rhs) +#define DCHECK_LT(lhs, rhs) DCHECK_OP(LT, <, lhs, rhs) +#define DCHECK_LE(lhs, rhs) DCHECK_OP(LE, <=, lhs, rhs) +#define DCHECK_NULL(val) DCHECK((val) == nullptr) +#define DCHECK_NOT_NULL(val) DCHECK((val) != nullptr) +#define DCHECK_IMPLIES(lhs, rhs) DCHECK_WITH_MSG(!(lhs) || (rhs), #lhs " implies " #rhs) +#else +#define DCHECK(condition) ((void)0) +#define DCHECK_EQ(v1, v2) ((void)0) +#define DCHECK_NE(v1, v2) ((void)0) +#define DCHECK_GT(v1, v2) ((void)0) +#define DCHECK_GE(v1, v2) ((void)0) +#define DCHECK_LT(v1, v2) ((void)0) +#define DCHECK_LE(v1, v2) ((void)0) +#define DCHECK_NULL(val) ((void)0) +#define DCHECK_NOT_NULL(val) ((void)0) +#define DCHECK_IMPLIES(v1, v2) ((void)0) +#endif + +#endif diff --git a/app/src/main/cpp/Dobby/external/logging/logging/logging.h b/app/src/main/cpp/Dobby/external/logging/logging/logging.h new file mode 100644 index 0000000..d53b7ac --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/logging/logging.h @@ -0,0 +1,202 @@ +#pragma once + +#include +#include +#include +#include + +#define LOG_TAG NULL + +typedef enum { + LOG_LEVEL_DEBUG = 0, + LOG_LEVEL_INFO = 1, + LOG_LEVEL_WARN = 2, + LOG_LEVEL_ERROR = 3, + LOG_LEVEL_FATAL = 4 +} LogLevel; + +#ifdef __cplusplus + +#if defined(USE_CXX_FILESTREAM) +#include +#endif + +class Logger { +public: + LogLevel log_level_; + + const char *log_tag_; + + const char *log_file_; +#if defined(USE_CXX_FILESTREAM) + std::fstream *log_file_stream_; +#else + FILE *log_file_stream_; +#endif + + bool enable_time_tag_; + bool enable_syslog_; + + static Logger *g_logger; + static Logger *Shared() { + if (g_logger == nullptr) { + g_logger = new Logger(); + } + return g_logger; + } + + Logger() { + log_tag_ = nullptr; + log_file_ = nullptr; + log_level_ = LOG_LEVEL_DEBUG; + enable_time_tag_ = false; + enable_syslog_ = false; + } + + Logger(const char *tag, const char *file, LogLevel level, bool enable_time_tag, bool enable_syslog) { + setTag(tag); + setLogFile(file); + setLogLevel(level); + enable_time_tag_ = enable_time_tag; + enable_syslog_ = enable_syslog; + } + + void setOptions(const char *tag, const char *file, LogLevel level, bool enable_time_tag, bool enable_syslog) { + if (tag) + setTag(tag); + if (file) + setLogFile(file); + setLogLevel(level); + enable_time_tag_ = enable_time_tag; + enable_syslog_ = enable_syslog; + } + + void setTag(const char *tag) { + log_tag_ = tag; + } + + void setLogFile(const char *file) { + log_file_ = file; +#if defined(USE_CXX_FILESTREAM) + log_file_stream_ = new std::fstream(); + log_file_stream_->open(log_file_, std::ios::out | std::ios::app); +#else + log_file_stream_ = fopen(log_file_, "a"); +#endif + } + + void setLogLevel(LogLevel level) { + log_level_ = level; + } + + void enableTimeTag() { + enable_time_tag_ = true; + } + + void enableSyslog() { + enable_syslog_ = true; + } + + void logv(LogLevel level, const char *fmt, va_list ap); + + void log(LogLevel level, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + logv(level, fmt, ap); + va_end(ap); + } + + void debug(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + logv(LOG_LEVEL_DEBUG, fmt, ap); + va_end(ap); + } + + void info(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + logv(LOG_LEVEL_INFO, fmt, ap); + va_end(ap); + } + + void warn(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + logv(LOG_LEVEL_WARN, fmt, ap); + va_end(ap); + } + + void error(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + logv(LOG_LEVEL_ERROR, fmt, ap); + va_end(ap); + } + + void fatal(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + logv(LOG_LEVEL_FATAL, fmt, ap); + va_end(ap); + } +}; + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(DOBBY_LOGGING_DISABLE) +#define LOG_FUNCTION_IMPL(...) +#else +#if !defined(LOG_FUNCTION_IMPL) +#define LOG_FUNCTION_IMPL logger_log_impl +#endif +#endif + +void *logger_create(const char *tag, const char *file, LogLevel level, bool enable_time_tag, bool enable_syslog); +void logger_set_options(void *logger, const char *tag, const char *file, LogLevel level, bool enable_time_tag, + bool enable_syslog); +void logger_log_impl(void *logger, LogLevel level, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#define LOG(level, fmt, ...) \ + do { \ + if (LOG_TAG) \ + LOG_FUNCTION_IMPL(NULL, level, "[%s] " fmt, LOG_TAG, ##__VA_ARGS__); \ + else \ + LOG_FUNCTION_IMPL(NULL, level, fmt, ##__VA_ARGS__); \ + } while (0) + +#define DEBUG_LOG(fmt, ...) \ + do { \ + LOG(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__); \ + } while (0) + +#define INFO_LOG(fmt, ...) \ + do { \ + LOG(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__); \ + } while (0) + +#define WARN_LOG(fmt, ...) \ + do { \ + LOG(LOG_LEVEL_WARN, fmt, ##__VA_ARGS__); \ + } while (0) + +#define ERROR_LOG(fmt, ...) \ + do { \ + LOG(LOG_LEVEL_ERROR, "[!] [%s:%d:%s]" fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + } while (0) + +#define FATAL_LOG(fmt, ...) \ + do { \ + LOG(LOG_LEVEL_FATAL, "[!] [%s:%d:%s]" fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + } while (0) + +#define UNIMPLEMENTED() FATAL_LOG("%s\n", "unimplemented code!!!") +#define UNREACHABLE() FATAL_LOG("%s\n", "unreachable code!!!") \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/logging/logging_kern.cc b/app/src/main/cpp/Dobby/external/logging/logging_kern.cc new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/cpp/Dobby/external/logging/priv_headers/_simple.h b/app/src/main/cpp/Dobby/external/logging/priv_headers/_simple.h new file mode 100644 index 0000000..2ae8384 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/priv_headers/_simple.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2006, 2010, 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _SYSTEM_SIMPLE_H_ +#define _SYSTEM_SIMPLE_H_ + +#include +#include + +#include + +typedef void *_SIMPLE_STRING; +typedef const char *_esc_func(unsigned char); + +__BEGIN_DECLS +/* + * A simplified vfprintf variant. The format string is interpreted with + * arguments from the va_list, and the results are written to the given + * file descriptor. + */ +void _simple_vdprintf(int __fd, const char *__fmt, va_list __ap) __printflike(2, 0); + +/* + * A simplified fprintf variant. The format string is interpreted with + * arguments from the variable argument list, and the results are written + * to the given file descriptor. + */ +void _simple_dprintf(int __fd, const char *__fmt, ...) __printflike(2, 3); + +/* + * A simplified string allocate routine. Pass the opaque pointer to structure + * to _simple_*sprintf() routines. Use _simple_string() to retrieve the + * current string (the string is guaranteed to be null terminated only on + * the call to _simple_string()). Use _simple_sfree() to free the structure + * and string memory. + */ +_SIMPLE_STRING _simple_salloc(void); + +/* + * The format string is interpreted with arguments from the va_list, and the + * results are appended to the string maintained by the opaque structure, as + * returned by a previous call to _simple_salloc(). Non-zero is returned on + * out-of-memory error. + */ +int _simple_vsprintf(_SIMPLE_STRING __b, const char *__fmt, va_list __ap) __printflike(2, 0); + +/* + * The format string is interpreted with arguments from the variable argument + * list, and the results are appended to the string maintained by the opaque + * structure, as returned by a previous call to _simple_salloc(). Non-zero is + * returned on out-of-memory error. + */ +int _simple_sprintf(_SIMPLE_STRING __b, const char *__fmt, ...) __printflike(2, 3); + +/* + * Like _simple_vsprintf(), except __esc is a function to call on each + * character; the function returns NULL if the character should be passed + * as is, otherwise, the returned character string is used instead. + */ +int _simple_vesprintf(_SIMPLE_STRING __b, _esc_func __esc, const char *__fmt, va_list __ap) __printflike(3, 0); + +/* + * Like _simple_sprintf(), except __esc is a function to call on each + * character; the function returns NULL if the character should be passed + * as is, otherwise, the returned character string is used instead. + */ +int _simple_esprintf(_SIMPLE_STRING __b, _esc_func __esc, const char *__fmt, ...) __printflike(3, 4); + +/* + * Return the null terminated string from the opaque structure, as returned + * by a previous call to _simple_salloc(). + */ +char *_simple_string(_SIMPLE_STRING __b); + +/* + * Reposition the pointer to the first null in the buffer. After a call to + * _simple_string, the buffer can be modified, and shrunk. + */ +void _simple_sresize(_SIMPLE_STRING __b); + +/* + * Append the null-terminated string to the string associated with the opaque + * structure. Non-zero is returned on out-of-memory error. + */ +int _simple_sappend(_SIMPLE_STRING __b, const char *__str); + +/* + * Like _simple_sappend(), except __esc is a function to call on each + * character; the function returns NULL if the character should be passed + * as is, otherwise, the returned character string is used instead. + */ +int _simple_esappend(_SIMPLE_STRING __b, _esc_func __esc, const char *__str); + +/* + * Write the string associated with the opaque structure to the file descriptor. + */ +void _simple_put(_SIMPLE_STRING __b, int __fd); + +/* + * Write the string associated with the opaque structure and a trailing newline, + * to the file descriptor. + */ +void _simple_putline(_SIMPLE_STRING __b, int __fd); + +/* + * Free the opaque structure, and the associated string. + */ +void _simple_sfree(_SIMPLE_STRING __b); + +/* + * Simplified ASL log interface; does not use malloc. Unfortunately, this + * requires knowledge of the format used by ASL. + */ +#ifndef ASL_LEVEL_DEBUG +#define ASL_LEVEL_EMERG 0 +#define ASL_LEVEL_ALERT 1 +#define ASL_LEVEL_CRIT 2 +#define ASL_LEVEL_ERR 3 +#define ASL_LEVEL_WARNING 4 +#define ASL_LEVEL_NOTICE 5 +#define ASL_LEVEL_INFO 6 +#define ASL_LEVEL_DEBUG 7 +#endif + +void _simple_asl_log(int __level, const char *__facility, const char *__message); +void _simple_asl_log_prog(int level, const char *facility, const char *message, const char *progname); + +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0) +_SIMPLE_STRING _simple_asl_msg_new(void); + +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0) +void _simple_asl_msg_set(_SIMPLE_STRING __b, const char *__key, const char *__val); + +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0) +void _simple_asl_send(_SIMPLE_STRING __b); + +__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0) +const char *_simple_getenv(const char *envp[], const char *var); + +__END_DECLS + +#endif /* _SYSTEM_SIMPLE_H_ */ diff --git a/app/src/main/cpp/Dobby/external/osbase/CMakeLists.txt b/app/src/main/cpp/Dobby/external/osbase/CMakeLists.txt new file mode 100644 index 0000000..ef7ce40 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/osbase/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(osbase STATIC + ${PROJECT_SOURCE_DIR}/source/Backend/UserMode/UnifiedInterface/platform-posix.cc + ) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/include/dobby.h b/app/src/main/cpp/Dobby/include/dobby.h new file mode 100644 index 0000000..745b798 --- /dev/null +++ b/app/src/main/cpp/Dobby/include/dobby.h @@ -0,0 +1,152 @@ +#ifndef dobby_h +#define dobby_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef uintptr_t addr_t; +typedef uint32_t addr32_t; +typedef uint64_t addr64_t; + +typedef void *dobby_dummy_func_t; +typedef void *asm_func_t; + +#if defined(__arm__) +typedef struct { + uint32_t dummy_0; + uint32_t dummy_1; + + uint32_t dummy_2; + uint32_t sp; + + union { + uint32_t r[13]; + struct { + uint32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12; + } regs; + } general; + + uint32_t lr; +} DobbyRegisterContext; +#elif defined(__arm64__) || defined(__aarch64__) +#define ARM64_TMP_REG_NDX_0 17 + +typedef union _FPReg { + __int128_t q; + struct { + double d1; + double d2; + } d; + struct { + float f1; + float f2; + float f3; + float f4; + } f; +} FPReg; + +// register context +typedef struct { + uint64_t dmmpy_0; // dummy placeholder + uint64_t sp; + + uint64_t dmmpy_1; // dummy placeholder + union { + uint64_t x[29]; + struct { + uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, + x23, x24, x25, x26, x27, x28; + } regs; + } general; + + uint64_t fp; + uint64_t lr; + + union { + FPReg q[32]; + struct { + FPReg q0, q1, q2, q3, q4, q5, q6, q7; + // [!!! READ ME !!!] + // for Arm64, can't access q8 - q31, unless you enable full floating-point register pack + FPReg q8, q9, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21, q22, q23, q24, q25, q26, q27, q28, q29, + q30, q31; + } regs; + } floating; +} DobbyRegisterContext; +#elif defined(_M_IX86) || defined(__i386__) +typedef struct _RegisterContext { + uint32_t dummy_0; + uint32_t esp; + + uint32_t dummy_1; + uint32_t flags; + + union { + struct { + uint32_t eax, ebx, ecx, edx, ebp, esp, edi, esi; + } regs; + } general; + +} DobbyRegisterContext; +#elif defined(_M_X64) || defined(__x86_64__) +typedef struct { + uint64_t dummy_0; + uint64_t rsp; + + union { + struct { + uint64_t rax, rbx, rcx, rdx, rbp, rsp, rdi, rsi, r8, r9, r10, r11, r12, r13, r14, r15; + } regs; + } general; + + uint64_t dummy_1; + uint64_t flags; +} DobbyRegisterContext; +#endif + +#define install_hook_name(name, fn_ret_t, fn_args_t...) \ + static fn_ret_t fake_##name(fn_args_t); \ + static fn_ret_t (*orig_##name)(fn_args_t); \ + /* __attribute__((constructor)) */ static void install_hook_##name(void *sym_addr) { \ + DobbyHook(sym_addr, (dobby_dummy_func_t)fake_##name, (dobby_dummy_func_t *)&orig_##name); \ + return; \ + } \ + fn_ret_t fake_##name(fn_args_t) + +// memory code patch +int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size); + +// function inline hook +int DobbyHook(void *address, dobby_dummy_func_t replace_func, dobby_dummy_func_t *origin_func); + +// dynamic binary instruction instrument +// for Arm64, can't access q8 - q31, unless enable full floating-point register pack +typedef void (*dobby_instrument_callback_t)(void *address, DobbyRegisterContext *ctx); +int DobbyInstrument(void *address, dobby_instrument_callback_t pre_handler); + +// destroy and restore code patch +int DobbyDestroy(void *address); + +const char *DobbyGetVersion(); + +// symbol resolver +void *DobbySymbolResolver(const char *image_name, const char *symbol_name); + +// import table replace +int DobbyImportTableReplace(char *image_name, char *symbol_name, dobby_dummy_func_t fake_func, + dobby_dummy_func_t *orig_func); + +// for arm, Arm64, try use b xxx instead of ldr absolute indirect branch +// for x86, x64, always use absolute indirect jump +void dobby_enable_near_branch_trampoline(); +void dobby_disable_near_branch_trampoline(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/Dobby/scripts/Dockerfile b/app/src/main/cpp/Dobby/scripts/Dockerfile new file mode 100644 index 0000000..149f448 --- /dev/null +++ b/app/src/main/cpp/Dobby/scripts/Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:focal + +ARG DEBIAN_FRONTEND='noninteractive' + +RUN apt-key adv --keyserver 'keyserver.ubuntu.com' --recv-key 'C99B11DEB97541F0' && + apt-add-repository -y -u 'https://cli.github.com/packages' && + apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' + +ADD setup_linux_cross_compile.sh /root/setup_linux_cross_compile.sh +RUN sh /root/setup_linux_cross_compile.sh diff --git a/app/src/main/cpp/Dobby/scripts/platform_builder.py b/app/src/main/cpp/Dobby/scripts/platform_builder.py new file mode 100644 index 0000000..b31b4c9 --- /dev/null +++ b/app/src/main/cpp/Dobby/scripts/platform_builder.py @@ -0,0 +1,242 @@ +import os +import pipes +import re +import shutil +import subprocess +import sys +import logging + +import argparse + +platforms = { + "macos": ["x86_64", "arm64", "arm64e"], + "iphoneos": ["arm64", "arm64e"], + "linux": ["x86", "x86_64", "arm", "arm64"], + "android": ["x86", "x86_64", "armeabi-v7a", "arm64-v8a"] +} + + +class PlatformBuilder(object): + cmake_args = list() + cmake_build_type = "Release" + cmake_build_verbose = False + cmake_build_dir = "" + + library_build_type = "static" + + project_dir = "" + output_dir = "" + + shared_output_name = "" + static_output_name = "" + + platform = "" + arch = "" + + def __init__(self, project_dir, library_build_type, platform, arch): + self.project_dir = project_dir + self.library_build_type = library_build_type + self.platform = platform + self.arch = arch + + self.cmake_build_dir = f"{self.project_dir}/build/cmake-build-{platform}-{arch}" + self.output_dir = f"{self.project_dir}/build/{platform}/{arch}" + + self.cmake = "cmake" if PlatformBuilder.cmake_dir is None else f"{PlatformBuilder.cmake_dir}/bin/cmake" + self.clang = "clang" if PlatformBuilder.llvm_dir is None else f"{PlatformBuilder.llvm_dir}/bin/clang" + self.clangxx = "clang++" if PlatformBuilder.llvm_dir is None else f"{PlatformBuilder.llvm_dir}/bin/clang++" + + self.setup_common_args() + + def cmake_generate_build_system(self): + cmake_cmd_options = ["-S {}".format(self.project_dir), "-B {}".format(self.cmake_build_dir)] + cmd = [f"{self.cmake}"] + cmake_cmd_options + self.cmake_args + # subprocess.run(cmd, check=True) + cmd_line = " ".join(cmd) + print(cmd_line) + os.system(cmd_line) + + def setup_common_args(self): + self.cmake_args += [f"-DCMAKE_C_COMPILER={self.clang}", f"-DCMAKE_CXX_COMPILER={self.clangxx}"] + + self.cmake_args += ["-DCMAKE_BUILD_TYPE={}".format(self.cmake_build_type)] + + if self.library_build_type == "shared": + pass + # self.cmake_args += ["-DDOBBY_GENERATE_SHARED=ON"] + elif self.library_build_type == "static": + pass + # self.cmake_args += ["-DDOBBY_GENERATE_SHARED=OFF"] + + def build(self): + subprocess.run(["mkdir", "-p", "{}".format(self.output_dir)], check=True) + self.cmake_generate_build_system() + + subprocess.run("cmake --build . --clean-first --target dobby --target dobby_static -- -j8", cwd=self.cmake_build_dir, shell=True, check=True) + + os.system(f"mkdir -p {self.output_dir}") + os.system(f"cp {self.cmake_build_dir}/{self.shared_output_name} {self.output_dir}") + os.system(f"cp {self.cmake_build_dir}/{self.static_output_name} {self.output_dir}") + + +class WindowsPlatformBuilder(PlatformBuilder): + + def __init__(self, project_dir, library_build_type, platform, arch): + super().__init__(project_dir, library_build_type, platform, arch) + + if self.library_build_type == "shared": + self.output_name = "libdobby.dll" + else: + self.output_name = "libdobby.lib" + + triples = { + "x86": "i686-pc-windows-msvc", + "x64": "x86_64-pc-windows-msvc", + # "arm": "arm-pc-windows-msvc", + "arm64": "arm64-pc-windows-msvc", + } + + # self.cmake_args += ["--target {}".format(triples[arch])] + self.cmake_args += [ + "-DCMAKE_SYSTEM_PROCESSOR={}".format(arch), + ] + + +class LinuxPlatformBuilder(PlatformBuilder): + + def __init__(self, project_dir, library_build_type, arch): + super().__init__(project_dir, library_build_type, "linux", arch) + + self.shared_output_name = "libdobby.so" + self.static_output_name = "libdobby.a" + + targets = { + "x86": "i686-linux-gnu", + "x86_64": "x86_64-linux-gnu", + "arm": "arm-linux-gnueabi", + "aarch64": "aarch64-linux-gnu", + } + + # self.cmake_args += ["--target={}".format(targets[arch])] + self.cmake_args += [ + "-DCMAKE_SYSTEM_NAME=Linux", + "-DCMAKE_SYSTEM_PROCESSOR={}".format(arch), + ] + + +class AndroidPlatformBuilder(PlatformBuilder): + + def __init__(self, android_nkd_dir, project_dir, library_build_type, arch): + super().__init__(project_dir, library_build_type, "android", arch) + + self.shared_output_name = "libdobby.so" + self.static_output_name = "libdobby.a" + + android_api_level = 21 + if arch == "armeabi-v7a" or arch == "x86": + android_api_level = 19 + + self.cmake_args += [ + "-DCMAKE_SYSTEM_NAME=Android", f"-DCMAKE_ANDROID_NDK={android_nkd_dir}", f"-DCMAKE_ANDROID_ARCH_ABI={arch}", + f"-DCMAKE_SYSTEM_VERSION={android_api_level}" + ] + + +class DarwinPlatformBuilder(PlatformBuilder): + + def __init__(self, project_dir, library_build_type, platform, arch): + super().__init__(project_dir, library_build_type, platform, arch) + + self.cmake_args += [ + "-DCMAKE_OSX_ARCHITECTURES={}".format(arch), + "-DCMAKE_SYSTEM_PROCESSOR={}".format(arch), + ] + + if platform == "macos": + self.cmake_args += ["-DCMAKE_SYSTEM_NAME=Darwin"] + elif platform == "iphoneos": + self.cmake_args += ["-DCMAKE_SYSTEM_NAME=iOS", "-DCMAKE_OSX_DEPLOYMENT_TARGET=9.3"] + + self.shared_output_name = "libdobby.dylib" + self.static_output_name = "libdobby.a" + + @classmethod + def lipo_create_fat(cls, project_dir, platform, output_name): + files = list() + archs = platforms[platform] + for arch in archs: + file = f"{project_dir}/build/{platform}/{arch}/{output_name}" + files.append(file) + + fat_output_dir = f"{project_dir}/build/{platform}/universal" + subprocess.run(["mkdir", "-p", "{}".format(fat_output_dir)], check=True) + cmd = ["lipo", "-create"] + files + ["-output", f"{fat_output_dir}/{output_name}"] + subprocess.run(cmd, check=True) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--platform", type=str, required=True) + parser.add_argument("--arch", type=str, required=True) + parser.add_argument("--library_build_type", type=str, default="static") + parser.add_argument("--android_ndk_dir", type=str) + parser.add_argument("--cmake_dir", type=str) + parser.add_argument("--llvm_dir", type=str) + args = parser.parse_args() + + logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") + + platform = args.platform + arch = args.arch + library_build_type = args.library_build_type + + PlatformBuilder.cmake_dir = args.cmake_dir + PlatformBuilder.llvm_dir = args.llvm_dir + + project_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + logging.info("project dir: {}".format(project_dir)) + if not os.path.exists(f"{project_dir}/CMakeLists.txt"): + logging.error("Please run this script in Dobby project root directory") + sys.exit(1) + + if platform not in platforms: + logging.error("invalid platform {}".format(platform)) + sys.exit(-1) + + if arch != "all" and arch not in platforms[platform]: + logging.error("invalid arch {} for platform {}".format(arch, platform)) + sys.exit(-1) + + if platform == "android": + if args.android_ndk_dir is None: + logging.error("ndk dir is required for android platform") + sys.exit(-1) + + archs = list() + if arch == "all": + archs = platforms[platform] + else: + archs.append(arch) + logging.info("build platform: {}, archs: {}".format(platform, archs)) + + builder: PlatformBuilder = None + for arch_ in archs: + if platform == "macos": + builder = DarwinPlatformBuilder(project_dir, library_build_type, platform, arch_) + elif platform == "iphoneos": + builder = DarwinPlatformBuilder(project_dir, library_build_type, platform, arch_) + elif platform == "android": + builder = AndroidPlatformBuilder(args.android_ndk_dir, project_dir, library_build_type, arch_) + elif platform == "linux": + builder = LinuxPlatformBuilder(project_dir, library_build_type, arch_) + else: + logging.error("invalid platform {}".format(platform)) + sys.exit(-1) + logging.info( + f"build platform: {platform}, arch: {arch_}, cmake_build_dir: {builder.cmake_build_dir}, output_dir: {builder.output_dir}" + ) + builder.build() + + if platform in ["iphoneos", "macos"] and arch == "all": + DarwinPlatformBuilder.lipo_create_fat(project_dir, platform, builder.shared_output_name) + DarwinPlatformBuilder.lipo_create_fat(project_dir, platform, builder.static_output_name) diff --git a/app/src/main/cpp/Dobby/scripts/setup_linux_cross_compile.sh b/app/src/main/cpp/Dobby/scripts/setup_linux_cross_compile.sh new file mode 100644 index 0000000..e2034b6 --- /dev/null +++ b/app/src/main/cpp/Dobby/scripts/setup_linux_cross_compile.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +set -x +set -e + +# sudo dpkg --add-architecture armhf +# sudo dpkg --add-architecture i386 +# sudo dpkg --add-architecture arm64 +# sudo apt-get -y update +# sudo apt-get -y dist-upgrade +# sudo apt-get -y install git build-essential libssl-dev pkg-config unzip gcc-multilib +# sudo apt-get -y install libc6-armhf-cross libc6-dev-armhf-cross gcc-arm-linux-gnueabihf libssl-dev:armhf +# sudo apt-get -y install libc6-i386-cross libc6-dev-i386-cross gcc-i686-linux-gnu libssl-dev:i386 +# sudo apt-get -y install libc6-arm64-cross libc6-dev-arm64-cross gcc-aarch64-linux-gnu libssl-dev:arm64 + +sudo apt-get -y update +sudo apt-get -y install aptitude +sudo apt-get -f -y install \ + apt-utils \ + binutils \ + build-essential \ + curl \ + wget \ + unzip \ + gcc-multilib \ + g++-multilib \ + make \ + zsh + +sudo apt-get -f -y install gcc g++ libc6-dev +sudo apt-get -f -y install gcc-i686-linux-gnu g++-i686-linux-gnu +sudo apt-get -f -y install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf +sudo apt-get -f -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + +mkdir -p ~/opt + +cd ~/opt +CMAKE_VERSION=3.25.2 +CMAKE_DOWNLOAD_PACKAGE=cmake-$CMAKE_VERSION-linux-x86_64 +wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/$CMAKE_DOWNLOAD_PACKAGE.tar.gz && + tar -zxf $CMAKE_DOWNLOAD_PACKAGE.tar.gz >/dev/null && + mv $CMAKE_DOWNLOAD_PACKAGE cmake-$CMAKE_VERSION +CMAKE_HOME=~/opt/cmake-$CMAKE_VERSION + +cd ~/opt +LLVM_VERSION=15.0.6 +LLVM_DOWNLOAD_PACKAGE=clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-18.04 +wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/$LLVM_DOWNLOAD_PACKAGE.tar.xz && + tar -xf $LLVM_DOWNLOAD_PACKAGE.tar.xz >/dev/null && + mv $LLVM_DOWNLOAD_PACKAGE llvm-$LLVM_VERSION +LLVM_HOME=~/opt/llvm-$LLVM_VERSION + +cd ~/opt +NDK_VERSION=r25b +NDK_DOWNLOAD_PACKAGE=android-ndk-$NDK_VERSION-linux +NDK_DOWNLOAD_UNZIP_PACKAGE=android-ndk-$NDK_VERSION +wget https://dl.google.com/android/repository/$NDK_DOWNLOAD_PACKAGE.zip && + unzip -q $NDK_DOWNLOAD_PACKAGE.zip >/dev/null && + mv $NDK_DOWNLOAD_UNZIP_PACKAGE ndk-$NDK_VERSION && + rm $NDK_DOWNLOAD_PACKAGE.zip +ANDROID_NDK_HOME=~/opt/android-ndk-$NDK_VERSION diff --git a/app/src/main/cpp/Dobby/scripts/setup_macos_cross_compile.sh b/app/src/main/cpp/Dobby/scripts/setup_macos_cross_compile.sh new file mode 100644 index 0000000..fb904b0 --- /dev/null +++ b/app/src/main/cpp/Dobby/scripts/setup_macos_cross_compile.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -x +set -e + +mkdir -p ~/opt + +cd ~/opt +CMAKE_VERSION=3.25.2 +CMAKE_DOWNLOAD_PACKAGE=cmake-$CMAKE_VERSION-macos-universal +wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/$CMAKE_DOWNLOAD_PACKAGE.tar.gz && + tar -zxf $CMAKE_DOWNLOAD_PACKAGE.tar.gz >/dev/null && + mv $CMAKE_DOWNLOAD_PACKAGE cmake-$CMAKE_VERSION +CMAKE_HOME=~/opt/cmake-$CMAKE_VERSION + +cd ~/opt +LLVM_VERSION=15.0.6 +LLVM_DOWNLOAD_PACKAGE=clang+llvm-$LLVM_VERSION-x86_64-apple-darwin +wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/$LLVM_DOWNLOAD_PACKAGE.tar.xz && + tar -xf $LLVM_DOWNLOAD_PACKAGE.tar.xz >/dev/null && + mv $LLVM_DOWNLOAD_PACKAGE llvm-$LLVM_VERSION +LLVM_HOME=~/opt/llvm-$LLVM_VERSION diff --git a/app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/clear-cache-tool-all.c b/app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/clear-cache-tool-all.c new file mode 100644 index 0000000..495257f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/clear-cache-tool-all.c @@ -0,0 +1,3 @@ +void ClearCache(void *start, void *end) { + return; +} diff --git a/app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/code-patch-tool-darwin.cc b/app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/code-patch-tool-darwin.cc new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/cpp/Dobby/source/Backend/KernelMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc b/app/src/main/cpp/Dobby/source/Backend/KernelMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/platform-darwin.cc b/app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/platform-darwin.cc new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c new file mode 100644 index 0000000..413ef48 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c @@ -0,0 +1,145 @@ +//===-- clear_cache.c - Implement __clear_cache ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +#if __APPLE__ +#include +#endif + +#if defined(_WIN32) +// Forward declare Win32 APIs since the GCC mode driver does not handle the +// newer SDKs as well as needed. +uint32_t FlushInstructionCache(uintptr_t hProcess, void *lpBaseAddress, uintptr_t dwSize); +uintptr_t GetCurrentProcess(void); +#endif + +// The compiler generates calls to __clear_cache() when creating +// trampoline functions on the stack for use with nested functions. +// It is expected to invalidate the instruction cache for the +// specified range. + +void _clear_cache(void *start, void *end) { +#if __i386__ || __x86_64__ || defined(_M_IX86) || defined(_M_X64) +// Intel processors have a unified instruction and data cache +// so there is nothing to do +#elif defined(_WIN32) && (defined(__arm__) || defined(__aarch64__)) + FlushInstructionCache(GetCurrentProcess(), start, end - start); +#elif defined(__arm__) && !defined(__APPLE__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + struct arm_sync_icache_args arg; + + arg.addr = (uintptr_t)start; + arg.len = (uintptr_t)end - (uintptr_t)start; + + sysarch(ARM_SYNC_ICACHE, &arg); +#elif defined(__linux__) +// We used to include asm/unistd.h for the __ARM_NR_cacheflush define, but +// it also brought many other unused defines, as well as a dependency on +// kernel headers to be installed. +// +// This value is stable at least since Linux 3.13 and should remain so for +// compatibility reasons, warranting it's re-definition here. +#define __ARM_NR_cacheflush 0x0f0002 + register int start_reg __asm("r0") = (int)(intptr_t)start; + const register int end_reg __asm("r1") = (int)(intptr_t)end; + const register int flags __asm("r2") = 0; + const register int syscall_nr __asm("r7") = __ARM_NR_cacheflush; + __asm __volatile("svc 0x0" : "=r"(start_reg) : "r"(syscall_nr), "r"(start_reg), "r"(end_reg), "r"(flags)); + assert(start_reg == 0 && "Cache flush syscall failed."); +#else + compilerrt_abort(); +#endif +#elif defined(__linux__) && defined(__mips__) + const uintptr_t start_int = (uintptr_t)start; + const uintptr_t end_int = (uintptr_t)end; + syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); +#elif defined(__mips__) && defined(__OpenBSD__) + cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE); +#elif defined(__aarch64__) && !defined(__APPLE__) + uint64_t xstart = (uint64_t)(uintptr_t)start; + uint64_t xend = (uint64_t)(uintptr_t)end; + + // Get Cache Type Info. + static uint64_t ctr_el0 = 0; + if (ctr_el0 == 0) + __asm __volatile("mrs %0, ctr_el0" : "=r"(ctr_el0)); + + // The DC and IC instructions must use 64-bit registers so we don't use + // uintptr_t in case this runs in an IPL32 environment. + uint64_t addr; + + // If CTR_EL0.IDC is set, data cache cleaning to the point of unification + // is not required for instruction to data coherence. + if (((ctr_el0 >> 28) & 0x1) == 0x0) { + const size_t dcache_line_size = 4 << ((ctr_el0 >> 16) & 15); + for (addr = xstart & ~(dcache_line_size - 1); addr < xend; addr += dcache_line_size) + __asm __volatile("dc cvau, %0" ::"r"(addr)); + } + __asm __volatile("dsb ish"); + + // If CTR_EL0.DIC is set, instruction cache invalidation to the point of + // unification is not required for instruction to data coherence. + if (((ctr_el0 >> 29) & 0x1) == 0x0) { + const size_t icache_line_size = 4 << ((ctr_el0 >> 0) & 15); + for (addr = xstart & ~(icache_line_size - 1); addr < xend; addr += icache_line_size) + __asm __volatile("ic ivau, %0" ::"r"(addr)); + __asm __volatile("dsb ish"); + } + __asm __volatile("isb sy"); +#elif defined(__powerpc64__) + const size_t line_size = 32; + const size_t len = (uintptr_t)end - (uintptr_t)start; + + const uintptr_t mask = ~(line_size - 1); + const uintptr_t start_line = ((uintptr_t)start) & mask; + const uintptr_t end_line = ((uintptr_t)start + len + line_size - 1) & mask; + + for (uintptr_t line = start_line; line < end_line; line += line_size) + __asm__ volatile("dcbf 0, %0" : : "r"(line)); + __asm__ volatile("sync"); + + for (uintptr_t line = start_line; line < end_line; line += line_size) + __asm__ volatile("icbi 0, %0" : : "r"(line)); + __asm__ volatile("isync"); +#elif defined(__sparc__) + const size_t dword_size = 8; + const size_t len = (uintptr_t)end - (uintptr_t)start; + + const uintptr_t mask = ~(dword_size - 1); + const uintptr_t start_dword = ((uintptr_t)start) & mask; + const uintptr_t end_dword = ((uintptr_t)start + len + dword_size - 1) & mask; + + for (uintptr_t dword = start_dword; dword < end_dword; dword += dword_size) + __asm__ volatile("flush %0" : : "r"(dword)); +#elif defined(__riscv) && defined(__linux__) + // See: arch/riscv/include/asm/cacheflush.h, arch/riscv/kernel/sys_riscv.c + register void *start_reg __asm("a0") = start; + const register void *end_reg __asm("a1") = end; + // "0" means that we clear cache for all threads (SYS_RISCV_FLUSH_ICACHE_ALL) + const register long flags __asm("a2") = 0; + const register long syscall_nr __asm("a7") = __NR_riscv_flush_icache; + __asm __volatile("ecall" : "=r"(start_reg) : "r"(start_reg), "r"(end_reg), "r"(flags), "r"(syscall_nr)); + assert(start_reg == 0 && "Cache flush syscall failed."); +#else +#if __APPLE__ + // On Darwin, sys_icache_invalidate() provides this functionality + sys_icache_invalidate(start, end - start); +#elif defined(__ve__) + __asm__ volatile("fencec 2"); +#else + compilerrt_abort(); +#endif +#endif +} + +void ClearCache(void *start, void *end) { + return _clear_cache(start, end); +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool/clear-cache-tool-arm-dummy.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool/clear-cache-tool-arm-dummy.cc new file mode 100644 index 0000000..abe26c4 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool/clear-cache-tool-arm-dummy.cc @@ -0,0 +1,53 @@ +#ifndef USER_MODE_CLEAR_CACHE_TOOL_H +#define USER_MODE_CLEAR_CACHE_TOOL_H + +#include "core/arch/Cpu.h" + +#include "PlatformInterface/globals.h" + +#if !HOST_OS_IOS +#include // for cache flushing. +#endif + +void CpuFeatures::FlushICache(void *startp, void *endp) { + +#if HOST_OS_IOS + // Precompilation never patches code so there should be no I cache flushes. + CpuFeatures::ClearCache(startp, endp); + +#else + + register uint32_t beg asm("r0") = reinterpret_cast(startp); + register uint32_t end asm("r1") = reinterpret_cast(endp); + register uint32_t flg asm("r2") = 0; + +#ifdef __clang__ + // This variant of the asm avoids a constant pool entry, which can be + // problematic when LTO'ing. It is also slightly shorter. + register uint32_t scno asm("r7") = __ARM_NR_cacheflush; + + asm volatile("svc 0\n" : : "r"(beg), "r"(end), "r"(flg), "r"(scno) : "memory"); +#else + // Use a different variant of the asm with GCC because some versions doesn't + // support r7 as an asm input. + asm volatile( + // This assembly works for both ARM and Thumb targets. + + // Preserve r7; it is callee-saved, and GCC uses it as a frame pointer for + // Thumb targets. + " push {r7}\n" + // r0 = beg + // r1 = end + // r2 = flags (0) + " ldr r7, =%c[scno]\n" // r7 = syscall number + " svc 0\n" + + " pop {r7}\n" + : + : "r"(beg), "r"(end), "r"(flg), [scno] "i"(__ARM_NR_cacheflush) + : "memory"); +#endif +#endif +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool/clear-cache-tool-arm64-dummy.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool/clear-cache-tool-arm64-dummy.cc new file mode 100644 index 0000000..60580a5 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/clear-cache-tool/clear-cache-tool-arm64-dummy.cc @@ -0,0 +1,103 @@ +#ifndef USER_MODE_CLEAR_CACHE_TOOL_ARM64_H +#define USER_MODE_CLEAR_CACHE_TOOL_ARM64_H + +#include "core/arch/Cpu.h" + +#include "PlatformInterface/globals.h" + +class CacheLineSizes { +public: + CacheLineSizes() { + // Copy the content of the cache type register to a core register. + __asm__ __volatile__("mrs %x[ctr], ctr_el0" // NOLINT + : [ctr] "=r"(cache_type_register_)); + } + + uint32_t icache_line_size() const { + return ExtractCacheLineSize(0); + } + uint32_t dcache_line_size() const { + return ExtractCacheLineSize(16); + } + +private: + uint32_t ExtractCacheLineSize(int cache_line_size_shift) const { + // The cache type register holds the size of cache lines in words as a + // power of two. + return 4 << ((cache_type_register_ >> cache_line_size_shift) & 0xF); + } + + uint32_t cache_type_register_; +}; + +void CpuFeatures::FlushICache(void *startp, void *endp) { + // The code below assumes user space cache operations are allowed. The goal + // of this routine is to make sure the code generated is visible to the I + // side of the CPU. + +#if HOST_OS_IOS + // Precompilation never patches code so there should be no I cache flushes. + CpuFeatures::ClearCache(startp, endp); +#else + uintptr_t start = reinterpret_cast(startp); + // Sizes will be used to generate a mask big enough to cover a pointer. + CacheLineSizes sizes; + uintptr_t dsize = sizes.dcache_line_size(); + uintptr_t isize = sizes.icache_line_size(); + // Cache line sizes are always a power of 2. + uintptr_t dstart = start & ~(dsize - 1); + uintptr_t istart = start & ~(isize - 1); + uintptr_t end = reinterpret_cast(endp); + + __asm__ __volatile__( // NOLINT + // Clean every line of the D cache containing the target data. + "0: \n\t" + // dc : Data Cache maintenance + // c : Clean + // i : Invalidate + // va : by (Virtual) Address + // c : to the point of Coherency + // See ARM DDI 0406B page B2-12 for more information. + // We would prefer to use "cvau" (clean to the point of unification) here + // but we use "civac" to work around Cortex-A53 errata 819472, 826319, + // 827319 and 824069. + "dc civac, %[dline] \n\t" + "add %[dline], %[dline], %[dsize] \n\t" + "cmp %[dline], %[end] \n\t" + "b.lt 0b \n\t" + // Barrier to make sure the effect of the code above is visible to the rest + // of the world. + // dsb : Data Synchronisation Barrier + // ish : Inner SHareable domain + // The point of unification for an Inner Shareable shareability domain is + // the point by which the instruction and data caches of all the processors + // in that Inner Shareable shareability domain are guaranteed to see the + // same copy of a memory location. See ARM DDI 0406B page B2-12 for more + // information. + "dsb ish \n\t" + // Invalidate every line of the I cache containing the target data. + "1: \n\t" + // ic : instruction cache maintenance + // i : invalidate + // va : by address + // u : to the point of unification + "ic ivau, %[iline] \n\t" + "add %[iline], %[iline], %[isize] \n\t" + "cmp %[iline], %[end] \n\t" + "b.lt 1b \n\t" + // Barrier to make sure the effect of the code above is visible to the rest + // of the world. + "dsb ish \n\t" + // Barrier to ensure any prefetching which happened before this code is + // discarded. + // isb : Instruction Synchronisation Barrier + "isb \n\t" + : [dline] "+r"(dstart), [iline] "+r"(istart) + : [dsize] "r"(dsize), [isize] "r"(isize), [end] "r"(end) + // This code does not write to memory but without the dependency gcc might + // move this code before the code is generated. + : "cc", "memory"); // NOLINT +#endif +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-darwin.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-darwin.cc new file mode 100644 index 0000000..f277d72 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-darwin.cc @@ -0,0 +1,172 @@ +#include "dobby/dobby_internal.h" + +#include "PlatformUnifiedInterface/ExecMemory/ClearCacheTool.h" + +#include + +#include +#include "UnifiedInterface/platform-darwin/mach_vm.h" + +#if defined(__APPLE__) +#include +#include +#endif + +#define KERN_RETURN_ERROR(kr, failure) \ + do { \ + if (kr != KERN_SUCCESS) { \ + ERROR_LOG("mach error: %s", mach_error_string(kr)); \ + return failure; \ + } \ + } while (0); + +#include + +#if defined(TARGET_ARCH_ARM64) +#define SYS_mprotect 74 +int mprotect_impl(void *addr, size_t len, int prot) { + int ret = 0; + __asm__ __volatile__("mov x16, %[_SYS_mprotect]\n" + "svc 0x80\n" + "mov %w[_ret], w0\n" + "add %w[_ret], %w[_ret], #0x0\n" + : [_ret] "=r"(ret) + : [_SYS_mprotect] "n"(SYS_mprotect) + :); + return ret; +} +#endif + +PUBLIC int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size) { + if (address == nullptr || buffer == nullptr || buffer_size == 0) { + ERROR_LOG("invalid argument"); + return -1; + } + + size_t page_size = PAGE_SIZE; + addr_t patch_page = ALIGN_FLOOR(address, page_size); + + // cross over page + if ((addr_t)address + buffer_size > patch_page + page_size) { + void *address_a = address; + uint8_t *buffer_a = buffer; + uint32_t buffer_size_a = (patch_page + page_size - (addr_t)address); + auto ret = DobbyCodePatch(address_a, buffer_a, buffer_size_a); + if (ret == -1) { + return ret; + } + + void *address_b = (void *)((addr_t)address + buffer_size_a); + uint8_t *buffer_b = buffer + buffer_size_a; + uint32_t buffer_size_b = buffer_size - buffer_size_a; + ret = DobbyCodePatch(address_b, buffer_b, buffer_size_b); + return ret; + } + + addr_t remap_dest_page = patch_page; + mach_vm_address_t remap_dummy_page = 0; + + auto self_task = mach_task_self(); + kern_return_t kr; + + int orig_prot = 0; + int orig_max_prot = 0; + int share_mode = 0; + int is_enable_remap = -1; + if (is_enable_remap == -1) { + auto get_region_info = [&](addr_t region_start) -> void { + vm_region_submap_info_64 region_submap_info; + mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; + mach_vm_address_t addr = region_start; + mach_vm_size_t size = 0; + natural_t depth = 0; + while (1) { + kr = mach_vm_region_recurse(mach_task_self(), (mach_vm_address_t *)&addr, (mach_vm_size_t *)&size, &depth, + (vm_region_recurse_info_t)®ion_submap_info, &count); + if (region_submap_info.is_submap) { + depth++; + } else { + orig_prot = region_submap_info.protection; + orig_max_prot = region_submap_info.max_protection; + share_mode = region_submap_info.share_mode; + return; + } + } + }; + get_region_info(remap_dest_page); + if (orig_max_prot != 5 && share_mode != 2) { + is_enable_remap = 1; + } else { + is_enable_remap = 0; + DEBUG_LOG("code patch %p won't use remap", address); + } + } + if (is_enable_remap == 1) { + addr_t remap_dummy_page = 0; + { + kr = mach_vm_allocate(self_task, (mach_vm_address_t *)&remap_dummy_page, page_size, VM_FLAGS_ANYWHERE); + KERN_RETURN_ERROR(kr, -1); + + memcpy((void *)remap_dummy_page, (void *)patch_page, page_size); + + int offset = (int)((addr_t)address - patch_page); + memcpy((void *)(remap_dummy_page + offset), buffer, buffer_size); + + kr = mach_vm_protect(self_task, remap_dummy_page, page_size, false, VM_PROT_READ | VM_PROT_EXECUTE); + KERN_RETURN_ERROR(kr, -1); + } + + vm_prot_t prot, max_prot; + kr = mach_vm_remap(self_task, (mach_vm_address_t *)&remap_dest_page, page_size, 0, + VM_FLAGS_OVERWRITE | VM_FLAGS_FIXED, self_task, remap_dummy_page, true, &prot, &max_prot, + VM_INHERIT_COPY); + KERN_RETURN_ERROR(kr, -1); + + kr = mach_vm_deallocate(self_task, remap_dummy_page, page_size); + KERN_RETURN_ERROR(kr, -1); + } else { + + if (0) { + { + auto kr = mach_vm_allocate(self_task, &remap_dummy_page, page_size, VM_FLAGS_ANYWHERE); + KERN_RETURN_ERROR(kr, -1); + + kr = mach_vm_deallocate(self_task, remap_dummy_page, page_size); + KERN_RETURN_ERROR(kr, -1); + } + + vm_prot_t prot, max_prot; + kr = mach_vm_remap(self_task, &remap_dummy_page, page_size, 0, VM_FLAGS_ANYWHERE, self_task, remap_dest_page, + false, &prot, &max_prot, VM_INHERIT_SHARE); + KERN_RETURN_ERROR(kr, -1); + + kr = mach_vm_protect(self_task, remap_dummy_page, page_size, false, VM_PROT_READ | VM_PROT_WRITE); + // the kr always return KERN_PROTECTION_FAILURE + kr = KERN_PROTECTION_FAILURE; + + memcpy((void *)(remap_dummy_page + ((uint64_t)address - remap_dest_page)), buffer, buffer_size); + } + + static __typeof(vm_protect) *vm_protect_impl = nullptr; + if (vm_protect_impl == nullptr) { + vm_protect_impl = (__typeof(vm_protect) *)DobbySymbolResolver("dyld", "vm_protect"); + if (vm_protect_impl == nullptr) { + vm_protect_impl = (__typeof(vm_protect) *)DobbySymbolResolver("libsystem_kernel.dylib", "_vm_protect"); + } + vm_protect_impl = (__typeof(vm_protect) *)pac_sign((void *)vm_protect_impl); + } + { + kr = vm_protect_impl(self_task, remap_dest_page, page_size, false, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); + KERN_RETURN_ERROR(kr, -1); + + memcpy((void *)(patch_page + ((uint64_t)address - remap_dest_page)), buffer, buffer_size); + + kr = vm_protect_impl(self_task, remap_dest_page, page_size, false, orig_prot); + KERN_RETURN_ERROR(kr, -1); + } + } + + ClearCache(address, (void *)((addr_t)address + buffer_size)); + + return 0; +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-posix.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-posix.cc new file mode 100644 index 0000000..bfe7361 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-posix.cc @@ -0,0 +1,37 @@ + +#include "dobby/dobby_internal.h" +#include "core/arch/Cpu.h" + +#include +#include +#include + +#if !defined(__APPLE__) +PUBLIC int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size) { +#if defined(__ANDROID__) || defined(__linux__) + int page_size = (int)sysconf(_SC_PAGESIZE); + uintptr_t patch_page = ALIGN_FLOOR(address, page_size); + uintptr_t patch_end_page = ALIGN_FLOOR((uintptr_t)address + buffer_size, page_size); + + // change page permission as rwx + mprotect((void *)patch_page, page_size, PROT_READ | PROT_WRITE | PROT_EXEC); + if (patch_page != patch_end_page) { + mprotect((void *)patch_end_page, page_size, PROT_READ | PROT_WRITE | PROT_EXEC); + } + + // patch buffer + memcpy(address, buffer, buffer_size); + + // restore page permission + mprotect((void *)patch_page, page_size, PROT_READ | PROT_EXEC); + if (patch_page != patch_end_page) { + mprotect((void *)patch_end_page, page_size, PROT_READ | PROT_EXEC); + } + + addr_t clear_start_ = (addr_t)address; + ClearCache((void *)clear_start_, (void *)(clear_start_ + buffer_size)); +#endif + return 0; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-windows.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-windows.cc new file mode 100644 index 0000000..471a467 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-windows.cc @@ -0,0 +1,27 @@ +#include "dobby/dobby_internal.h" + +#include + +using namespace zz; + +PUBLIC int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size) { + DWORD oldProtect; + int page_size; + + // Get page size + SYSTEM_INFO si; + GetSystemInfo(&si); + page_size = si.dwPageSize; + + void *addressPageAlign = (void *)ALIGN(address, page_size); + + if (!VirtualProtect(addressPageAlign, page_size, PAGE_EXECUTE_READWRITE, &oldProtect)) + return kMemoryOperationError; + + memcpy(address, buffer, buffer_size); + + if (!VirtualProtect(addressPageAlign, page_size, oldProtect, &oldProtect)) + return kMemoryOperationError; + + return 0; +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/substrated/mach_interface_support/substrated.defs b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/substrated/mach_interface_support/substrated.defs new file mode 100644 index 0000000..3871f7a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/substrated/mach_interface_support/substrated.defs @@ -0,0 +1,27 @@ +/* + * Regenerate with: + * + * $(xcrun --sdk macosx -f mig) \ + * -isysroot $(xcrun --sdk macosx --show-sdk-path) \ + * -sheader substratedserver.h \ + * -server substratedserver.c \ + * -header substratedclient.h \ + * -user substratedclient.c \ + * substrated.defs + */ + +subsystem substrated 9000; + +#include +#include + +routine substrated_mark(server + : mach_port_t; + task + : vm_task_entry_t; + source_address + : mach_vm_address_t; + source_size + : mach_vm_size_t; + inout target_address + : mach_vm_address_t); diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/MultiThreadSupport/ThreadSupport.cpp b/app/src/main/cpp/Dobby/source/Backend/UserMode/MultiThreadSupport/ThreadSupport.cpp new file mode 100644 index 0000000..30be99c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/MultiThreadSupport/ThreadSupport.cpp @@ -0,0 +1,22 @@ +#include "MultiThreadSupport/ThreadSupport.h" + +using namespace zz; + +OSThread::LocalStorageKey ThreadSupport::thread_callstack_key_ = 0; + +// Get current CallStack +CallStack *ThreadSupport::CurrentThreadCallStack() { + + // TODO: __attribute__((destructor)) is better ? + if (!thread_callstack_key_) { + thread_callstack_key_ = OSThread::CreateThreadLocalKey(); + } + + if (OSThread::HasThreadLocal(thread_callstack_key_)) { + return static_cast(OSThread::GetThreadLocal(thread_callstack_key_)); + } else { + CallStack *callstack = new CallStack(); + OSThread::SetThreadLocal(thread_callstack_key_, callstack); + return callstack; + } +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/MultiThreadSupport/ThreadSupport.h b/app/src/main/cpp/Dobby/source/Backend/UserMode/MultiThreadSupport/ThreadSupport.h new file mode 100644 index 0000000..ed02a79 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/MultiThreadSupport/ThreadSupport.h @@ -0,0 +1,63 @@ +#ifndef USER_MODE_MULTI_THREAD_SUPPORT_H +#define USER_MODE_MULTI_THREAD_SUPPORT_H + +#include +#include + +#include "dobby/dobby_internal.h" + +#include "source/Backend/UserMode/Thread/PlatformThread.h" + +// StackFrame base in CallStack +typedef struct _StackFrame { + // context between `pre_call` and `post_call` + std::map kv_context; + // origin function ret address + void *orig_ret; +} StackFrame; + +// (thead) CallStack base in thread +typedef struct _CallStack { + tinystl::vector stackframes; +} CallStack; + +// ThreadSupport base on vm_core, support mutipl platforms. +class ThreadSupport { +public: + // Push stack frame + static void PushStackFrame(StackFrame *stackframe) { + CallStack *callstack = ThreadSupport::CurrentThreadCallStack(); + callstack->stackframes.push_back(stackframe); + } + + // Pop stack frame + static StackFrame *PopStackFrame() { + CallStack *callstack = ThreadSupport::CurrentThreadCallStack(); + StackFrame *stackframe = callstack->stackframes.back(); + callstack->stackframes.pop_back(); + return stackframe; + } + + // ===== + static void SetStackFrameContextValue(StackFrame *stackframe, char *key, void *value) { + std::map *kv_context = &stackframe->kv_context; + kv_context->insert(std::pair(key, value)); + }; + + static void *GetStackFrameContextValue(StackFrame *stackframe, char *key) { + std::map kv_context = stackframe->kv_context; + std::map::iterator it; + it = kv_context.find(key); + if (it != kv_context.end()) { + return (void *)it->second; + } + return NULL; + }; + + static CallStack *CurrentThreadCallStack(); + +private: + static zz::OSThread::LocalStorageKey thread_callstack_key_; +}; + +#endif diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc new file mode 100644 index 0000000..988c1aa --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc @@ -0,0 +1,143 @@ +#include "dobby/dobby_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "UnifiedInterface/platform-darwin/mach_vm.h" +#include "PlatformUtil/ProcessRuntimeUtility.h" + +static bool memory_region_comparator(MemRegion a, MemRegion b) { + return (a.start < b.start); +} + +tinystl::vector *regions; + +const tinystl::vector &ProcessRuntimeUtility::GetProcessMemoryLayout() { + if (regions == nullptr) { + regions = new tinystl::vector(); + } + + regions->clear(); + + vm_region_submap_info_64 region_submap_info; + mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; + mach_vm_address_t addr = 0; + mach_vm_size_t size = 0; + natural_t depth = 0; + while (true) { + count = VM_REGION_SUBMAP_INFO_COUNT_64; + kern_return_t kr = mach_vm_region_recurse(mach_task_self(), (mach_vm_address_t *)&addr, (mach_vm_size_t *)&size, + &depth, (vm_region_recurse_info_t)®ion_submap_info, &count); + if (kr != KERN_SUCCESS) { + if (kr == KERN_INVALID_ADDRESS) { + break; + } else { + break; + } + } + + if (region_submap_info.is_submap) { + depth++; + } else { + MemoryPermission permission; + if ((region_submap_info.protection & PROT_READ) && (region_submap_info.protection & PROT_WRITE)) { + permission = MemoryPermission::kReadWrite; + } else if ((region_submap_info.protection & PROT_READ) == region_submap_info.protection) { + permission = MemoryPermission::kRead; + } else if ((region_submap_info.protection & PROT_READ) && (region_submap_info.protection & PROT_EXEC)) { + permission = MemoryPermission::kReadExecute; + } else { + permission = MemoryPermission::kNoAccess; + } +#if 0 + DEBUG_LOG("%p --- %p", addr, addr + size); +#endif + MemRegion region = MemRegion(addr, size, permission); + regions->push_back(region); + addr += size; + } + } + + // std::sort(ProcessMemoryLayout.begin(), ProcessMemoryLayout.end(), memory_region_comparator); + + return *regions; +} + +static tinystl::vector *modules; + +const tinystl::vector &ProcessRuntimeUtility::GetProcessModuleMap() { + if (modules == nullptr) { + modules = new tinystl::vector(); + } + modules->clear(); + + kern_return_t kr; + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kr = task_info(mach_task_self_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count); + if (kr != KERN_SUCCESS) { + return *modules; + } + + struct dyld_all_image_infos *infos = (struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; + const struct dyld_image_info *infoArray = infos->infoArray; + uint32_t infoArrayCount = infos->infoArrayCount; + + RuntimeModule module = {0}; + strncpy(module.path, "dummy-placeholder-module", sizeof(module.path) - 1); + module.load_address = 0; + modules->push_back(module); + + strncpy(module.path, infos->dyldPath, sizeof(module.path) - 1); + module.load_address = (void *)infos->dyldImageLoadAddress; + modules->push_back(module); + + for (int i = 0; i < infoArrayCount; ++i) { + const struct dyld_image_info *info = &infoArray[i]; + + { + strncpy(module.path, info->imageFilePath, sizeof(module.path) - 1); + module.load_address = (void *)info->imageLoadAddress; + modules->push_back(module); + } + } + + modules->sort([](const RuntimeModule &a, const RuntimeModule &b) -> int { return a.load_address < b.load_address; }); + + return *modules; +} + +RuntimeModule ProcessRuntimeUtility::GetProcessModule(const char *name) { + auto modules = GetProcessModuleMap(); + for (auto module : modules) { + if (strstr(module.path, name) != 0) { + return module; + } + } + return RuntimeModule{0}; +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc new file mode 100644 index 0000000..862c29f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc @@ -0,0 +1,237 @@ +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define LINE_MAX 2048 + +// ================================================================ +// GetProcessMemoryLayout + +static bool memory_region_comparator(MemRange a, MemRange b) { + return (a.start < b.start); +} + +tinystl::vector regions; +const tinystl::vector &ProcessRuntimeUtility::GetProcessMemoryLayout() { + regions.clear(); + + FILE *fp = fopen("/proc/self/maps", "r"); + if (fp == nullptr) + return regions; + + while (!feof(fp)) { + char line_buffer[LINE_MAX + 1]; + fgets(line_buffer, LINE_MAX, fp); + + // ignore the rest of characters + if (strlen(line_buffer) == LINE_MAX && line_buffer[LINE_MAX] != '\n') { + // Entry not describing executable data. Skip to end of line to set up + // reading the next entry. + int c; + do { + c = getc(fp); + } while ((c != EOF) && (c != '\n')); + if (c == EOF) + break; + } + + addr_t region_start, region_end; + addr_t region_offset; + char permissions[5] = {'\0'}; // Ensure NUL-terminated string. + uint8_t dev_major = 0; + uint8_t dev_minor = 0; + long inode = 0; + int path_index = 0; + + // Sample format from man 5 proc: + // + // address perms offset dev inode pathname + // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm + // + // The final %n term captures the offset in the input string, which is used + // to determine the path name. It *does not* increment the return value. + // Refer to man 3 sscanf for details. + if (sscanf(line_buffer, + "%" PRIxPTR "-%" PRIxPTR " %4c " + "%" PRIxPTR " %hhx:%hhx %ld %n", + ®ion_start, ®ion_end, permissions, ®ion_offset, &dev_major, &dev_minor, &inode, + &path_index) < 7) { + ERROR_LOG("/proc/self/maps parse failed!"); + fclose(fp); + return regions; + } + + MemoryPermission permission; + if (permissions[0] == 'r' && permissions[1] == 'w') { + permission = MemoryPermission::kReadWrite; + } else if (permissions[0] == 'r' && permissions[2] == 'x') { + permission = MemoryPermission::kReadExecute; + } else if (permissions[0] == 'r' && permissions[1] == 'w' && permissions[2] == 'x') { + permission = MemoryPermission::kReadWriteExecute; + } else { + permission = MemoryPermission::kNoAccess; + } + +#if 0 + DEBUG_LOG("%p --- %p", region_start, region_end); +#endif + + MemRegion region = MemRegion(region_start, region_end - region_start, permission); + regions.push_back(region); + } + std::sort(regions.begin(), regions.end(), memory_region_comparator); + + fclose(fp); + return regions; +} + +// ================================================================ +// GetProcessModuleMap + +static tinystl::vector *modules; +static tinystl::vector &get_process_map_with_proc_maps() { + if (modules == nullptr) { + modules = new tinystl::vector(); + } + + FILE *fp = fopen("/proc/self/maps", "r"); + if (fp == nullptr) + return *modules; + + while (!feof(fp)) { + char line_buffer[LINE_MAX + 1]; + fgets(line_buffer, LINE_MAX, fp); + + // ignore the rest of characters + if (strlen(line_buffer) == LINE_MAX && line_buffer[LINE_MAX] != '\n') { + // Entry not describing executable data. Skip to end of line to set up + // reading the next entry. + int c; + do { + c = getc(fp); + } while ((c != EOF) && (c != '\n')); + if (c == EOF) + break; + } + + addr_t region_start, region_end; + addr_t region_offset; + char permissions[5] = {'\0'}; // Ensure NUL-terminated string. + uint8_t dev_major = 0; + uint8_t dev_minor = 0; + long inode = 0; + int path_index = 0; + + // Sample format from man 5 proc: + // + // address perms offset dev inode pathname + // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm + // + // The final %n term captures the offset in the input string, which is used + // to determine the path name. It *does not* increment the return value. + // Refer to man 3 sscanf for details. + if (sscanf(line_buffer, + "%" PRIxPTR "-%" PRIxPTR " %4c " + "%" PRIxPTR " %hhx:%hhx %ld %n", + ®ion_start, ®ion_end, permissions, ®ion_offset, &dev_major, &dev_minor, &inode, + &path_index) < 7) { + ERROR_LOG("/proc/self/maps parse failed!"); + fclose(fp); + return *modules; + } + + // check header section permission + if (strcmp(permissions, "r--p") != 0 && strcmp(permissions, "r-xp") != 0) + continue; + + // check elf magic number + ElfW(Ehdr) *header = (ElfW(Ehdr) *)region_start; + if (memcmp(header->e_ident, ELFMAG, SELFMAG) != 0) { + continue; + } + + char *path_buffer = line_buffer + path_index; + if (*path_buffer == 0 || *path_buffer == '\n' || *path_buffer == '[') + continue; + RuntimeModule module; + + // strip + if (path_buffer[strlen(path_buffer) - 1] == '\n') { + path_buffer[strlen(path_buffer) - 1] = 0; + } + strncpy(module.path, path_buffer, sizeof(module.path) - 1); + module.load_address = (void *)region_start; + modules->push_back(module); + +#if 0 + DEBUG_LOG("module: %s", module.path); +#endif + } + + fclose(fp); + return *modules; +} + +#if defined(__LP64__) +static tinystl::vector get_process_map_with_linker_iterator() { + tinystl::vector ProcessModuleMap; + + static int (*dl_iterate_phdr_ptr)(int (*)(struct dl_phdr_info *, size_t, void *), void *); + dl_iterate_phdr_ptr = (__typeof(dl_iterate_phdr_ptr))dlsym(RTLD_DEFAULT, "dl_iterate_phdr"); + if (dl_iterate_phdr_ptr == NULL) { + return ProcessModuleMap; + } + + dl_iterate_phdr_ptr( + [](dl_phdr_info *info, size_t size, void *data) { + RuntimeModule module = {0}; + if (info->dlpi_name && info->dlpi_name[0] == '/') + strcpy(module.path, info->dlpi_name); + + module.load_address = (void *)info->dlpi_addr; + for (size_t i = 0; i < info->dlpi_phnum; ++i) { + if (info->dlpi_phdr[i].p_type == PT_LOAD) { + uintptr_t load_bias = (info->dlpi_phdr[i].p_vaddr - info->dlpi_phdr[i].p_offset); + module.load_address = (void *)((addr_t)module.load_address + load_bias); + break; + } + } + + // push to vector + auto ProcessModuleMap = reinterpret_cast *>(data); + ProcessModuleMap->push_back(module); + return 0; + }, + (void *)&ProcessModuleMap); + + return ProcessModuleMap; +} +#endif + +const tinystl::vector &ProcessRuntimeUtility::GetProcessModuleMap() { +#if defined(__LP64__) && 0 + // TODO: won't resolve main binary + return get_process_map_with_linker_iterator(); +#else + return get_process_map_with_proc_maps(); +#endif +} + +RuntimeModule ProcessRuntimeUtility::GetProcessModule(const char *name) { + auto modules = GetProcessModuleMap(); + for (auto module : modules) { + if (strstr(module.path, name) != 0) { + return module; + } + } + return RuntimeModule{0}; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc new file mode 100644 index 0000000..632bfc5 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc @@ -0,0 +1,81 @@ +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include + +#include + +#define LINE_MAX 2048 + +// ================================================================ +// GetProcessMemoryLayout + +static bool memory_region_comparator(MemRange a, MemRange b) { + return (a.address > b.address); +} + +// https://gist.github.com/jedwardsol/9d4fe1fd806043a5767affbd200088ca + +tinystl::vector ProcessMemoryLayout; +tinystl::vector ProcessRuntimeUtility::GetProcessMemoryLayout() { + if (!ProcessMemoryLayout.empty()) { + ProcessMemoryLayout.clear(); + } + + char *address{nullptr}; + MEMORY_BASIC_INFORMATION region; + + while (VirtualQuery(address, ®ion, sizeof(region))) { + address += region.RegionSize; + if (!(region.State & (MEM_COMMIT | MEM_RESERVE))) { + continue; + } + + MemoryPermission permission = MemoryPermission::kNoAccess; + auto mask = PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE; + switch (region.Protect & ~mask) { + case PAGE_NOACCESS: + case PAGE_READONLY: + break; + + case PAGE_EXECUTE: + case PAGE_EXECUTE_READ: + permission = MemoryPermission::kReadExecute; + break; + + case PAGE_READWRITE: + case PAGE_WRITECOPY: + permission = MemoryPermission::kReadWrite; + break; + + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + permission = MemoryPermission::kReadWriteExecute; + break; + } + + ProcessMemoryLayout.push_back(MemRange{(void *)region.BaseAddress, region.RegionSize, permission}); + } + return ProcessMemoryLayout; +} + +// ================================================================ +// GetProcessModuleMap + +tinystl::vector ProcessModuleMap; + +tinystl::vector ProcessRuntimeUtility::GetProcessModuleMap() { + if (!ProcessMemoryLayout.empty()) { + ProcessMemoryLayout.clear(); + } + return ProcessModuleMap; +} + +RuntimeModule ProcessRuntimeUtility::GetProcessModule(const char *name) { + tinystl::vector ProcessModuleMap = GetProcessModuleMap(); + for (auto module : ProcessModuleMap) { + if (strstr(module.path, name) != 0) { + return module; + } + } + return RuntimeModule{0}; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.cc new file mode 100644 index 0000000..827d125 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.cc @@ -0,0 +1,19 @@ +#include "./PlatformThread.h" + +namespace zz { +int OSThread::GetThreadLocalInt(LocalStorageKey key) { + return static_cast(reinterpret_cast(GetThreadLocal(key))); +} + +void OSThread::SetThreadLocalInt(LocalStorageKey key, int value) { + SetThreadLocal(key, reinterpret_cast(static_cast(value))); +} + +bool OSThread::HasThreadLocal(LocalStorageKey key) { + return GetThreadLocal(key) != nullptr; +} + +void *OSThread::GetExistingThreadLocal(LocalStorageKey key) { + return GetThreadLocal(key); +} +} // namespace zz \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.h b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.h new file mode 100644 index 0000000..5a46c6c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.h @@ -0,0 +1,32 @@ +#pragma once + +#include "dobby/common.h" + +namespace zz { + +class OSThread { +public: + typedef int LocalStorageKey; + + static int GetCurrentProcessId(); + + static int GetCurrentThreadId(); + + static LocalStorageKey CreateThreadLocalKey(); + + static void DeleteThreadLocalKey(LocalStorageKey key); + + static void *GetThreadLocal(LocalStorageKey key); + + static int GetThreadLocalInt(LocalStorageKey key); + + static void SetThreadLocal(LocalStorageKey key, void *value); + + static void SetThreadLocalInt(LocalStorageKey key, int value); + + static bool HasThreadLocal(LocalStorageKey key); + + static void *GetExistingThreadLocal(LocalStorageKey key); +}; + +} // namespace zz \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/platform-thread-posix.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/platform-thread-posix.cc new file mode 100644 index 0000000..486618c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/platform-thread-posix.cc @@ -0,0 +1,71 @@ +#include "Thread/PlatformThread.h" + +#include // getpid +#include // pthread +#include + +using namespace zz; + +int OSThread::GetCurrentProcessId() { + return static_cast(getpid()); +} + +int OSThread::GetCurrentThreadId() { +#if defined(__APPLE__) + return static_cast(pthread_mach_thread_np(pthread_self())); +#elif defined(__ANDROID__) + return static_cast(gettid()); +#elif defined(__linux__) + return static_cast(syscall(__NR_gettid)); +#else + return static_cast(reinterpret_cast(pthread_self())); +#endif +} + +static OSThread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) { +#if defined(__cygwin__) + // We need to cast pthread_key_t to OSThread::LocalStorageKey in two steps + // because pthread_key_t is a pointer type on Cygwin. This will probably not + // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway. + assert(sizeof(OSThread::LocalStorageKey) == sizeof(pthread_key_t)); + intptr_t ptr_key = reinterpret_cast(pthread_key); + return static_cast(ptr_key); +#else + return static_cast(pthread_key); +#endif +} + +static pthread_key_t LocalKeyToPthreadKey(OSThread::LocalStorageKey local_key) { +#if defined(__cygwin__) + assert(sizeof(OSThread::LocalStorageKey) == sizeof(pthread_key_t)); + intptr_t ptr_key = static_cast(local_key); + return reinterpret_cast(ptr_key); +#else + return static_cast(local_key); +#endif +} + +OSThread::LocalStorageKey OSThread::CreateThreadLocalKey() { + pthread_key_t key; + int result = pthread_key_create(&key, nullptr); + DCHECK_EQ(0, result); + LocalStorageKey local_key = PthreadKeyToLocalKey(key); + return local_key; +} + +void OSThread::DeleteThreadLocalKey(LocalStorageKey key) { + pthread_key_t pthread_key = LocalKeyToPthreadKey(key); + int result = pthread_key_delete(pthread_key); + DCHECK_EQ(0, result); +} + +void *OSThread::GetThreadLocal(LocalStorageKey key) { + pthread_key_t pthread_key = LocalKeyToPthreadKey(key); + return pthread_getspecific(pthread_key); +} + +void OSThread::SetThreadLocal(LocalStorageKey key, void *value) { + pthread_key_t pthread_key = LocalKeyToPthreadKey(key); + int result = pthread_setspecific(pthread_key, value); + DCHECK_EQ(0, result); +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/platform-thread-windows.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/platform-thread-windows.cc new file mode 100644 index 0000000..1428bb0 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/platform-thread-windows.cc @@ -0,0 +1,25 @@ +#include "PlatformThread.h" + +using namespace zz; + +int OSThread::GetCurrentProcessId() { + return 0; +} + +int OSThread::GetCurrentThreadId() { + return 0; +} + +OSThread::LocalStorageKey OSThread::CreateThreadLocalKey() { + return 0; +} + +void OSThread::DeleteThreadLocalKey(LocalStorageKey key) { +} + +void *OSThread::GetThreadLocal(LocalStorageKey key) { + return NULL; +} + +void OSThread::SetThreadLocal(LocalStorageKey key, void *value) { +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-darwin/mach_vm.h b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-darwin/mach_vm.h new file mode 100644 index 0000000..a9cab32 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-darwin/mach_vm.h @@ -0,0 +1,933 @@ +#ifndef _mach_vm_user_ +#define _mach_vm_user_ + +/* Module mach_vm */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* BEGIN MIG_STRNCPY_ZEROFILL CODE */ + +#if defined(__has_include) +#if __has_include() +#ifndef USING_MIG_STRNCPY_ZEROFILL +#define USING_MIG_STRNCPY_ZEROFILL +#endif +#ifndef __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ +#define __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ +#ifdef __cplusplus +extern "C" { +#endif +extern int mig_strncpy_zerofill(char *dest, const char *src, int len) __attribute__((weak_import)); +#ifdef __cplusplus +} +#endif +#endif /* __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ */ +#endif /* __has_include() */ +#endif /* __has_include */ + +/* END MIG_STRNCPY_ZEROFILL CODE */ + +#ifdef AUTOTEST +#ifndef FUNCTION_PTR_T +#define FUNCTION_PTR_T +typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t); +typedef struct { + char *name; + function_ptr_t function; +} function_table_entry; +typedef function_table_entry *function_table_t; +#endif /* FUNCTION_PTR_T */ +#endif /* AUTOTEST */ + +#ifndef mach_vm_MSG_COUNT +#define mach_vm_MSG_COUNT 20 +#endif /* mach_vm_MSG_COUNT */ + +#include +#include +#include +#include + +#ifdef __BeforeMigUserHeader +__BeforeMigUserHeader +#endif /* __BeforeMigUserHeader */ + +#include + + __BEGIN_DECLS + +/* Routine mach_vm_allocate */ +#ifdef mig_external + mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags); + +/* Routine mach_vm_deallocate */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_deallocate(vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); + +/* Routine mach_vm_protect */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_protect(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, boolean_t set_maximum, + vm_prot_t new_protection); + +/* Routine mach_vm_inherit */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_inherit(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, vm_inherit_t new_inheritance); + +/* Routine mach_vm_read */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_read(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, vm_offset_t *data, + mach_msg_type_number_t *dataCnt); + +/* Routine mach_vm_read_list */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_read_list(vm_map_t target_task, mach_vm_read_entry_t data_list, natural_t count); + +/* Routine mach_vm_write */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt); + +/* Routine mach_vm_copy */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_copy(vm_map_t target_task, mach_vm_address_t source_address, mach_vm_size_t size, + mach_vm_address_t dest_address); + +/* Routine mach_vm_read_overwrite */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_read_overwrite(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, mach_vm_address_t data, + mach_vm_size_t *outsize); + +/* Routine mach_vm_msync */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_msync(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, vm_sync_t sync_flags); + +/* Routine mach_vm_behavior_set */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_behavior_set(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, + vm_behavior_t new_behavior); + +/* Routine mach_vm_map */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_map(vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t size, mach_vm_offset_t mask, int flags, + mem_entry_name_port_t object, memory_object_offset_t offset, boolean_t copy, vm_prot_t curr_protection, + vm_prot_t max_protection, vm_inherit_t inheritance); + +/* Routine mach_vm_machine_attribute */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_machine_attribute(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, + vm_machine_attribute_t attribute, vm_machine_attribute_val_t *value); + +/* Routine mach_vm_remap */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_remap(vm_map_t target_task, mach_vm_address_t *target_address, mach_vm_size_t size, mach_vm_offset_t mask, + int flags, vm_map_t src_task, mach_vm_address_t src_address, boolean_t copy, + vm_prot_t *curr_protection, vm_prot_t *max_protection, vm_inherit_t inheritance); + +/* Routine mach_vm_page_query */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_page_query(vm_map_t target_map, mach_vm_offset_t offset, integer_t *disposition, integer_t *ref_count); + +/* Routine mach_vm_region_recurse */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_region_recurse(vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t *size, + natural_t *nesting_depth, vm_region_recurse_info_t info, mach_msg_type_number_t *infoCnt); + +/* Routine mach_vm_region */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_region(vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t *size, vm_region_flavor_t flavor, + vm_region_info_t info, mach_msg_type_number_t *infoCnt, mach_port_t *object_name); + +/* Routine _mach_make_memory_entry */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + _mach_make_memory_entry(vm_map_t target_task, memory_object_size_t *size, memory_object_offset_t offset, + vm_prot_t permission, mem_entry_name_port_t *object_handle, + mem_entry_name_port_t parent_handle); + +/* Routine mach_vm_purgable_control */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_purgable_control(vm_map_t target_task, mach_vm_address_t address, vm_purgable_t control, int *state); + +/* Routine mach_vm_page_info */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ + kern_return_t + mach_vm_page_info(vm_map_t target_task, mach_vm_address_t address, vm_page_info_flavor_t flavor, + vm_page_info_t info, mach_msg_type_number_t *infoCnt); + +__END_DECLS + +/********************** Caution **************************/ +/* The following data types should be used to calculate */ +/* maximum message sizes only. The actual message may be */ +/* smaller, and the position of the arguments within the */ +/* message layout may vary from what is presented here. */ +/* For example, if any of the arguments are variable- */ +/* sized, and less than the maximum is sent, the data */ +/* will be packed tight in the actual message to reduce */ +/* the presence of holes. */ +/********************** Caution **************************/ + +/* typedefs for all requests */ + +#ifndef __Request__mach_vm_subsystem__defined +#define __Request__mach_vm_subsystem__defined + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + int flags; +} __Request__mach_vm_allocate_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; +} __Request__mach_vm_deallocate_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + boolean_t set_maximum; + vm_prot_t new_protection; +} __Request__mach_vm_protect_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + vm_inherit_t new_inheritance; +} __Request__mach_vm_inherit_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; +} __Request__mach_vm_read_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_read_entry_t data_list; + natural_t count; +} __Request__mach_vm_read_list_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_ool_descriptor_t data; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_vm_address_t address; + mach_msg_type_number_t dataCnt; +} __Request__mach_vm_write_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t source_address; + mach_vm_size_t size; + mach_vm_address_t dest_address; +} __Request__mach_vm_copy_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + mach_vm_address_t data; +} __Request__mach_vm_read_overwrite_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + vm_sync_t sync_flags; +} __Request__mach_vm_msync_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + vm_behavior_t new_behavior; +} __Request__mach_vm_behavior_set_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t object; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + mach_vm_offset_t mask; + int flags; + memory_object_offset_t offset; + boolean_t copy; + vm_prot_t curr_protection; + vm_prot_t max_protection; + vm_inherit_t inheritance; +} __Request__mach_vm_map_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + vm_machine_attribute_t attribute; + vm_machine_attribute_val_t value; +} __Request__mach_vm_machine_attribute_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t src_task; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_vm_address_t target_address; + mach_vm_size_t size; + mach_vm_offset_t mask; + int flags; + mach_vm_address_t src_address; + boolean_t copy; + vm_inherit_t inheritance; +} __Request__mach_vm_remap_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_offset_t offset; +} __Request__mach_vm_page_query_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + natural_t nesting_depth; + mach_msg_type_number_t infoCnt; +} __Request__mach_vm_region_recurse_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + vm_region_flavor_t flavor; + mach_msg_type_number_t infoCnt; +} __Request__mach_vm_region_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t parent_handle; + /* end of the kernel processed data */ + NDR_record_t NDR; + memory_object_size_t size; + memory_object_offset_t offset; + vm_prot_t permission; +} __Request___mach_make_memory_entry_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + vm_purgable_t control; + int state; +} __Request__mach_vm_purgable_control_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + vm_page_info_flavor_t flavor; + mach_msg_type_number_t infoCnt; +} __Request__mach_vm_page_info_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif +#endif /* !__Request__mach_vm_subsystem__defined */ + +/* union of all requests */ + +#ifndef __RequestUnion__mach_vm_subsystem__defined +#define __RequestUnion__mach_vm_subsystem__defined +union __RequestUnion__mach_vm_subsystem { + __Request__mach_vm_allocate_t Request_mach_vm_allocate; + __Request__mach_vm_deallocate_t Request_mach_vm_deallocate; + __Request__mach_vm_protect_t Request_mach_vm_protect; + __Request__mach_vm_inherit_t Request_mach_vm_inherit; + __Request__mach_vm_read_t Request_mach_vm_read; + __Request__mach_vm_read_list_t Request_mach_vm_read_list; + __Request__mach_vm_write_t Request_mach_vm_write; + __Request__mach_vm_copy_t Request_mach_vm_copy; + __Request__mach_vm_read_overwrite_t Request_mach_vm_read_overwrite; + __Request__mach_vm_msync_t Request_mach_vm_msync; + __Request__mach_vm_behavior_set_t Request_mach_vm_behavior_set; + __Request__mach_vm_map_t Request_mach_vm_map; + __Request__mach_vm_machine_attribute_t Request_mach_vm_machine_attribute; + __Request__mach_vm_remap_t Request_mach_vm_remap; + __Request__mach_vm_page_query_t Request_mach_vm_page_query; + __Request__mach_vm_region_recurse_t Request_mach_vm_region_recurse; + __Request__mach_vm_region_t Request_mach_vm_region; + __Request___mach_make_memory_entry_t Request__mach_make_memory_entry; + __Request__mach_vm_purgable_control_t Request_mach_vm_purgable_control; + __Request__mach_vm_page_info_t Request_mach_vm_page_info; +}; +#endif /* !__RequestUnion__mach_vm_subsystem__defined */ +/* typedefs for all replies */ + +#ifndef __Reply__mach_vm_subsystem__defined +#define __Reply__mach_vm_subsystem__defined + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_address_t address; +} __Reply__mach_vm_allocate_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; +} __Reply__mach_vm_deallocate_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; +} __Reply__mach_vm_protect_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; +} __Reply__mach_vm_inherit_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_ool_descriptor_t data; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_msg_type_number_t dataCnt; +} __Reply__mach_vm_read_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_read_entry_t data_list; +} __Reply__mach_vm_read_list_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; +} __Reply__mach_vm_write_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; +} __Reply__mach_vm_copy_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_size_t outsize; +} __Reply__mach_vm_read_overwrite_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; +} __Reply__mach_vm_msync_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; +} __Reply__mach_vm_behavior_set_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_address_t address; +} __Reply__mach_vm_map_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + vm_machine_attribute_val_t value; +} __Reply__mach_vm_machine_attribute_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_address_t target_address; + vm_prot_t curr_protection; + vm_prot_t max_protection; +} __Reply__mach_vm_remap_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + integer_t disposition; + integer_t ref_count; +} __Reply__mach_vm_page_query_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_address_t address; + mach_vm_size_t size; + natural_t nesting_depth; + mach_msg_type_number_t infoCnt; + int info[19]; +} __Reply__mach_vm_region_recurse_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t object_name; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + mach_msg_type_number_t infoCnt; + int info[10]; +} __Reply__mach_vm_region_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t object_handle; + /* end of the kernel processed data */ + NDR_record_t NDR; + memory_object_size_t size; +} __Reply___mach_make_memory_entry_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int state; +} __Reply__mach_vm_purgable_control_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif + +#ifdef __MigPackStructs +#pragma pack(4) +#endif +typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_type_number_t infoCnt; + int info[32]; +} __Reply__mach_vm_page_info_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack() +#endif +#endif /* !__Reply__mach_vm_subsystem__defined */ + +/* union of all replies */ + +#ifndef __ReplyUnion__mach_vm_subsystem__defined +#define __ReplyUnion__mach_vm_subsystem__defined +union __ReplyUnion__mach_vm_subsystem { + __Reply__mach_vm_allocate_t Reply_mach_vm_allocate; + __Reply__mach_vm_deallocate_t Reply_mach_vm_deallocate; + __Reply__mach_vm_protect_t Reply_mach_vm_protect; + __Reply__mach_vm_inherit_t Reply_mach_vm_inherit; + __Reply__mach_vm_read_t Reply_mach_vm_read; + __Reply__mach_vm_read_list_t Reply_mach_vm_read_list; + __Reply__mach_vm_write_t Reply_mach_vm_write; + __Reply__mach_vm_copy_t Reply_mach_vm_copy; + __Reply__mach_vm_read_overwrite_t Reply_mach_vm_read_overwrite; + __Reply__mach_vm_msync_t Reply_mach_vm_msync; + __Reply__mach_vm_behavior_set_t Reply_mach_vm_behavior_set; + __Reply__mach_vm_map_t Reply_mach_vm_map; + __Reply__mach_vm_machine_attribute_t Reply_mach_vm_machine_attribute; + __Reply__mach_vm_remap_t Reply_mach_vm_remap; + __Reply__mach_vm_page_query_t Reply_mach_vm_page_query; + __Reply__mach_vm_region_recurse_t Reply_mach_vm_region_recurse; + __Reply__mach_vm_region_t Reply_mach_vm_region; + __Reply___mach_make_memory_entry_t Reply__mach_make_memory_entry; + __Reply__mach_vm_purgable_control_t Reply_mach_vm_purgable_control; + __Reply__mach_vm_page_info_t Reply_mach_vm_page_info; +}; +#endif /* !__RequestUnion__mach_vm_subsystem__defined */ + +#ifndef subsystem_to_name_map_mach_vm +#define subsystem_to_name_map_mach_vm \ + {"mach_vm_allocate", 4800}, {"mach_vm_deallocate", 4801}, {"mach_vm_protect", 4802}, {"mach_vm_inherit", 4803}, \ + {"mach_vm_read", 4804}, {"mach_vm_read_list", 4805}, {"mach_vm_write", 4806}, {"mach_vm_copy", 4807}, \ + {"mach_vm_read_overwrite", 4808}, {"mach_vm_msync", 4809}, {"mach_vm_behavior_set", 4810}, \ + {"mach_vm_map", 4811}, {"mach_vm_machine_attribute", 4812}, {"mach_vm_remap", 4813}, \ + {"mach_vm_page_query", 4814}, {"mach_vm_region_recurse", 4815}, {"mach_vm_region", 4816}, \ + {"_mach_make_memory_entry", 4817}, {"mach_vm_purgable_control", 4818}, { \ + "mach_vm_page_info", 4819 \ + } +#endif + +#ifdef __AfterMigUserHeader +__AfterMigUserHeader +#endif /* __AfterMigUserHeader */ + +#endif /* _mach_vm_user_ */ diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-posix.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-posix.cc new file mode 100644 index 0000000..5894ae8 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-posix.cc @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#include // for pthread_set_name_np +#endif + +#include // for sched_yield +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include // NOLINT, for sysctl +#endif + +#include "logging/logging.h" +#include "logging/check_logging.h" +#include "PlatformUnifiedInterface/platform.h" + +#if defined(__APPLE__) +#include +#include +#include +#endif + +#if defined(ANDROID) && !defined(ANDROID_LOG_STDOUT) +#define ANDROID_LOG_TAG "Dobby" + +#include + +#endif + +#include + +#if defined(__APPLE__) +const int kMmapFd = VM_MAKE_TAG(255); +#else +const int kMmapFd = -1; +#endif + +const int kMmapFdOffset = 0; + +using namespace base; + +typedef struct thread_handle_t { + pthread_t thread; +} thread_handle_t; + +void ThreadInterface::SetName(const char *name) { +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) + pthread_set_name_np(pthread_self(), name); +#elif defined(__APPLE__) + pthread_setname_np(name); +#endif +} + +int ThreadInterface::CurrentId() { +#if defined(__APPLE__) + mach_port_t port = mach_thread_self(); + mach_port_deallocate(mach_task_self(), port); + return port; +#elif defined(_POSIX_VERSION) + return syscall(__NR_gettid); +#endif +} + +static void *thread_handler_wrapper(void *ctx) { + ThreadInterface::Delegate *d = (ThreadInterface::Delegate *)ctx; + d->ThreadMain(); + return nullptr; +} + +bool ThreadInterface::Create(ThreadInterface::Delegate *delegate, ThreadHandle *handle) { + thread_handle_t *handle_impl = new thread_handle_t; + + int err = 0; + err = pthread_create(&(handle_impl->thread), nullptr, thread_handler_wrapper, delegate); + if (err != 0) { + ERROR_LOG("pthread create failed"); + return false; + } + return true; +} + +OSThread::OSThread(const char *name) { + strncpy(name_, name, sizeof(name_) -1); +} + +bool OSThread::Start() { + if (ThreadInterface::Create(this, &handle_) == false) { + return false; + } + return true; +} + +static int GetProtectionFromMemoryPermission(MemoryPermission access) { + switch (access) { + case MemoryPermission::kNoAccess: + return PROT_NONE; + case MemoryPermission::kRead: + return PROT_READ; + case MemoryPermission::kReadWrite: + return PROT_READ | PROT_WRITE; + case MemoryPermission::kReadWriteExecute: + return PROT_READ | PROT_WRITE | PROT_EXEC; + case MemoryPermission::kReadExecute: + return PROT_READ | PROT_EXEC; + } + UNREACHABLE(); +} + +int OSMemory::PageSize() { + return static_cast(sysconf(_SC_PAGESIZE)); +} + +void *OSMemory::Allocate(size_t size, MemoryPermission access) { + return OSMemory::Allocate(size, access, nullptr); +} + +void *OSMemory::Allocate(size_t size, MemoryPermission access, void *fixed_address) { + int prot = GetProtectionFromMemoryPermission(access); + + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + if (fixed_address != nullptr) { + flags = flags | MAP_FIXED; + } + void *result = mmap(fixed_address, size, prot, flags, kMmapFd, kMmapFdOffset); + if (result == MAP_FAILED) + return nullptr; + + return result; +} + +bool OSMemory::Free(void *address, size_t size) { + DCHECK_EQ(0, reinterpret_cast(address) % PageSize()); + DCHECK_EQ(0, size % PageSize()); + + return munmap(address, size) == 0; +} + +bool OSMemory::Release(void *address, size_t size) { + DCHECK_EQ(0, reinterpret_cast(address) % PageSize()); + DCHECK_EQ(0, size % PageSize()); + + return munmap(address, size) == 0; +} + +bool OSMemory::SetPermission(void *address, size_t size, MemoryPermission access) { + DCHECK_EQ(0, reinterpret_cast(address) % PageSize()); + DCHECK_EQ(0, size % PageSize()); + + int prot = GetProtectionFromMemoryPermission(access); + int ret = mprotect(address, size, prot); + if (ret) { + ERROR_LOG("OSMemory::SetPermission: %s\n", ((const char *)strerror(errno))); + } + + return ret == 0; +} + +void OSPrint::Print(const char *format, ...) { + va_list args; + va_start(args, format); + VPrint(format, args); + va_end(args); +} + +void OSPrint::VPrint(const char *format, va_list args) { +#if defined(ANDROID) && !defined(ANDROID_LOG_STDOUT) + __android_log_vprint(ANDROID_LOG_INFO, ANDROID_LOG_TAG, format, args); +#else + vprintf(format, args); +#endif +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-windows.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-windows.cc new file mode 100644 index 0000000..30af5b8 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-windows.cc @@ -0,0 +1,86 @@ +#include + +#include + +#include "logging/logging.h" +#include "logging/check_logging.h" +#include "PlatformUnifiedInterface/platform.h" + +int GetProtectionFromMemoryPermission(MemoryPermission access) { + if (kReadWriteExecute == access) + return PAGE_EXECUTE_READWRITE; + else if (kReadExecute == access) + return PAGE_EXECUTE_READ; +} + +int OSMemory::AllocPageSize() { + static int lastRet = -1; + if (lastRet == -1) { + SYSTEM_INFO si; + GetSystemInfo(&si); + lastRet = si.dwAllocationGranularity; // should be used with VirtualAlloc(MEM_RESERVE) + } + return lastRet; +} + +int OSMemory::PageSize() { + static int lastRet = -1; + if (lastRet == -1) { + SYSTEM_INFO si; + GetSystemInfo(&si); + lastRet = si.dwPageSize; // should be used with VirtualAlloc(MEM_RESERVE) + } + return lastRet; +} + +void *OSMemory::Allocate(void *address, int size, MemoryPermission access) { + DCHECK_EQ(0, reinterpret_cast(address) % AllocPageSize()); + DCHECK_EQ(0, size % PageSize()); + + void *result = VirtualAlloc(address, size, MEM_COMMIT | MEM_RESERVE, PAGE_NOACCESS); + OSMemory::SetPermission(result, size, kReadWriteExecute); + if (result == nullptr) + return nullptr; + + // TODO: if need align + void *aligned_base = result; + return static_cast(aligned_base); +} + +// static +bool OSMemory::Free(void *address, const int size) { + DCHECK_EQ(0, reinterpret_cast(address) % PageSize()); + DCHECK_EQ(0, size % PageSize()); + + return VirtualFree(address, size, MEM_RELEASE); +} + +bool OSMemory::Release(void *address, int size) { + DCHECK_EQ(0, reinterpret_cast(address) % PageSize()); + DCHECK_EQ(0, size % PageSize()); + + return OSMemory::Free(address, size); +} + +bool OSMemory::SetPermission(void *address, int size, MemoryPermission access) { + DCHECK_EQ(0, reinterpret_cast(address) % PageSize()); + DCHECK_EQ(0, size % PageSize()); + + int prot = GetProtectionFromMemoryPermission(access); + + DWORD oldProtect; + return VirtualProtect(address, size, prot, &oldProtect); +} + +// ===== + +void OSPrint::Print(const char *format, ...) { + va_list args; + va_start(args, format); + VPrint(format, args); + va_end(args); +} + +void OSPrint::VPrint(const char *format, va_list args) { + vprintf(format, args); +} diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/semaphore.cc b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/semaphore.cc new file mode 100644 index 0000000..b454c6e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/semaphore.cc @@ -0,0 +1,160 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/base/platform/semaphore.h" + +#if V8_OS_MACOSX +#include +#endif + +#include + +#include "src/base/logging.h" +#include "src/base/platform/elapsed-timer.h" +#include "src/base/platform/time.h" + +namespace v8 { +namespace base { + +#if V8_OS_MACOSX + +Semaphore::Semaphore(int count) { + native_handle_ = dispatch_semaphore_create(count); + DCHECK(native_handle_); +} + +Semaphore::~Semaphore() { + dispatch_release(native_handle_); +} + +void Semaphore::Signal() { + dispatch_semaphore_signal(native_handle_); +} + +void Semaphore::Wait() { + dispatch_semaphore_wait(native_handle_, DISPATCH_TIME_FOREVER); +} + +bool Semaphore::WaitFor(const TimeDelta &rel_time) { + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time.InNanoseconds()); + return dispatch_semaphore_wait(native_handle_, timeout) == 0; +} + +#elif V8_OS_POSIX + +Semaphore::Semaphore(int count) { + DCHECK_GE(count, 0); + int result = sem_init(&native_handle_, 0, count); + DCHECK_EQ(0, result); + USE(result); +} + +Semaphore::~Semaphore() { + int result = sem_destroy(&native_handle_); + DCHECK_EQ(0, result); + USE(result); +} + +void Semaphore::Signal() { + int result = sem_post(&native_handle_); + // This check may fail with 0) { + // sem_timedwait in glibc prior to 2.3.4 returns the errno instead of -1. + errno = result; + result = -1; + } +#endif + if (result == -1 && errno == ETIMEDOUT) { + // Timed out while waiting for semaphore. + return false; + } + // Signal caused spurious wakeup. + DCHECK_EQ(-1, result); + DCHECK_EQ(EINTR, errno); + } +} + +#elif V8_OS_WIN + +Semaphore::Semaphore(int count) { + DCHECK_GE(count, 0); + native_handle_ = ::CreateSemaphoreA(nullptr, count, 0x7FFFFFFF, nullptr); + DCHECK_NOT_NULL(native_handle_); +} + +Semaphore::~Semaphore() { + BOOL result = CloseHandle(native_handle_); + DCHECK(result); + USE(result); +} + +void Semaphore::Signal() { + LONG dummy; + BOOL result = ReleaseSemaphore(native_handle_, 1, &dummy); + DCHECK(result); + USE(result); +} + +void Semaphore::Wait() { + DWORD result = WaitForSingleObject(native_handle_, INFINITE); + DCHECK(result == WAIT_OBJECT_0); + USE(result); +} + +bool Semaphore::WaitFor(const TimeDelta &rel_time) { + TimeTicks now = TimeTicks::Now(); + TimeTicks end = now + rel_time; + while (true) { + int64_t msec = (end - now).InMilliseconds(); + if (msec >= static_cast(INFINITE)) { + DWORD result = WaitForSingleObject(native_handle_, INFINITE - 1); + if (result == WAIT_OBJECT_0) { + return true; + } + DCHECK(result == WAIT_TIMEOUT); + now = TimeTicks::Now(); + } else { + DWORD result = WaitForSingleObject(native_handle_, (msec < 0) ? 0 : static_cast(msec)); + if (result == WAIT_TIMEOUT) { + return false; + } + DCHECK(result == WAIT_OBJECT_0); + return true; + } + } +} + +#endif // V8_OS_MACOSX + +} // namespace base +} // namespace v8 diff --git a/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/semaphore.h b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/semaphore.h new file mode 100644 index 0000000..cff4cd4 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/semaphore.h @@ -0,0 +1,98 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_BASE_PLATFORM_SEMAPHORE_H_ +#define V8_BASE_PLATFORM_SEMAPHORE_H_ + +#include "src/base/base-export.h" +#include "src/base/lazy-instance.h" +#if V8_OS_WIN +#include "src/base/win32-headers.h" +#endif + +#if V8_OS_MACOSX +#include // NOLINT +#elif V8_OS_POSIX +#include // NOLINT +#endif + +namespace v8 { +namespace base { + +// Forward declarations. +class TimeDelta; + +// ---------------------------------------------------------------------------- +// Semaphore +// +// A semaphore object is a synchronization object that maintains a count. The +// count is decremented each time a thread completes a wait for the semaphore +// object and incremented each time a thread signals the semaphore. When the +// count reaches zero, threads waiting for the semaphore blocks until the +// count becomes non-zero. + +class V8_BASE_EXPORT Semaphore final { +public: + explicit Semaphore(int count); + ~Semaphore(); + + // Increments the semaphore counter. + void Signal(); + + // Decrements the semaphore counter if it is positive, or blocks until it + // becomes positive and then decrements the counter. + void Wait(); + + // Like Wait() but returns after rel_time time has passed. If the timeout + // happens the return value is false and the counter is unchanged. Otherwise + // the semaphore counter is decremented and true is returned. + bool WaitFor(const TimeDelta &rel_time) V8_WARN_UNUSED_RESULT; + +#if V8_OS_MACOSX + using NativeHandle = dispatch_semaphore_t; +#elif V8_OS_POSIX + using NativeHandle = sem_t; +#elif V8_OS_WIN + using NativeHandle = HANDLE; +#endif + + NativeHandle &native_handle() { + return native_handle_; + } + const NativeHandle &native_handle() const { + return native_handle_; + } + +private: + NativeHandle native_handle_; + + DISALLOW_COPY_AND_ASSIGN(Semaphore); +}; + +// POD Semaphore initialized lazily (i.e. the first time Pointer() is called). +// Usage: +// // The following semaphore starts at 0. +// static LazySemaphore<0>::type my_semaphore = LAZY_SEMAPHORE_INITIALIZER; +// +// void my_function() { +// // Do something with my_semaphore.Pointer(). +// } +// + +template struct CreateSemaphoreTrait { + static Semaphore *Create() { + return new Semaphore(N); + } +}; + +template struct LazySemaphore { + using typename LazyDynamicInstance, ThreadSafeInitOnceTrait>::type; +}; + +#define LAZY_SEMAPHORE_INITIALIZER LAZY_DYNAMIC_INSTANCE_INITIALIZER + +} // namespace base +} // namespace v8 + +#endif // V8_BASE_PLATFORM_SEMAPHORE_H_ diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/InstructionRelocation.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/InstructionRelocation.h new file mode 100644 index 0000000..079fc78 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/InstructionRelocation.h @@ -0,0 +1,5 @@ +#include "dobby/dobby_internal.h" + +void GenRelocateCode(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch); + +void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated); diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.cc b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.cc new file mode 100644 index 0000000..1e8f15e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.cc @@ -0,0 +1,895 @@ +#include "platform_detect_macro.h" + +#if defined(TARGET_ARCH_ARM) + +#include "dobby/dobby_internal.h" + +#include "InstructionRelocation/arm/InstructionRelocationARM.h" + +#include "core/arch/arm/registers-arm.h" +#include "core/assembler/assembler-arm.h" +#include "core/codegen/codegen-arm.h" + +using namespace zz; +using namespace zz::arm; + +typedef struct { + addr_t mapped_addr; + + bool thumb_mode; + + uint8_t *buffer; + uint8_t *buffer_cursor; + size_t buffer_size; + + addr_t src_vmaddr; + addr_t dst_vmaddr; + + CodeMemBlock *relocated; + CodeBuffer *relocated_buffer; + + ExecuteState start_state; + ExecuteState curr_state; + Assembler *curr_assembler; + ThumbTurboAssembler *thumb_assembler; + TurboAssembler *arm_assembler; + + tinystl::unordered_map execute_state_map; + + tinystl::unordered_map relocated_offset_map; + + tinystl::unordered_map label_map; +} relo_ctx_t; + +// --- + +addr_t relo_cur_src_vmaddr(relo_ctx_t *ctx) { + int relocated_len = ctx->buffer_cursor - ctx->buffer; + if (ctx->curr_state == zz::arm::ARMExecuteState) { + return ctx->src_vmaddr + relocated_len + ARM_PC_OFFSET; + } else { + return ctx->src_vmaddr + relocated_len + Thumb_PC_OFFSET; + } +} + +static bool is_thumb2(uint32_t insn) { + uint16_t insn1, insn2; + insn1 = insn & 0x0000ffff; + insn2 = (insn & 0xffff0000) >> 16; + // refer: Top level T32 instruction set encoding + uint32_t op0 = bits(insn1, 13, 15); + uint32_t op1 = bits(insn1, 11, 12); + + if (op0 == 0b111 && op1 != 0b00) { + return true; + } + return false; +} + +bool check_execute_state_changed(relo_ctx_t *ctx, addr_t insn_addr) { + for (auto iter = ctx->execute_state_map.begin(); iter != ctx->execute_state_map.end(); ++iter) { + addr_t execute_state_changed_pc = iter->first; + auto state = iter->second; + if (execute_state_changed_pc == insn_addr) { + return true; + } + } + return false; +} + +static inline int32_t SignExtend(unsigned x, int M, int N) { +#if 1 + char sign_bit = bit(x, M - 1); + unsigned sign_mask = 0 - sign_bit; + x |= ((sign_mask >> M) << M); +#else + x = (long)((long)x << (N - M)) >> (N - M); +#endif + return (int32_t)x; +} + +enum arm_shift_type { arm_shift_lsl, arm_shift_lsr, arm_shift_asr, arm_shift_ror, arm_shift_rrx }; + +uint32_t arm_shift_c(uint32_t val, uint32_t shift_type, uint32_t shift_count, uint32_t carry_in, uint32_t *carry_out) { + if (shift_count == 0) + return val; + uint32_t r_val; + uint32_t carry = carry_in; + switch (shift_type) { + case arm_shift_lsl: + r_val = val; + r_val = r_val << shift_count; + carry = (r_val >> 32) & 0x1; + val = r_val; + break; + case arm_shift_lsr: + r_val = val; + r_val = r_val >> (shift_count - 1); + carry = r_val & 0x1; + val = (r_val >> 1); + break; + case arm_shift_asr: + r_val = val; + if (val & 0x80000000) { + r_val |= 0xFFFFFFFF00000000ULL; + } + r_val = r_val >> (shift_count - 1); + carry = r_val & 0x1; + val = (r_val >> 1); + break; + case arm_shift_ror: + val = (val >> (shift_count % 32)) | (val << (32 - (shift_count % 32))); + carry = (val >> 31); + break; + case arm_shift_rrx: + carry = val & 0x1; + val = (carry_in << 31) | (val >> 1); + break; + break; + } + return val; +} + +uint32_t arm_expand_imm_c(uint32_t imm12) { + uint32_t unrotated_value = bits(imm12, 0, 7); + return arm_shift_c(unrotated_value, arm_shift_ror, 2 * bits(imm12, 8, 11), 0, 0); +} + +uint32_t A32ExpandImm(uint32_t imm12) { + return arm_expand_imm_c(imm12); +} + +static void ARMRelocateSingleInsn(relo_ctx_t *ctx, int32_t insn) { + auto turbo_assembler_ = static_cast(ctx->curr_assembler); +#define _ turbo_assembler_-> + + bool is_insn_relocated = false; + + // top level encoding + uint32_t cond, op0, op1; + cond = bits(insn, 28, 31); + op0 = bits(insn, 25, 27); + op1 = bit(insn, 4); + // Load/Store Word, Unsigned byte (immediate, literal) + if (cond != 0b1111 && op0 == 0b010) { + uint32_t P, U, o2, W, o1, Rn, Rt, imm12; + P = bit(insn, 24); + U = bit(insn, 23); + W = bit(insn, 21); + imm12 = bits(insn, 0, 11); + Rn = bits(insn, 16, 19); + Rt = bits(insn, 12, 15); + o1 = bit(insn, 20); + o2 = bit(insn, 22); + uint32_t P_W = (P << 1) | W; + do { + // LDR (literal) + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + if (o1 == 1 && o2 == 0 && P_W != 0b01 && Rn == 0b1111) { + goto load_literal_fix_scheme; + } + if (o1 == 1 && o2 == 1 && P_W != 0b01 && Rn == 0b1111) { + goto load_literal_fix_scheme; + } + break; + load_literal_fix_scheme: + addr32_t dst_vmaddr = 0; + if (U == 0b1) + dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm12; + else + dst_vmaddr = relo_cur_src_vmaddr(ctx) - imm12; + Register regRt = Register::R(Rt); + + auto label = RelocLabel::withData(dst_vmaddr); + _ AppendRelocLabel(label); + + if (regRt.code() == pc.code()) { + _ Ldr(VOLATILE_REGISTER, label); + _ ldr(regRt, MemOperand(VOLATILE_REGISTER)); + } else { + _ Ldr(regRt, label); + _ ldr(regRt, MemOperand(regRt)); + } + + is_insn_relocated = true; + } while (0); + } + + // Data-processing and miscellaneous instructions + if (cond != 0b1111 && (op0 & 0b110) == 0b000) { + uint32_t op0, op1, op2, op3, op4; + op0 = bit(insn, 25); + // Data-processing immediate + if (op0 == 1) { + uint32_t op0, op1; + op0 = bits(insn, 23, 24); + op1 = bits(insn, 20, 21); + // Integer Data Processing (two register and immediate) + if ((op0 & 0b10) == 0b00) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t opc, S, Rn; + opc = bits(insn, 21, 23); + S = bit(insn, 20); + Rn = bits(insn, 16, 19); + + uint32_t dst_vmaddr = -1; + int Rd = bits(insn, 12, 15); + int imm12 = bits(insn, 0, 11); + uint32_t imm = arm_expand_imm_c(imm12); + if (opc == 0b010 && S == 0b0 && Rn == 0b1111) { // ADR - A2 variant + dst_vmaddr = relo_cur_src_vmaddr(ctx) - imm; + } else if (opc == 0b100 && S == 0b0 && Rn == 0b1111) { // ADR - A1 variant + dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + } + + if (dst_vmaddr != -1) { + Register regRd = Register::R(Rd); + auto dst_label = RelocLabel::withData(dst_vmaddr); + _ AppendRelocLabel(dst_label); + + _ Ldr(regRd, dst_label); + + is_insn_relocated = true; + } + } + } + } + + // Branch, branch with link, and block data transfer + if ((op0 & 0b110) == 0b100) { + uint32_t cond, op0; + cond = bits(insn, 28, 31); + op0 = bit(insn, 25); + // Branch (immediate) on page F4-4034 + if (op0 == 1) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t H = 0, imm24 = 0; + H = bit(insn, 24); + imm24 = bits(insn, 0, 23); + int32_t label = SignExtend(imm24 << 2, 2 + 24, 32); + uint32_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + label; + bool branch_link; + if (cond != 0b1111 && H == 0) { // B + branch_link = false; + } else if (cond != 0b1111 && H == 1) { // BL, BLX (immediate) - A1 on page F5-4135 + branch_link = true; + } else if (cond == 0b1111) { // BL, BLX (immediate) - A2 on page F5-4135 + branch_link = true; + cond = AL; + dst_vmaddr |= 1; + } else + UNREACHABLE(); + + if (branch_link) + _ bl((Condition)cond, 0); // goto [dst_vmaddr] + else + _ b((Condition)cond, 0); // goto [dst_vmaddr] + _ b(4); // goto [rest_flow] + // [dst_vmaddr] + _ ldr(pc, MemOperand(pc, -4)); + _ EmitAddress(dst_vmaddr); + // [rest_flow] + _ mov(r8, r8); + + is_insn_relocated = true; + } + } + + // if the insn do not needed relocate, just rewrite the origin + if (!is_insn_relocated) { + _ EmitARMInst(insn); + } +} + +// relocate thumb-1 instructions +static void Thumb1RelocateSingleInsn(relo_ctx_t *ctx, int16_t insn) { + auto turbo_assembler_ = static_cast(ctx->curr_assembler); +#define _ turbo_assembler_-> + + bool is_insn_relocated = false; + + _ AlignThumbNop(); + + uint32_t op = 0, rt = 0, rm = 0, rn = 0, rd = 0, shift = 0, cond = 0; + int32_t offset = 0; + + int32_t op0 = 0, op1 = 0; + op0 = bits(insn, 10, 15); + // Special data instructions and branch and exchange on page F3-3942 + if (op0 == 0b010001) { + op0 = bits(insn, 8, 9); + // Add, subtract, compare, move (two high registers) + if (op0 != 0b11) { + int rs = bits(insn, 3, 6); + // rs is PC register + if (rs == 15) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), + relo_cur_src_vmaddr(ctx)); + + thumb1_inst_t rewrite_inst = insn; + set_bits(rewrite_inst, 3, 6, VOLATILE_REGISTER.code()); + + auto label = ThumbRelocLabelEntry::withData(relo_cur_src_vmaddr(ctx), false); + _ AppendRelocLabel(label); + + _ T2_Ldr(VOLATILE_REGISTER, label); + _ EmitInt16(rewrite_inst); + + is_insn_relocated = true; + } + } + + // Branch and exchange + if (op0 == 0b11) { + int32_t L = bit(insn, 7); + rm = bits(insn, 3, 6); + // BX + if (L == 0b0) { + if (rm == pc.code()) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx); + auto label = ThumbRelocLabelEntry::withData(dst_vmaddr, true); + _ AppendRelocLabel(label); + + _ T2_Ldr(pc, label); + + ctx->execute_state_map[dst_vmaddr] = ARMExecuteState; + + is_insn_relocated = true; + } + } + // BLX + if (L == 0b1) { + if (rm == pc.code()) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx); + auto label = ThumbRelocLabelEntry::withData(dst_vmaddr, true); + _ AppendRelocLabel(label); + + _ t2_bl(4); + _ t2_b(4); // goto [rest flow] + _ T2_Ldr(pc, label); // goto [dst_vmaddr] + // [rest flow] + + ctx->execute_state_map[dst_vmaddr] = ARMExecuteState; + + is_insn_relocated = true; + } + } + } + } + + // LDR (literal) - T1 variant on page F5-4243 + // ldr literal + if ((insn & 0xf800) == 0x4800) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t imm8 = bits(insn, 0, 7); + uint32_t imm = imm8 << 2; + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + dst_vmaddr = ALIGN_FLOOR(dst_vmaddr, 4); + rt = bits(insn, 8, 10); + + auto label = ThumbRelocLabelEntry::withData(dst_vmaddr, false); + _ AppendRelocLabel(label); + + _ T2_Ldr(Register::R(rt), label); + _ t2_ldr(Register::R(rt), MemOperand(Register::R(rt), 0)); + + is_insn_relocated = true; + } + + // Add PC/SP (immediate) on page F3-3939 + // adr + if ((insn & 0xf800) == 0xa000) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + rd = bits(insn, 8, 10); + uint32_t imm8 = bits(insn, 0, 7); + int32_t imm32 = imm8 << 2; + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm32; + + auto label = ThumbRelocLabelEntry::withData(dst_vmaddr, false); + _ AppendRelocLabel(label); + + _ T2_Ldr(Register::R(rd), label); + + is_insn_relocated = true; + } + + // Conditional branch, and Supervisor Call on page F3-3946 + // b + if ((insn & 0xf000) == 0xd000) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint16_t cond = bits(insn, 8, 11); + // cond != 111x + if (cond >= 0b1110) { + UNREACHABLE(); + } + uint32_t imm8 = bits(insn, 0, 7); + int32_t imm = SignExtend(imm8 << 1, 8 + 1, 32); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + dst_vmaddr |= 1; + + auto label = ThumbRelocLabelEntry::withData(dst_vmaddr, true); + _ AppendRelocLabel(label); + + thumb1_inst_t b_cond_insn = 0xe000; + set_bits(b_cond_insn, 8, 11, cond); + _ EmitInt16(b_cond_insn | (4 >> 1)); + _ t1_nop(); // align + _ t2_b(4); + _ T2_Ldr(pc, label); + + is_insn_relocated = true; + } + + // Miscellaneous 16-bit instructions on page F3-3943 + // CBNZ, CBZ + if ((insn & 0xf500) == 0xb100) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t imm5 = bits(insn, 3, 7); + uint32_t i = bit(insn, 9); + uint32_t imm = (i << 6) | (imm5 << 1); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + + rn = bits(insn, 0, 2); + + auto label = ThumbRelocLabelEntry::withData(dst_vmaddr + 1, true); + _ AppendRelocLabel(label); + + imm5 = bits(0x4, 1, 5); + set_bits(insn, 3, 7, imm5); + i = bit(0x4, 6); + set_bit(insn, 9, i); + _ EmitInt16(insn); + _ t1_nop(); // align + _ t2_b(4); // goto [rest flow] + _ T2_Ldr(pc, label); + // [rest flow] + + is_insn_relocated = true; + } + + // F3.1 + // T32 instruction set encoding + // b + if ((insn & 0xf800) == 0xe000) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t imm11 = bits(insn, 0, 10); + int32_t imm = SignExtend(imm11 << 1, 11 + 1, 32); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + + auto label = ThumbRelocLabelEntry::withData(dst_vmaddr + 1, true); + _ AppendRelocLabel(label); + + _ T2_Ldr(pc, label); + + is_insn_relocated = true; + } + + // if the insn do not needed relocate, just rewrite the origin + if (!is_insn_relocated) { +#if 0 + if (relo_cur_src_vmaddr(ctx) % Thumb2_INST_LEN) + _ t1_nop(); +#endif + _ EmitInt16(insn); + } +} + +static void Thumb2RelocateSingleInsn(relo_ctx_t *ctx, thumb1_inst_t insn1, thumb1_inst_t insn2) { + auto turbo_assembler_ = static_cast(ctx->curr_assembler); +#define _ turbo_assembler_-> + + bool is_insn_relocated = false; + + // if (turbo_assembler->pc_offset() % 4) { + // _ t1_nop(); + // } + + _ AlignThumbNop(); + + // Branches and miscellaneous control on page F3-3979 + if ((insn1 & 0xf800) == 0xf000 && (insn2 & 0x8000) == 0x8000) { + uint32_t op1 = 0, op3 = 0; + op1 = bits(insn1, 6, 9); + op3 = bits(insn2, 12, 14); + + // B - T3 variant on page F5-4118 + if (((op1 & 0b1110) != 0b1110) && ((op3 & 0b101) == 0b000)) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t S = bit(insn1, 10); + uint32_t J1 = bit(insn2, 13); + uint32_t J2 = bit(insn2, 11); + uint32_t imm6 = bits(insn1, 0, 5); + uint32_t imm11 = bits(insn2, 0, 10); + + int32_t imm = + SignExtend((S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1), 1 + 1 + 1 + 6 + 11 + 1, 32); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + dst_vmaddr |= 1; + + uint32_t cond = bits(insn1, 6, 9); + thumb1_inst_t b_cond_insn = 0xe000; + set_bits(b_cond_insn, 8, 11, cond); + _ EmitInt16(b_cond_insn | (4 >> 1)); + _ t1_nop(); // align + _ t2_b(8); + _ t2_ldr(pc, MemOperand(pc, 0)); + _ EmitAddress(dst_vmaddr); + + is_insn_relocated = true; + } + + // B - T4 variant on page F5-4118 + if ((op3 & 0b101) == 0b001) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t S = bit(insn1, 10); + uint32_t J1 = bit(insn2, 13); + uint32_t J2 = bit(insn2, 11); + uint32_t imm10 = bits(insn1, 0, 9); + uint32_t imm11 = bits(insn2, 0, 10); + uint32_t i1 = !(J1 ^ S); + uint32_t i2 = !(J2 ^ S); + + int32_t imm = + SignExtend((S << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1), 1 + 1 + 1 + 10 + 11 + 1, 32); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + dst_vmaddr |= 1; + + _ t2_ldr(pc, MemOperand(pc, 0)); + _ EmitAddress(dst_vmaddr); + + is_insn_relocated = true; + } + + // BL, BLX (immediate) - T1 variant on page F5-4135 + if ((op3 & 0b101) == 0b101) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t S = bit(insn1, 10); + uint32_t J1 = bit(insn2, 13); + uint32_t J2 = bit(insn2, 11); + uint32_t i1 = !(J1 ^ S); + uint32_t i2 = !(J2 ^ S); + uint32_t imm11 = bits(insn2, 0, 10); + uint32_t imm10 = bits(insn1, 0, 9); + int32_t imm = + SignExtend((S << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1), 1 + 1 + 1 + 10 + 11 + 1, 32); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + dst_vmaddr |= 1; + + _ t2_bl(4); + _ t2_b(8); + _ t2_ldr(pc, MemOperand(pc, 0)); + _ EmitAddress(dst_vmaddr); + + is_insn_relocated = true; + } + + // BL, BLX (immediate) - T2 variant on page F5-4136 + if ((op3 & 0b101) == 0b100) { + DEBUG_LOG("%d:relo at %p", ctx->relocated_offset_map.size(), relo_cur_src_vmaddr(ctx)); + + uint32_t S = bit(insn1, 10); + uint32_t J1 = bit(insn2, 13); + uint32_t J2 = bit(insn2, 11); + uint32_t i1 = !(J1 ^ S); + uint32_t i2 = !(J2 ^ S); + uint32_t imm10h = bits(insn1, 0, 9); + uint32_t imm10l = bits(insn2, 1, 10); + int32_t imm = + SignExtend((S << 24) | (i1 << 23) | (i2 << 22) | (imm10h << 12) | (imm10l << 2), 1 + 1 + 1 + 10 + 10 + 1, 32); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx); + dst_vmaddr = ALIGN_FLOOR(dst_vmaddr, 4); + dst_vmaddr += imm; + + _ t2_bl(4); + _ t2_b(8); + _ t2_ldr(pc, MemOperand(pc, 0)); + _ EmitAddress(dst_vmaddr); + + is_insn_relocated = true; + } + } + + // Data-processing (plain binary immediate) on page F3-3983 + if ((insn1 & (0xfa10)) == 0xf200 & (insn2 & 0x8000) == 0) { + uint32_t op0 = 0, op1 = 0; + op0 = bit(insn1, 8); + op1 = bits(insn2, 5, 6); + + // Data-processing (simple immediate) + if (op0 == 0 && (op1 & 0b10) == 0b00) { + int o1 = bit(insn1, 7); + int o2 = bit(insn1, 5); + int rn = bits(insn1, 0, 3); + + // ADR + if (((o1 == 0 && o2 == 0) || (o1 == 1 && o2 == 1)) && rn == 0b1111) { + uint32_t i = bit(insn1, 10); + uint32_t imm3 = bits(insn2, 12, 14); + uint32_t imm8 = bits(insn2, 0, 7); + uint32_t rd = bits(insn2, 8, 11); + uint32_t imm = (i << 11) | (imm3 << 8) | imm8; + + addr_t dst_vmaddr = 0; + if (o1 == 0 && o2 == 0) { // ADR - T3 on page F5-4098 + dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + } else if (o1 == 1 && o2 == 1) { // ADR - T2 on page F5-4097 + dst_vmaddr = relo_cur_src_vmaddr(ctx) - imm; + } else { + UNREACHABLE(); + } + + _ t2_ldr(Register::R(rd), MemOperand(pc, 4)); + _ t2_b(0); + _ EmitAddress(dst_vmaddr); + + is_insn_relocated = true; + } + } + } + + // Load/store single on page F3-3988 + // Load, unsigned (literal) on page F3-3992 + // Load, signed (literal) on page F3-3996 + // LDR literal (T2) + if ((insn1 & 0xff7f) == 0xf85f) { + uint32_t U = bit(insn1, 7); + uint32_t imm12 = bits(insn2, 0, 11); + uint16_t rt = bits(insn2, 12, 15); + + uint32_t imm = imm12; + + addr_t dst_vmaddr = 0; + if (U == 1) { + dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + } else { + dst_vmaddr = relo_cur_src_vmaddr(ctx) - imm; + } + + Register regRt = Register::R(rt); + + _ t2_ldr(regRt, MemOperand(pc, 8)); + _ t2_ldr(regRt, MemOperand(regRt, 0)); + _ t2_b(4); + _ EmitAddress(dst_vmaddr); + + is_insn_relocated = true; + } + + // if the insn not needed relocate, just rewrite the origin + if (!is_insn_relocated) { +#if 0 + if (relo_cur_src_vmaddr(ctx) % Thumb2_INST_LEN) + _ t1_nop(); +#endif + _ EmitInt16(insn1); + _ EmitInt16(insn2); + } +} + +void gen_arm_relocate_code(relo_ctx_t *ctx) { + +#undef _ +#define _ turbo_assembler_-> + auto turbo_assembler_ = static_cast(ctx->curr_assembler); +#define _ turbo_assembler_-> + + auto relocated_buffer = turbo_assembler_->GetCodeBuffer(); + + DEBUG_LOG("[arm] ARM relocate %d start >>>>>", ctx->buffer_size); + + while (ctx->buffer_cursor < ctx->buffer + ctx->buffer_size) { + uint32_t orig_off = ctx->buffer_cursor - ctx->buffer; + uint32_t relocated_off = relocated_buffer->GetBufferSize(); + ctx->relocated_offset_map[orig_off] = relocated_off; + + arm_inst_t insn = *(arm_inst_t *)ctx->buffer_cursor; + + int last_relo_offset = turbo_assembler_->GetCodeBuffer()->GetBufferSize(); + + ARMRelocateSingleInsn(ctx, insn); + DEBUG_LOG("[arm] Relocate arm insn: 0x%x", insn); + + // move to next instruction + ctx->buffer_cursor += ARM_INST_LEN; + + // execute state changed + addr32_t next_insn_addr = relo_cur_src_vmaddr(ctx) - ARM_PC_OFFSET; + if (check_execute_state_changed(ctx, next_insn_addr)) { + break; + } + } + + bool is_relocate_interrupted = ctx->buffer_cursor < ctx->buffer + ctx->buffer_size; + if (is_relocate_interrupted) { + turbo_assembler_->SetExecuteState(ThumbExecuteState); + } +} + +void gen_thumb_relocate_code(relo_ctx_t *ctx) { + int relocated_insn_count = 0; + + auto turbo_assembler_ = static_cast(ctx->curr_assembler); +#define _ turbo_assembler_-> + + auto relocated_buffer = turbo_assembler_->GetCodeBuffer(); + + DEBUG_LOG("[arm] Thumb relocate %d start >>>>>", ctx->buffer_size); + + while (ctx->buffer_cursor < ctx->buffer + ctx->buffer_size) { + uint32_t orig_off = ctx->buffer_cursor - ctx->buffer; + uint32_t relocated_off = relocated_buffer->GetBufferSize(); + ctx->relocated_offset_map[orig_off] = relocated_off; + + // align nop + _ t1_nop(); + + thumb2_inst_t insn = *(thumb2_inst_t *)ctx->buffer_cursor; + + int last_relo_offset = relocated_buffer->GetBufferSize(); + if (is_thumb2(insn)) { + Thumb2RelocateSingleInsn(ctx, (uint16_t)insn, (uint16_t)(insn >> 16)); + DEBUG_LOG("[arm] Relocate thumb2 insn: 0x%x", insn); + } else { + Thumb1RelocateSingleInsn(ctx, (uint16_t)insn); + DEBUG_LOG("[arm] Relocate thumb1 insn: 0x%x", (uint16_t)insn); + } + + // Move to next instruction + if (is_thumb2(insn)) { + ctx->buffer_cursor += Thumb2_INST_LEN; + } else { + ctx->buffer_cursor += Thumb1_INST_LEN; + } + + // execute state changed + addr32_t next_insn_addr = relo_cur_src_vmaddr(ctx) - Thumb_PC_OFFSET; + if (check_execute_state_changed(ctx, next_insn_addr)) { + break; + } + } + + // .thumb1 bx pc + // .thumb1 mov r8, r8 + // .arm ldr pc, [pc, #-4] + + bool is_relocate_interrupted = ctx->buffer_cursor < ctx->buffer + ctx->buffer_size; + if (is_relocate_interrupted) { + turbo_assembler_->SetExecuteState(ARMExecuteState); + } +} + +void GenRelocateCode(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) { + relo_ctx_t ctx; + + if ((addr_t)buffer % 2) { + ctx.start_state = ThumbExecuteState; + ctx.curr_state = ThumbExecuteState; + // remove thumb address flag + buffer = (void *)((addr_t)buffer - 1); + } else { + ctx.start_state = ARMExecuteState; + ctx.curr_state = ARMExecuteState; + } + + ctx.buffer = ctx.buffer_cursor = (uint8_t *)buffer; + ctx.buffer_size = origin->size; + + ctx.src_vmaddr = (addr_t)origin->addr; + ctx.dst_vmaddr = 0; + + auto *relocated_buffer = new CodeBuffer(); + ctx.relocated_buffer = relocated_buffer; + + ThumbTurboAssembler thumb_turbo_assembler_(0, ctx.relocated_buffer); +#define thumb_ thumb_turbo_assembler_. + TurboAssembler arm_turbo_assembler_(0, ctx.relocated_buffer); +#define arm_ arm_turbo_assembler_. + + if (ctx.start_state == ThumbExecuteState) + ctx.curr_assembler = &thumb_turbo_assembler_; + else + ctx.curr_assembler = &arm_turbo_assembler_; + +relocate_remain: + if (ctx.curr_state == ThumbExecuteState) { + ctx.curr_assembler = &thumb_turbo_assembler_; + gen_thumb_relocate_code(&ctx); + if (thumb_turbo_assembler_.GetExecuteState() == ARMExecuteState) { + // translate interrupt as execute state changed + bool is_translate_interrupted = ctx.buffer_cursor < ctx.buffer + ctx.buffer_size; + if (is_translate_interrupted) { + // add nop to align ARM + if (thumb_turbo_assembler_.pc_offset() % 4) + thumb_turbo_assembler_.t1_nop(); + goto relocate_remain; + } + } + } else { + ctx.curr_assembler = &arm_turbo_assembler_; + gen_arm_relocate_code(&ctx); + if (arm_turbo_assembler_.GetExecuteState() == ThumbExecuteState) { + bool is_translate_interrupted = ctx.buffer_cursor < ctx.buffer + ctx.buffer_size; + // translate interrupt as execute state changed + if (is_translate_interrupted) { + goto relocate_remain; + } + } + } + + // update origin + int new_origin_len = (addr_t)ctx.buffer_cursor - (addr_t)ctx.buffer; + origin->reset(origin->addr, new_origin_len); + + // TODO: if last insn is unlink branch, skip + if (branch) { + if (ctx.curr_state == ThumbExecuteState) { + // branch to the rest of instructions + thumb_ AlignThumbNop(); + thumb_ t2_ldr(pc, MemOperand(pc, 0)); + // get the real branch address + thumb_ EmitAddress(origin->addr + origin->size + THUMB_ADDRESS_FLAG); + } else { + // branch to the rest of instructions + CodeGen codegen(&arm_turbo_assembler_); + // get the real branch address + codegen.LiteralLdrBranch(origin->addr + origin->size); + } + } + + // fixup the insn branch into trampoline(has been modified) + arm_turbo_assembler_.RelocLabelFixup(&ctx.relocated_offset_map); + thumb_turbo_assembler_.RelocLabelFixup(&ctx.relocated_offset_map); + + // realize all the pseudo data label + thumb_turbo_assembler_.RelocBind(); + arm_turbo_assembler_.RelocBind(); + + // generate executable code + { + // assembler without specific memory address + auto relocated_mem = MemoryAllocator::SharedAllocator()->allocateExecMemory(relocated_buffer->GetBufferSize()); + if (relocated_mem == nullptr) + return; + + thumb_turbo_assembler_.SetRealizedAddress((void *)relocated_mem); + arm_turbo_assembler_.SetRealizedAddress((void *)relocated_mem); + + AssemblyCode *code = NULL; + code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(ctx.curr_assembler); + relocated->reset(code->addr, code->size); + } + + // thumb + if (ctx.start_state == ThumbExecuteState) { + // add thumb address flag + relocated->reset(relocated->addr + THUMB_ADDRESS_FLAG, relocated->size); + } + + // clean + { + thumb_turbo_assembler_.ClearCodeBuffer(); + arm_turbo_assembler_.ClearCodeBuffer(); + + delete relocated_buffer; + } +} + +void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated) { + GenRelocateCode(buffer, origin, relocated, true); +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.h new file mode 100644 index 0000000..ed289aa --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.h @@ -0,0 +1,307 @@ +#pragma once +#include "dobby/dobby_internal.h" + +#include "core/arch/arm/constants-arm.h" +#include "core/assembler/assembler-arm.h" + +namespace zz { +namespace arm { + +// thumb1/thumb2 pseudo label type, only support Thumb1-Ldr | Thumb2-Ldr +enum ref_label_type_t { kThumb1Ldr, kThumb2LiteralLdr }; + +// custom thumb pseudo label for thumb/thumb2 +class ThumbPseudoLabel : public AssemblerPseudoLabel { +public: + ThumbPseudoLabel(addr_t addr) : AssemblerPseudoLabel(addr) { + } + + // fix the instruction which not link to the label yet. + void link_confused_instructions(CodeBuffer *buffer) { + CodeBuffer *_buffer; + if (buffer) + _buffer = buffer; + + for (auto &ref_label_insn : ref_label_insns_) { + // instruction offset to label + thumb2_inst_t insn = _buffer->LoadThumb2Inst(ref_label_insn.pc_offset); + thumb1_inst_t insn1 = _buffer->LoadThumb1Inst(ref_label_insn.pc_offset); + thumb1_inst_t insn2 = _buffer->LoadThumb1Inst(ref_label_insn.pc_offset + sizeof(thumb1_inst_t)); + + switch (ref_label_insn.link_type) { + case kThumb1Ldr: { + UNREACHABLE(); + } break; + case kThumb2LiteralLdr: { + int64_t pc = ref_label_insn.pc_offset + Thumb_PC_OFFSET; + assert(pc % 4 == 0); + int32_t imm12 = pos() - pc; + + if (imm12 > 0) { + set_bit(insn1, 7, 1); + } else { + set_bit(insn1, 7, 0); + imm12 = -imm12; + } + set_bits(insn2, 0, 11, imm12); + _buffer->RewriteThumb1Inst(ref_label_insn.pc_offset, insn1); + _buffer->RewriteThumb1Inst(ref_label_insn.pc_offset + Thumb1_INST_LEN, insn2); + + DEBUG_LOG("[thumb label link] insn offset %d link offset %d", ref_label_insn.pc_offset, imm12); + } break; + default: + UNREACHABLE(); + break; + } + } + } +}; + +class ThumbRelocLabelEntry : public ThumbPseudoLabel, public RelocLabel { +public: + ThumbRelocLabelEntry(bool is_pc_register) : RelocLabel(), ThumbPseudoLabel(0), is_pc_register_(is_pc_register) { + } + + template static ThumbRelocLabelEntry *withData(T value, bool is_pc_register) { + auto label = new ThumbRelocLabelEntry(is_pc_register); + label->setData(value); + return label; + } + + bool is_pc_register() { + return is_pc_register_; + } + +private: + bool is_pc_register_; +}; + +// --- + +class ThumbAssembler : public Assembler { +public: + ThumbAssembler(void *address) : Assembler(address) { + this->SetExecuteState(ThumbExecuteState); + } + + ThumbAssembler(void *address, CodeBuffer *buffer) : Assembler(address, buffer) { + this->SetExecuteState(ThumbExecuteState); + } + + void EmitInt16(int16_t val) { + buffer_->Emit16(val); + } + + void Emit2Int16(int16_t val1, int16_t val2) { + EmitInt16(val1); + EmitInt16(val2); + } + + void EmitAddress(uint32_t value) { + buffer_->Emit32(value); + } + + // ===== + void t1_nop() { + EmitInt16(0xbf00); + } + void t1_b(int32_t imm) { + ASSERT(CheckSignLength(imm, 12)); + ASSERT(CheckAlign(imm, 2)); + + int32_t imm11 = bits(imm >> 1, 0, 10); + EmitInt16(0xe000 | imm11); + } + + // ===== + void t2_b(uint32_t imm) { + EmitThumb2Branch(AL, imm, false); + } + void t2_bl(uint32_t imm) { + EmitThumb2Branch(AL, imm, true); + } + void t2_blx(uint32_t imm) { + UNIMPLEMENTED(); + } + + // ===== + void t2_ldr(Register dst, const MemOperand &src) { + // WARNNING: literal ldr, base = ALIGN(pc, 4) + EmitThumb2LoadStore(true, dst, src); + } + +private: + void EmitThumb2LoadLiteral(Register rt, const MemOperand x) { + bool add = true; + uint32_t U, imm12; + int32_t offset = x.offset(); + +#if 0 + // literal ldr, base = ALIGN(pc, 4) + if (rt.Is(pc)) { + // TODO: convert to `GetRealizedAddress()` ??? + addr_t curr_pc = pc_offset() + (addr_t)GetRealizedAddress(); + if (curr_pc % 4) { + t1_nop(); + } + } +#endif + + if (offset > 0) { + U = B7; + imm12 = offset; + } else { + U = 0; + imm12 = -offset; + } + EmitInt16(0xf85f | U); + EmitInt16(0x0 | (rt.code() << 12) | imm12); + } + void EmitThumb2LoadStore(bool load, Register rt, const MemOperand x) { + if (x.rn().Is(pc)) { + EmitThumb2LoadLiteral(rt, x); + return; + } + + bool index, add, wback; + if (x.IsRegisterOffset() && x.offset() >= 0) { + index = true, add = true, wback = false; + uint32_t imm12 = x.offset(); + EmitInt16(0xf8d0 | (x.rn().code() << 0)); + EmitInt16(0x0 | (rt.code() << 12) | imm12); + } else { + // use bit accelerate + uint32_t P = 0, W = 0, U = 0; + uint32_t imm8 = x.offset() > 0 ? x.offset() : -x.offset(); + U = x.offset() > 0 ? 0 : B9; + if (x.IsPostIndex()) { + P = 0, W = B8; + } else if (x.IsPreIndex()) { + P = B10, W = B8; + } + index = (P == B10); + add = (U == B9); + wback = (W == B8); + EmitInt16(0xf850 | (x.rn().code() << 0)); + EmitInt16(0x0800 | (rt.code() << 12) | P | U | W | imm8); + } + } + + void EmitThumb2Branch(Condition cond, int32_t imm, bool link) { + uint32_t operand = imm >> 1; + ASSERT(CheckSignLength(operand, 25)); + ASSERT(CheckAlign(operand, 2)); + + uint32_t signbit = (imm >> 31) & 0x1; + uint32_t i1 = (operand >> 22) & 0x1; + uint32_t i2 = (operand >> 21) & 0x1; + uint32_t imm10 = (operand >> 11) & 0x03ff; + uint32_t imm11 = operand & 0x07ff; + uint32_t j1 = (!(i1 ^ signbit)); + uint32_t j2 = (!(i2 ^ signbit)); + + if (cond != AL) { + UNIMPLEMENTED(); + } + + EmitInt16(0xf000 | LeftShift(signbit, 1, 10) | LeftShift(imm10, 10, 0)); + if (link) { + // Not use LeftShift(1, 1, 14), and use B14 for accelerate + EmitInt16(0x9000 | LeftShift(j1, 1, 13) | (LeftShift(j2, 1, 11)) | LeftShift(imm11, 11, 0) | B14); + } else { + EmitInt16(0x9000 | LeftShift(j1, 1, 13) | (LeftShift(j2, 1, 11)) | LeftShift(imm11, 11, 0)); + } + } +}; + +// --- + +class ThumbTurboAssembler : public ThumbAssembler { +public: + ThumbTurboAssembler(void *address) : ThumbAssembler(address) { + } + + ThumbTurboAssembler(void *address, CodeBuffer *buffer) : ThumbAssembler(address, buffer) { + } + + ~ThumbTurboAssembler() { + } + + void T1_Ldr(Register rt, ThumbPseudoLabel *label) { + UNREACHABLE(); + +// t1_ldr: rt can't be PC register +// === +#if 0 + if (label->is_bound()) { + const int64_t dest = label->pos() - buffer_.Size(); + ldr(rt, MemOperand(pc, dest)); + } else { + // record this ldr, and fix later. + label->link_to(buffer_.Size(), ThumbPseudoLabel::kThumb1Ldr); + ldr(rt, MemOperand(pc, 0)); + } +#endif + } + + void T2_Ldr(Register rt, ThumbPseudoLabel *label) { + if (label->pos()) { + int offset = label->pos() - buffer_->GetBufferSize(); + t2_ldr(rt, MemOperand(pc, offset)); + } else { + // record this ldr, and fix later. + label->link_to(kThumb2LiteralLdr, buffer_->GetBufferSize()); + t2_ldr(rt, MemOperand(pc, 0)); + } + } + + void AlignThumbNop() { + addr32_t pc = this->GetCodeBuffer()->GetBufferSize() + (uintptr_t)GetRealizedAddress(); + if (pc % Thumb2_INST_LEN) { + t1_nop(); + } else { + } + } + + // --- + + void PseudoBind(ThumbPseudoLabel *label) { + const addr_t bound_pc = buffer_->GetBufferSize(); + label->bind_to(bound_pc); + // If some instructions have been wrote, before the label bound, we need link these `confused` instructions + if (label->has_confused_instructions()) { + label->link_confused_instructions(GetCodeBuffer()); + } + } + + void RelocBind() { + for (auto *data_label : data_labels_) { + PseudoBind(data_label); + reinterpret_cast(buffer_)->EmitBuffer(data_label->data_, data_label->data_size_); + } + } + + void AppendRelocLabel(ThumbRelocLabelEntry *label) { + data_labels_.push_back(label); + } + + void RelocLabelFixup(tinystl::unordered_map *relocated_offset_map) { + for (auto *data_label : data_labels_) { + auto val = data_label->data(); + auto iter = relocated_offset_map->find(val); + if (iter != relocated_offset_map->end()) { + data_label->fixupData(iter->second); + } + } + } + +private: + tinystl::vector data_labels_; +}; + +#if 0 +void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated); +#endif + +} // namespace arm +} // namespace zz diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.cc b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.cc new file mode 100644 index 0000000..899ac9e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.cc @@ -0,0 +1,366 @@ +#include "platform_detect_macro.h" + +#if defined(TARGET_ARCH_ARM64) + +#include "InstructionRelocation/arm64/InstructionRelocationARM64.h" + +#include "dobby/dobby_internal.h" + +#include "core/arch/arm64/registers-arm64.h" +#include "core/assembler/assembler-arm64.h" +#include "core/codegen/codegen-arm64.h" + +#include "inst_constants.h" +#include "inst_decode_encode_kit.h" + +using namespace zz::arm64; + +#if defined(DOBBY_DEBUG) +#define debug_nop() _ nop() +#else +#define debug_nop() +#endif + +#define arm64_trunc_page(x) ((x) & (~(0x1000 - 1))) +#define arm64_round_page(x) trunc_page((x) + (0x1000 - 1)) + +typedef struct { + addr_t mapped_addr; + + uint8_t *buffer; + uint8_t *buffer_cursor; + size_t buffer_size; + + addr_t src_vmaddr; + addr_t dst_vmaddr; + + CodeMemBlock *origin; + CodeMemBlock *relocated; + + tinystl::unordered_map relocated_offset_map; + + tinystl::unordered_map label_map; + +} relo_ctx_t; + +// --- + +addr_t relo_cur_src_vmaddr(relo_ctx_t *ctx) { + return ctx->src_vmaddr + (ctx->buffer_cursor - ctx->buffer); +} + +addr_t relo_cur_dst_vmaddr(relo_ctx_t *ctx, TurboAssembler *assembler) { + return ctx->dst_vmaddr + assembler->GetCodeBuffer()->GetBufferSize(); +} + +addr_t relo_src_offset_to_vmaddr(relo_ctx_t *ctx, off_t offset) { + return ctx->src_vmaddr + offset; +} + +addr_t relo_dst_offset_to_vmaddr(relo_ctx_t *ctx, off_t offset) { + return ctx->dst_vmaddr + offset; +} + +// --- + +#if 0 +bool has_relo_label_at(relo_ctx_t *ctx, addr_t addr) { + if (ctx->label_map.count(addr)) { + return true; + } + return false; +} + +AssemblerPseudoLabel *relo_label_create_or_get(relo_ctx_t *ctx, addr_t addr) { + if (!ctx->label_map.count(addr)) { + auto *label = new AssemblerPseudoLabel(addr); + ctx->label_map[addr] = label; + } + return ctx->label_map[addr]; +} + +int64_t relo_label_link_offset(relo_ctx_t *ctx, pcrel_type_t pcrel_type, int64_t offset) { + auto is_offset_undefined = [ctx](int64_t offset) -> bool { + if (ctx->buffer_cursor + offset < ctx->buffer || ctx->buffer_cursor + offset > ctx->buffer + ctx->buffer_size) { + return true; + } + return false; + }; + + auto is_offset_uninitialized = [ctx](int64_t offset) -> bool { + if (ctx->buffer_cursor + offset > ctx->buffer && ctx->buffer_cursor + offset < ctx->buffer + ctx->buffer_size) { + if (!ctx->relocated_offset_map.count(ctx->buffer_cursor + offset - ctx->buffer_cursor)) + return true; + } + return false; + }; + + addr_t label_vmaddr = relo_cur_src_vmaddr(ctx) + offset; + if (pcrel_type == RELO_ARM64_RELOC_PAGE21) { + label_vmaddr = arm64_trunc_page(label_vmaddr); + } + + auto *label = relo_label_create_or_get(ctx, label_vmaddr); + if (is_offset_undefined(offset)) { // pc relative target is beyond our scope + label->link_to(AssemblerPseudoLabel::kLabelImm19, relo_cur_src_vmaddr(ctx), (addr_t)ctx->buffer_cursor - ctx->mapped_addr); + return 0; + } else if (is_offset_uninitialized(offset)) { // pc relative target is in our control, but not handle yet + label->link_to(AssemblerPseudoLabel::kLabelImm19, relo_cur_src_vmaddr(ctx), (addr_t)ctx->buffer_cursor - ctx->mapped_addr); + return 0; + } else { // pc relative target is already handled + off_t off = ctx->buffer_cursor + offset - ctx->buffer; + off_t relocated_off = label->pos(); + int64_t new_offset = relo_dst_offset_to_vmaddr(ctx, relocated_off) - relo_src_offset_to_vmaddr(ctx, off); + return new_offset; + } +} +#endif + +// --- + +static inline bool inst_is_b_bl(uint32_t instr) { + return (instr & UnconditionalBranchFixedMask) == UnconditionalBranchFixed; +} + +static inline bool inst_is_ldr_literal(uint32_t instr) { + return ((instr & LoadRegLiteralFixedMask) == LoadRegLiteralFixed); +} + +static inline bool inst_is_adr(uint32_t instr) { + return (instr & PCRelAddressingFixedMask) == PCRelAddressingFixed && (instr & PCRelAddressingMask) == ADR; +} + +static inline bool inst_is_adrp(uint32_t instr) { + return (instr & PCRelAddressingFixedMask) == PCRelAddressingFixed && (instr & PCRelAddressingMask) == ADRP; +} + +static inline bool inst_is_b_cond(uint32_t instr) { + return (instr & ConditionalBranchFixedMask) == ConditionalBranchFixed; +} + +static inline bool inst_is_compare_b(uint32_t instr) { + return (instr & CompareBranchFixedMask) == CompareBranchFixed; +} + +static inline bool inst_is_test_b(uint32_t instr) { + return (instr & TestBranchFixedMask) == TestBranchFixed; +} + +// --- + +int relo_relocate(relo_ctx_t *ctx, bool branch) { + int relocated_insn_count = 0; + + TurboAssembler turbo_assembler_(0); +#define _ turbo_assembler_. + + auto relocated_buffer = turbo_assembler_.GetCodeBuffer(); + + while (ctx->buffer_cursor < ctx->buffer + ctx->buffer_size) { + uint32_t orig_off = ctx->buffer_cursor - ctx->buffer; + uint32_t relocated_off = relocated_buffer->GetBufferSize(); + ctx->relocated_offset_map[orig_off] = relocated_off; + +#if 0 + addr_t inst_vmaddr = 0; + inst_vmaddr = relo_cur_src_vmaddr(ctx); + if (has_relo_label_at(ctx, inst_vmaddr)) { + auto *label = relo_label_create_or_get(ctx, inst_vmaddr); + label->bind_to(inst_vmaddr); + } +#endif + + arm64_inst_t inst = *(arm64_inst_t *)ctx->buffer_cursor; + if (inst_is_b_bl(inst)) { + DEBUG_LOG("%d:relo at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx)); + + int64_t offset = decode_imm26_offset(inst); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset; + + auto dst_label = RelocLabel::withData(dst_vmaddr); + _ AppendRelocLabel(dst_label); + + { + _ Ldr(TMP_REG_0, dst_label); + if ((inst & UnconditionalBranchMask) == BL) { + _ blr(TMP_REG_0); + } else { + _ br(TMP_REG_0); + } + } + + } else if (inst_is_ldr_literal(inst)) { + DEBUG_LOG("%d:relo at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx)); + + int64_t offset = decode_imm19_offset(inst); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset; + + int rt = decode_rt(inst); + char opc = bits(inst, 30, 31); + + { + _ Mov(TMP_REG_0, dst_vmaddr); + if (opc == 0b00) + _ ldr(W(rt), MemOperand(TMP_REG_0, 0)); + else if (opc == 0b01) + _ ldr(X(rt), MemOperand(TMP_REG_0, 0)); + else { + UNIMPLEMENTED(); + } + } + } else if (inst_is_adr(inst)) { + DEBUG_LOG("%d:relo at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx)); + + int64_t offset = decode_immhi_immlo_offset(inst); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset; + + int rd = decode_rd(inst); + + { + _ Mov(X(rd), dst_vmaddr); + ; + } + } else if (inst_is_adrp(inst)) { + DEBUG_LOG("%d:relo at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx)); + + int64_t offset = decode_immhi_immlo_zero12_offset(inst); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset; + dst_vmaddr = arm64_trunc_page(dst_vmaddr); + + int rd = decode_rd(inst); + + { + _ Mov(X(rd), dst_vmaddr); + ; + } + } else if (inst_is_b_cond(inst)) { + DEBUG_LOG("%d:relo at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx)); + + int64_t offset = decode_imm19_offset(inst); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset; + + arm64_inst_t branch_instr = inst; + { + char cond = bits(inst, 0, 3); + cond = cond ^ 1; + set_bits(branch_instr, 0, 3, cond); + + int64_t offset = 4 * 3; + uint32_t imm19 = offset >> 2; + set_bits(branch_instr, 5, 23, imm19); + } + + auto dst_label = RelocLabel::withData(dst_vmaddr); + _ AppendRelocLabel(dst_label); + + { + _ Emit(branch_instr); + { + _ Ldr(TMP_REG_0, dst_label); + _ br(TMP_REG_0); + } + } + } else if (inst_is_compare_b(inst)) { + DEBUG_LOG("%d:relo at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx)); + + int64_t offset = decode_imm19_offset(inst); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset; + + arm64_inst_t branch_instr = inst; + { + char op = bit(inst, 24); + op = op ^ 1; + set_bit(branch_instr, 24, op); + + int64_t offset = 4 * 3; + uint32_t imm19 = offset >> 2; + set_bits(branch_instr, 5, 23, imm19); + } + + auto dst_label = RelocLabel::withData(dst_vmaddr); + _ AppendRelocLabel(dst_label); + + { + _ Emit(branch_instr); + { + _ Ldr(TMP_REG_0, dst_label); + _ br(TMP_REG_0); + } + } + } else if (inst_is_test_b(inst)) { + DEBUG_LOG("%d:relo at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx)); + + int64_t offset = decode_imm14_offset(inst); + addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset; + + arm64_inst_t branch_instr = inst; + { + char op = bit(inst, 24); + op = op ^ 1; + set_bit(branch_instr, 24, op); + + int64_t offset = 4 * 3; + uint32_t imm14 = offset >> 2; + set_bits(branch_instr, 5, 18, imm14); + } + + auto dst_label = RelocLabel::withData(dst_vmaddr); + _ AppendRelocLabel(dst_label); + + { + _ Emit(branch_instr); + { + _ Ldr(TMP_REG_0, dst_label); + _ br(TMP_REG_0); + } + } + } else { + _ Emit(inst); + } + + ctx->buffer_cursor += sizeof(arm64_inst_t); + } +#undef _ + + // update origin + int new_origin_len = (addr_t)ctx->buffer_cursor - (addr_t)ctx->buffer; + ctx->origin->reset(ctx->origin->addr, new_origin_len); + + // TODO: if last instr is unlink branch, ignore it + if (branch) { + CodeGen codegen(&turbo_assembler_); + codegen.LiteralLdrBranch(ctx->origin->addr + ctx->origin->size); + } + + // Bind all labels + turbo_assembler_.RelocBind(); + + // Generate executable code + { + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + ctx->relocated = code; + } + return 0; +} + +void GenRelocateCode(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) { + relo_ctx_t ctx = {0}; + + ctx.buffer = ctx.buffer_cursor = (uint8_t *)buffer; + ctx.buffer_size = origin->size; + + ctx.src_vmaddr = (addr_t)origin->addr; + ctx.dst_vmaddr = (addr_t)relocated->addr; + + ctx.origin = origin; + + relo_relocate(&ctx, branch); + + relocated->reset(ctx.relocated->addr, ctx.relocated->size); +} + +void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated) { + GenRelocateCode(buffer, origin, relocated, true); +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.h new file mode 100644 index 0000000..226d3ad --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.h @@ -0,0 +1,13 @@ +#pragma once + +#include "dobby/dobby_internal.h" + +#include "core/arch/arm64/constants-arm64.h" + +#if 0 +namespace zz { +namespace arm64 { +void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated); +} // namespace arm64 +} // namespace zz +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_constants.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_constants.h new file mode 100644 index 0000000..228a952 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_constants.h @@ -0,0 +1,49 @@ +#pragma once + +#if 0 +enum LoadRegLiteralOp { + LoadRegLiteralFixed = 0x18000000, + LoadRegLiteralFixedMask = 0x3B000000, + LoadRegLiteralMask = 0xFF000000, +}; + +// PC relative addressing. +enum PCRelAddressingOp { + PCRelAddressingFixed = 0x10000000, + PCRelAddressingFixedMask = 0x1F000000, + PCRelAddressingMask = 0x9F000000, + ADR = PCRelAddressingFixed | 0x00000000, + ADRP = PCRelAddressingFixed | 0x80000000 +}; + +// Unconditional branch. +enum UnconditionalBranchOp { + UnconditionalBranchFixed = 0x14000000, + UnconditionalBranchFixedMask = 0x7C000000, + UnconditionalBranchMask = 0xFC000000, + + B = UnconditionalBranchFixed | 0x00000000, + BL = UnconditionalBranchFixed | 0x80000000 +}; +#endif + +// Compare and branch. +enum CompareBranchOp { + CompareBranchFixed = 0x34000000, + CompareBranchFixedMask = 0x7E000000, + CompareBranchMask = 0xFF000000, +}; + +// Conditional branch. +enum ConditionalBranchOp { + ConditionalBranchFixed = 0x54000000, + ConditionalBranchFixedMask = 0xFE000000, + ConditionalBranchMask = 0xFF000010, +}; + +// Test and branch. +enum TestBranchOp { + TestBranchFixed = 0x36000000, + TestBranchFixedMask = 0x7E000000, + TestBranchMask = 0x7F000000, +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_decode_encode_kit.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_decode_encode_kit.h new file mode 100644 index 0000000..cfae889 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_decode_encode_kit.h @@ -0,0 +1,110 @@ +#pragma once + +#include "dobby/common.h" + +static inline int64_t SignExtend(unsigned long x, int M, int N) { +#if 1 + char sign_bit = bit(x, M - 1); + unsigned long sign_mask = 0 - sign_bit; + x |= ((sign_mask >> M) << M); +#else + x = (long)((long)x << (N - M)) >> (N - M); +#endif + return (int64_t)x; +} + +static inline int64_t decode_imm14_offset(uint32_t instr) { + int64_t offset; + { + int64_t imm14 = bits(instr, 5, 18); + offset = (imm14 << 2); + } + offset = SignExtend(offset, 2 + 14, 64); + return offset; +} +static inline uint32_t encode_imm14_offset(uint32_t instr, int64_t offset) { + uint32_t imm14 = bits((offset >> 2), 0, 13); + set_bits(instr, 5, 18, imm14); + return instr; +} + +static inline int64_t decode_imm19_offset(uint32_t instr) { + int64_t offset; + { + int64_t imm19 = bits(instr, 5, 23); + offset = (imm19 << 2); + } + offset = SignExtend(offset, 2 + 19, 64); + return offset; +} + +static inline uint32_t encode_imm19_offset(uint32_t instr, int64_t offset) { + uint32_t imm19 = bits((offset >> 2), 0, 18); + set_bits(instr, 5, 23, imm19); + return instr; +} + +static inline int64_t decode_imm26_offset(uint32_t instr) { + int64_t offset; + { + int64_t imm26 = bits(instr, 0, 25); + offset = (imm26 << 2); + } + offset = SignExtend(offset, 2 + 26, 64); + return offset; +} +static inline uint32_t encode_imm26_offset(uint32_t instr, int64_t offset) { + uint32_t imm26 = bits((offset >> 2), 0, 25); + set_bits(instr, 0, 25, imm26); + return instr; +} + +static inline int64_t decode_immhi_immlo_offset(uint32_t instr) { + typedef uint32_t instr_t; + struct { + instr_t Rd : 5; // Destination register + instr_t immhi : 19; // 19-bit upper immediate + instr_t dummy_0 : 5; // Must be 10000 == 0x10 + instr_t immlo : 2; // 2-bit lower immediate + instr_t op : 1; // 0 = ADR, 1 = ADRP + } instr_decode; + + *(instr_t *)&instr_decode = instr; + + int64_t imm = instr_decode.immlo + (instr_decode.immhi << 2); + imm = SignExtend(imm, 2 + 19, 64); + return imm; +} +static inline uint32_t encode_immhi_immlo_offset(uint32_t instr, int64_t offset) { + struct { + uint32_t Rd : 5; // Destination register + uint32_t immhi : 19; // 19-bit upper immediate + uint32_t dummy_0 : 5; // Must be 10000 == 0x10 + uint32_t immlo : 2; // 2-bit lower immediate + uint32_t op : 1; // 0 = ADR, 1 = ADRP + } instr_decode; + + *(uint32_t *)&instr_decode = instr; + instr_decode.immlo = bits(offset, 0, 2); + instr_decode.immhi = bits(offset, 2, 2 + 19); + + return *(uint32_t *)&instr_decode; +} + +static inline int64_t decode_immhi_immlo_zero12_offset(uint32_t instr) { + int64_t imm = decode_immhi_immlo_offset(instr); + imm = imm << 12; + return imm; +} +static inline uint32_t encode_immhi_immlo_zero12_offset(uint32_t instr, int64_t offset) { + offset = (offset >> 12); + return encode_immhi_immlo_offset(instr, offset); +} + +static inline int decode_rt(uint32_t instr) { + return bits(instr, 0, 4); +} + +static inline int decode_rd(uint32_t instr) { + return bits(instr, 0, 4); +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.cc b/app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.cc new file mode 100644 index 0000000..d9d78fd --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.cc @@ -0,0 +1,79 @@ +#include "platform_detect_macro.h" + +#if defined(TARGET_ARCH_X64) + +#include "dobby/dobby_internal.h" + +#include "InstructionRelocation/x64/InstructionRelocationX64.h" +#include "InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h" + +#include "core/arch/x64/registers-x64.h" +#include "core/assembler/assembler-x64.h" +#include "core/codegen/codegen-x64.h" + +using namespace zz::x64; + +int GenRelocateCodeFixed(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) { + TurboAssembler turbo_assembler_(0); + // Set fixed executable code chunk address + turbo_assembler_.SetRealizedAddress((void *)relocated->addr); +#define _ turbo_assembler_. +#define __ turbo_assembler_.GetCodeBuffer()-> + + auto curr_orig_ip = (addr64_t)origin->addr; + auto curr_relo_ip = (addr64_t)relocated->addr; + + auto buffer_cursor = (uint8_t *)buffer; + + int predefined_relocate_size = origin->size; + + while ((buffer_cursor < ((uint8_t *)buffer + predefined_relocate_size))) { + x86_insn_decode_t insn = {0}; + memset(&insn, 0, sizeof(insn)); + GenRelocateSingleX86Insn(curr_orig_ip, curr_relo_ip, buffer_cursor, &turbo_assembler_, + turbo_assembler_.GetCodeBuffer(), insn, 64); + + // go next + curr_orig_ip += insn.length; + buffer_cursor += insn.length; + curr_relo_ip = (addr64_t)relocated->addr + turbo_assembler_.ip_offset(); + } + + // jmp to the origin rest instructions + if (branch) { + CodeGen codegen(&turbo_assembler_); + // TODO: 6 == jmp [RIP + disp32] instruction size + addr64_t stub_addr = curr_relo_ip + 6; + codegen.JmpNearIndirect(stub_addr); + turbo_assembler_.GetCodeBuffer()->Emit64(curr_orig_ip); + } + + // update origin + int new_origin_len = curr_orig_ip - (addr_t)origin->addr; + origin->reset(origin->addr, new_origin_len); + + int relo_len = turbo_assembler_.GetCodeBuffer()->GetBufferSize(); + if (relo_len > relocated->size) { + DEBUG_LOG("pre-alloc code chunk not enough"); + return -1; + } + + // generate executable code + { + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + relocated->reset(code->addr, code->size); + delete code; + } + + return 0; +} + +void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated) { + GenRelocateCode(buffer, origin, relocated, true); +} + +void GenRelocateCode(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) { + GenRelocateCodeX86Shared(buffer, origin, relocated, branch); +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.h new file mode 100644 index 0000000..bad483c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.h @@ -0,0 +1,9 @@ +#pragma once + +#include "dobby/common.h" + +#include "core/arch/x64/constants-x64.h" + +#include "MemoryAllocator/AssemblyCodeBuilder.h" + +#include "InstructionRelocation/x86/InstructionRelocationX86Shared.h" diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.cc b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.cc new file mode 100644 index 0000000..2e84552 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.cc @@ -0,0 +1,80 @@ +#include "platform_detect_macro.h" + +#if defined(TARGET_ARCH_IA32) + +#include "dobby/dobby_internal.h" + +#include "InstructionRelocation/x86/InstructionRelocationX86.h" +#include "InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h" + +#include "core/arch/x86/registers-x86.h" +#include "core/assembler/assembler-ia32.h" +#include "core/codegen/codegen-ia32.h" + +using namespace zz::x86; + +int GenRelocateCodeFixed(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) { + TurboAssembler turbo_assembler_(0); + // Set fixed executable code chunk address + turbo_assembler_.SetRealizedAddress((void *)relocated->addr); +#define _ turbo_assembler_. +#define __ turbo_assembler_.GetCodeBuffer()-> + + auto curr_orig_ip = (addr32_t)origin->addr; + auto curr_relo_ip = (addr32_t)relocated->addr; + + uint8_t *buffer_cursor = (uint8_t *)buffer; + + x86_options_t conf = {0}; + conf.mode = 32; + + int predefined_relocate_size = origin->size; + + while ((buffer_cursor < ((uint8_t *)buffer + predefined_relocate_size))) { + x86_insn_decode_t insn = {0}; + memset(&insn, 0, sizeof(insn)); + GenRelocateSingleX86Insn(curr_orig_ip, curr_relo_ip, buffer_cursor, &turbo_assembler_, + turbo_assembler_.GetCodeBuffer(), insn, 64); + + // go next + curr_orig_ip += insn.length; + buffer_cursor += insn.length; + curr_relo_ip = (addr32_t)relocated->addr + turbo_assembler_.ip_offset(); + } + + // jmp to the origin rest instructions + if (branch) { + CodeGen codegen(&turbo_assembler_); + addr32_t stub_addr = curr_relo_ip + 6; + codegen.JmpNear(curr_orig_ip); + } + + // update origin + int new_origin_len = curr_orig_ip - (addr_t)origin->addr; + origin->reset(origin->addr, new_origin_len); + + int relo_len = turbo_assembler_.GetCodeBuffer()->GetBufferSize(); + if (relo_len > relocated->size) { + DEBUG_LOG("pre-alloc code chunk not enough"); + return -1; + } + + // generate executable code + { + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + relocated->reset(code->addr, code->size); + delete code; + } + + return 0; +} + +void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated) { + GenRelocateCode(buffer, origin, relocated, true); +} + +void GenRelocateCode(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) { + GenRelocateCodeX86Shared(buffer, origin, relocated, branch); +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.h new file mode 100644 index 0000000..b554e48 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.h @@ -0,0 +1,9 @@ +#pragma once + +#include "dobby/common.h" + +#include "core/arch/x86/constants-x86.h" + +#include "MemoryAllocator/AssemblyCodeBuilder.h" + +#include "InstructionRelocation/x86/InstructionRelocationX86Shared.h" diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.cc b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.cc new file mode 100644 index 0000000..7df7038 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.cc @@ -0,0 +1,218 @@ +#include "platform_detect_macro.h" + +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) + +#include "dobby/dobby_internal.h" + +#include "InstructionRelocation/x86/InstructionRelocationX86.h" +#include "InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h" +#include "MemoryAllocator/NearMemoryAllocator.h" + +using namespace zz::x86; + +// x64 jmp absolute address +inline void codegen_x64_jmp_absolute_addr(CodeBufferBase *buffer, addr_t target) { + // jmp *(rip) + buffer->Emit8(0xFF); + buffer->Emit8(0x25); // ModR/M: 00 100 101 + buffer->Emit32(0x00); + // .long target + buffer->Emit64(target); +} + +// simple impl for ReloLabel +inline void emit_rel32_label(CodeBufferBase *buffer, uint32_t last_offset, addr_t curr_relo_ip, addr_t orig_dst_ip) { + addr_t curr_offset = buffer->GetBufferSize(); + uint32_t relo_insn_len = curr_offset + sizeof(uint32_t) - last_offset; + addr_t relo_ip = curr_relo_ip + relo_insn_len; + int32_t new_offset = orig_dst_ip - relo_ip; + buffer->Emit32(new_offset); +} + +int GenRelocateSingleX86Insn(addr_t curr_orig_ip, addr_t curr_relo_ip, uint8_t *buffer_cursor, AssemblerBase *assembler, + CodeBufferBase *code_buffer, x86_insn_decode_t &insn, int8_t mode) { +#define __ code_buffer-> + + int relocated_insn_len = -1; + + x86_options_t conf = {0}; + conf.mode = mode; + + // decode x86/x64 insn + x86_insn_decode(&insn, (uint8_t *)buffer_cursor, &conf); + + // x86 ip register == next instruction address + curr_orig_ip = curr_orig_ip + insn.length; + + auto last_relo_offset = code_buffer->GetBufferSize(); + + static auto x86_insn_encode_start = 0; + static auto x86_insn_encoded_len = 0; + auto x86_insn_encode_begin = [&] { x86_insn_encode_start = code_buffer->GetBufferSize(); }; + auto x86_insn_encode_end = [&] { x86_insn_encoded_len = code_buffer->GetBufferSize() - x86_insn_encode_start; }; + + if (insn.primary_opcode >= 0x70 && insn.primary_opcode <= 0x7F) { // jcc rel8 + DEBUG_LOG("[x86 relo] %p: jc rel8", buffer_cursor); + + int8_t offset = insn.immediate; + addr_t orig_dst_ip = curr_orig_ip + offset; +#if defined(TARGET_ARCH_IA32) + uint8_t opcode = 0x80 | (insn.primary_opcode & 0x0f); + + x86_insn_encode_begin(); + __ Emit8(0x0F); + __ Emit8(opcode); + emit_rel32_label(code_buffer, x86_insn_encode_start, curr_relo_ip, orig_dst_ip); +#else + // jcc_true stage 1 + const uint8_t label_jcc_cond_true_stage2 = 2; + __ Emit8(insn.primary_opcode); + __ Emit8(label_jcc_cond_true_stage2); + + // jcc_false + const uint8_t label_cond_false = 6 + 8; + __ Emit8(0xEB); + __ Emit8(label_cond_false); + + // jcc_true stage 2, jmp to orig dst + codegen_x64_jmp_absolute_addr(code_buffer, orig_dst_ip); +#endif + + } else if (mode == 64 && (insn.flags & X86_INSN_DECODE_FLAG_IP_RELATIVE) && + (insn.operands[1].mem.base == RIP)) { // RIP + DEBUG_LOG("[x86 relo] %p: rip", buffer_cursor); + + int32_t orig_disp = insn.operands[1].mem.disp; + addr_t orig_dst_ip = curr_orig_ip + orig_disp; + + addr_t rip_insn_seq_addr = 0; + { + + uint32_t jmp_near_range = (uint32_t)2 * 1024 * 1024 * 1024; + auto rip_insn_seq = (addr_t)NearMemoryAllocator::SharedAllocator()->allocateNearExecMemory( + insn.length + 6 + 8, orig_dst_ip, jmp_near_range); + + rip_insn_seq_addr = rip_insn_seq; + } + + // jmp *(rip) => jmp to [rip insn seq] + x86_insn_encode_begin(); + __ Emit8(0xFF); + __ Emit8(0x25); // ModR/M: 00 100 101 + __ Emit32(0); + __ Emit64(rip_insn_seq_addr); + x86_insn_encode_end(); + + { + auto rip_insn_seq_buffer = CodeBufferBase(); +#define ___ rip_insn_seq_buffer. + + auto rip_insn_req_ip = rip_insn_seq_addr; + rip_insn_req_ip = rip_insn_req_ip + insn.length; // next insn addr + int32_t new_disp = (int32_t)(orig_dst_ip - rip_insn_req_ip); + + // keep orig insn opcode + ___ EmitBuffer(buffer_cursor, insn.displacement_offset); + ___ Emit32(new_disp); + // keep orig insn immediate + if (insn.immediate_offset) { + ___ EmitBuffer((buffer_cursor + insn.immediate_offset), insn.length - insn.immediate_offset); + } + + // jmp *(rip) => back to relo process + auto relo_next_ip = curr_relo_ip + x86_insn_encoded_len; + codegen_x64_jmp_absolute_addr(&rip_insn_seq_buffer, relo_next_ip); + + DobbyCodePatch((void *)rip_insn_seq_addr, rip_insn_seq_buffer.GetBuffer(), rip_insn_seq_buffer.GetBufferSize()); + } + + } else if (insn.primary_opcode == 0xEB) { // jmp rel8 + DEBUG_LOG("[x86 relo] %p: jmp rel8", buffer_cursor); + + int8_t offset = insn.immediate; + addr_t orig_dst_ip = curr_orig_ip + offset; + +#if defined(TARGET_ARCH_IA32) + x86_insn_encode_begin(); + __ Emit8(0xE9); + emit_rel32_label(code_buffer, x86_insn_encode_start, curr_relo_ip, orig_dst_ip); +#else + // jmp *(rip) + codegen_x64_jmp_absolute_addr(code_buffer, orig_dst_ip); +#endif + } else if (insn.primary_opcode == 0xE8 || insn.primary_opcode == 0xE9) { // call or jmp rel32 + DEBUG_LOG("[x86 relo] %p:jmp or call rel32", buffer_cursor); + + int32_t offset = insn.immediate; + addr_t orig_dst_ip = curr_orig_ip + offset; + + assert(insn.immediate_offset == 1); + +#if defined(TARGET_ARCH_IA32) + x86_insn_encode_begin(); + + __ EmitBuffer(buffer_cursor, insn.immediate_offset); + emit_rel32_label(code_buffer, x86_insn_encode_start, curr_relo_ip, orig_dst_ip); +#else + __ Emit8(0xFF); + if (insn.primary_opcode == 0xE8) { + // call *(rip + 2) + __ Emit8(0x15); // ModR/M: 00 010 101 + __ Emit32(2); + + // jmp 8 + __ Emit8(0xEB); + __ Emit8(0x08); + + // dst + __ Emit64(orig_dst_ip); + } else { + // jmp *(rip) + __ Emit8(0x25); // ModR/M: 00 100 101 + __ Emit32(0); + + // dst + __ Emit64(orig_dst_ip); + } +#endif + } else if (insn.primary_opcode >= 0xE0 && insn.primary_opcode <= 0xE2) { // LOOPNZ/LOOPZ/LOOP/JECXZ + // LOOP/LOOPcc + UNIMPLEMENTED(); + } else if (insn.primary_opcode == 0xE3) { + // JCXZ JCEXZ JCRXZ + UNIMPLEMENTED(); + } else { + __ EmitBuffer(buffer_cursor, insn.length); + } + + // insn -> relocated insn + { + int relo_offset = code_buffer->GetBufferSize(); + int relo_len = relo_offset - last_relo_offset; + DEBUG_LOG("insn -> relocated insn: %d -> %d", insn.length, relo_len); + } + return relocated_insn_len; +} + +void GenRelocateCodeX86Shared(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) { + int expected_relocated_mem_size = 32; +x86_try_again: + if (!relocated->addr) { + auto relocated_mem = MemoryAllocator::SharedAllocator()->allocateExecMemory(expected_relocated_mem_size); + if (relocated_mem == nullptr) { + return; + } + relocated->reset((addr_t)relocated_mem, expected_relocated_mem_size); + } + + int ret = GenRelocateCodeFixed(buffer, origin, relocated, branch); + if (ret != 0) { + const int step_size = 16; + expected_relocated_mem_size += step_size; + relocated->reset(0, 0); + + goto x86_try_again; + } +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.h new file mode 100644 index 0000000..0df35e3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.h @@ -0,0 +1,16 @@ +#pragma once + +#include "dobby/common.h" + +#include "MemoryAllocator/AssemblyCodeBuilder.h" + +#include "x86_insn_decode/x86_insn_decode.h" + +#include "X86DecodeKit.h" + +int GenRelocateCodeFixed(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch); + +void GenRelocateCodeX86Shared(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch); + +int GenRelocateSingleX86Insn(addr_t curr_orig_ip, addr_t curr_relo_ip, uint8_t *buffer_cursor, AssemblerBase *assembler, + CodeBufferBase *code_buffer, x86_insn_decode_t &insn, int8_t mode); \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/X86DecodeKit.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/X86DecodeKit.h new file mode 100644 index 0000000..2a4195c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/X86DecodeKit.h @@ -0,0 +1,21 @@ +#pragma once + +struct x86_insn_t {}; + +struct x86_insn_decoder_t { + uint8_t *buffer; + uint32_t buffer_size; + + uint8_t mode = 64; + + x86_insn_t insn; + explicit x86_insn_decoder_t(uint8_t *buffer, uint32_t buffer_size) : buffer(buffer), buffer_size(buffer_size) { + } + + uint8_t peak_byte() const { + return *buffer; + } + + void decode_prefix() { + } +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/Ia32Disassembler.cc b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/Ia32Disassembler.cc new file mode 100644 index 0000000..f1bb1ae --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/Ia32Disassembler.cc @@ -0,0 +1,388 @@ +#include +#include + +#include "logging/logging.h" + +enum SegmentPrefix { + kCs = 0x2e, + kSs = 0x36, + kDs = 0x3e, + kEs = 0x26, + kFs = 0x64, + kGs = 0x65, +}; + +bool supports_rex_ = false; + +void DecodeInstruction(uint8_t *instr) { + bool have_prefixes = true; + uint8_t prefix[4] = {0, 0, 0, 0}; + + // decode legacy prefix + do { + switch (*instr) { + // Group 1 - lock and repeat prefixes: + case 0xF0: + case 0xF2: + case 0xF3: + prefix[0] = *instr; + break; + // Group 2 - segment override prefixes: + case kCs: + case kSs: + case kDs: + case kEs: + case kFs: + case kGs: + prefix[1] = *instr; + break; + // Group 3 - operand size override: + case 0x66: + prefix[2] = *instr; + break; + // Group 4 - address size override: + case 0x67: + prefix[3] = *instr; + break; + default: + have_prefixes = false; + break; + } + if (have_prefixes) { + instr++; + } + } while (have_prefixes); + + // x64 rex + uint8_t rex = (supports_rex_ && (*instr >= 0x40) && (*instr <= 0x4F)) ? *instr : 0; + if (rex != 0) { + instr++; + } + + bool has_modrm = false; + bool reg_is_opcode = false; + + size_t immediate_bytes = 0; + +#define OpEn_MR \ + do { \ + has_modrm = true; \ + } while (0); \ + break; + +#define OpEn_RM \ + do { \ + has_modrm = true; \ + } while (0); \ + break; + +#define OpEn_I(immediate_size) \ + do { \ + immediate_bytes = immediate_size; \ + } while (0); \ + break; + +#define OpEn_RMI(immediate_size) \ + do { \ + immediate_bytes = immediate_size; \ + } while (0); \ + break; + +#define OpEn_O \ + do { \ + reg_is_opcode = true; \ + } while (0); \ + break; + +#define OpEn_D \ + do { \ + reg_is_opcode = true; \ + } while (0); \ + break; + +#define OpEn_ZO \ + do { \ + reg_is_opcode = true; \ + } while (0); \ + break; + +#define Op_Prefix \ + do { \ + reg_is_opcode = true; \ + } while (0); \ + break; + +#define UnImplOpcode \ + do { \ + DEBUG_LOG("opcode unreachable"); \ + } while (0); \ + break; + + typedef enum { + MR, + } OpEnTy; + + // decode opcode + switch (*instr) { + case 0x00: + OpEn_MR; + case 0x01: + OpEn_MR; + case 0x02: + OpEn_RM; + case 0x03: + OpEn_RM; + case 0x04: + OpEn_I(8); + case 0x05: + OpEn_I(16 | 32); + + case 0x06: + case 0x07: + UnImplOpcode; + + case 0x08: + OpEn_MR; + case 0x09: + OpEn_MR; + case 0x0a: + OpEn_RM; + case 0x0b: + OpEn_RM; + case 0x0c: + OpEn_I(8); + case 0x0d: + OpEn_I(16 | 32); + + case 0x0e: + case 0x0f: + UnImplOpcode; + + case 0x10: + OpEn_MR; + case 0x11: + OpEn_MR; + case 0x12: + OpEn_RM; + case 0x13: + OpEn_RM; + case 0x14: + OpEn_I(8); + case 0x15: + OpEn_I(16 | 32); + + case 0x16: + case 0x17: + UnImplOpcode; + + case 0x18: + OpEn_MR; + case 0x19: + OpEn_MR; + case 0x1a: + OpEn_RM; + case 0x1b: + OpEn_RM; + case 0x1c: + OpEn_I(8); + case 0x1d: + OpEn_I(16 | 32); + + case 0x1e: + case 0x1f: + UnImplOpcode; + + case 0x20: + OpEn_MR; + case 0x21: + OpEn_MR; + case 0x22: + OpEn_RM; + case 0x23: + OpEn_RM; + case 0x24: + OpEn_I(8); + case 0x25: + OpEn_I(16 | 32); + + case 0x26: + case 0x27: + UnImplOpcode; + + case 0x28: + OpEn_MR; + case 0x29: + OpEn_MR; + case 0x2a: + OpEn_RM; + case 0x2b: + OpEn_RM; + case 0x2c: + OpEn_I(8); + case 0x2d: + OpEn_I(16 | 32); + + case 0x2e: + case 0x2f: + UnImplOpcode; + + case 0x30: + OpEn_MR; + case 0x31: + OpEn_MR; + case 0x32: + OpEn_RM; + case 0x33: + OpEn_RM; + case 0x34: + OpEn_I(8); + case 0x35: + OpEn_I(16 | 32); + + case 0x36: + case 0x37: + UnImplOpcode; + + case 0x38: + OpEn_MR; + case 0x39: + OpEn_MR; + case 0x3a: + OpEn_RM; + case 0x3b: + OpEn_RM; + case 0x3c: + OpEn_I(8); + case 0x3d: + OpEn_I(16 | 32); + + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + UnImplOpcode; + + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + OpEn_O; + + case 0x60: + case 0x61: + case 0x62: + UnImplOpcode; + + case 0x63: + if ((rex & REX_W) != 0) { + OpEn_RM; + }; + break; + + case 0x64: + case 0x65: + case 0x66: + case 0x67: + Op_Prefix; + + case 0x68: + OpEn_I(16 | 32); + + case 0x69: + OpEn_RMI(16 | 32); + + case 0x6a: + OpEn_I(8); + + case 0x6b: + OpEn_RMI(8); + + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + OpEn_D; + + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + UnImplOpcode; + + case 0x86: + case 0x87: + OpEn_RM; + + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + OpEn_RM; + + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + UnImplOpcode; + + case 0x9d: + OpEn_ZO; + + case 0x0f: + DecodeExtendedOpcode + } +} + +void DecodeExtendedOpcode(uint8_t *instr) { +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.cc b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.cc new file mode 100644 index 0000000..800badf --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.cc @@ -0,0 +1,604 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) + +#include "./X86OpcodoDecodeTable.h" + +// clang-format on + +#define _xUnknownOpHanlder -1, -1, OpSz_0, ImmSz_0, _UnknownOpHanlder +void _UnknownOpHanlder(InstrMnemonic *instr, addr_t p) { + // printf("Unknown Operand\n"); + return; +} + +#define _xInvalidOpHanlder -1, -1, OpSz_0, ImmSz_0, _InValidOpHanlder +void _InValidOpHanlder(InstrMnemonic *instr, addr_t p) { + // printf("Invalid Operand\n"); + return; +} + +inline void _ContinueDispatch(InstrMnemonic *instr, addr_t p) { + OpcodeDecodeItem *item = &OpcodeDecodeTable[*(unsigned char *)p]; + item->DecodeHandler(instr, p); +} + +// ===== Decode LegacyPrefix and REXPrefix ===== + +// clang-format off +#define _xDecodePrefix_0F 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeLegacyPrefix +#define _xDecodePrefix_66 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodePrefix_66 +#define _xDecodePrefix_67 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeLegacyPrefix +#define _xDecodeREXPrefix 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeREXPrefix +#define _xDecodePrefix 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeLegacyPrefix +#define _xDecodeSegPrefix 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeLegacyPrefix +// clang-format on + +void _DecodeREXPrefix(InstrMnemonic *instr, addr_t p) { + instr->instr.REX = *(byte_t *)p; + instr->len++; + instr->OperandSz = OpSz_64; + + _ContinueDispatch(instr, p + 1); // continue decode +} + +void _DecodeLegacyPrefix(InstrMnemonic *instr, addr_t p) { + instr->instr.prefix = *(byte_t *)p; + instr->len++; + + _ContinueDispatch(instr, p + 1); // continue decode +} + +void _DecodePrefix_66(InstrMnemonic *instr, addr_t p) { + instr->OperandSz = OpSz_16; + _DecodeLegacyPrefix(instr, p); +} + +// ===== Decode Opcode ===== + +static void _DecodeOp(InstrMnemonic *instr, addr_t p) { + instr->instr.opcode1 = *(byte_t *)p; + instr->len++; +} + +static void _DecodeOpExtraOp(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); +} + +static void _DecodeOpWithReg(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); +} + +#define _xDecodeOpEn_ZO 1, OpEn_ZO, OpSz_0, ImmSz_0, _DecodeOpEn_ZO +void _DecodeOpEn_ZO(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); +} + +#define _xDecodeOpEn_O 1, OpEn_O, OpSz_0, ImmSz_0, _DecodeOpEn_O +void _DecodeOpEn_O(InstrMnemonic *instr, addr_t p) { + _DecodeOpWithReg(instr, p); +} + +// ===== Decode Operand ===== + +// ===== Decode ModRM Operand ===== + +#define REX_W(byte) ((byte & 0b00001000) >> 3) +#define REX_R(byte) ((byte & 0b00000100) >> 2) +#define REX_X(byte) ((byte & 0b00000010) >> 1) +#define REX_B(byte) ((byte & 0b00000001) >> 0) + +#define ModRM_Mod(byte) ((byte & 0b11000000) >> 6) +#define ModRM_RegOpcode(byte) ((byte & 0b00111000) >> 3) +#define ModRM_RM(byte) (byte & 0b00000111) + +#define SIB_Scale(sib) ((sib & 0b11000000) >> 6) +#define SIB_Index(sib) ((sib & 0b00111000) >> 3) +#define SIB_Base(sib) ((sib & 0b00000111) >> 0) + +#define REX_SIB_Base(rex, sib) ((REX_B(rex) << 3) | SIB_Base(sib)) + +void _DecodeDisplacement8(InstrMnemonic *instr, addr_t p) { + *(byte_t *)&instr->instr.Displacement = *(byte_t *)p; + instr->len += 1; +} + +void _DecodeDisplacement32(InstrMnemonic *instr, addr_t p) { + instr->instr.DisplacementOffset = instr->len; + *(dword *)&instr->instr.Displacement = *(byte_t *)p; + instr->len += 4; +} + +void _DecodeSIB(InstrMnemonic *instr, addr_t p) { + instr->instr.SIB = *(byte_t *)p; + instr->len++; +} + +void _DecodeModRM(InstrMnemonic *instr, addr_t p) { + int init_len = instr->len; + + instr->instr.ModRM = *(byte_t *)p; + instr->len++; + +#if defined(_M_X64) || defined(__x86_64__) + if (ModRM_Mod(instr->instr.ModRM) == 0b00 && ModRM_RM(instr->instr.ModRM) == 0b101) { + // RIP-Relative Addressing + instr->flag = instr->flag | kIPRelativeAddress; + _DecodeDisplacement32(instr, p + (instr->len - init_len)); + return; + } +#endif + + // Addressing Forms with the SIB Byte + if (ModRM_Mod(instr->instr.ModRM) != 0b11 && ModRM_RM(instr->instr.ModRM) == 0b100) { + _DecodeSIB(instr, p + (instr->len - init_len)); + } + + // [REG] + if (ModRM_Mod(instr->instr.ModRM) == 0b00) { + if (ModRM_RM(instr->instr.ModRM) == 0b101) { + _DecodeDisplacement32(instr, p + (instr->len - init_len)); + return; + } + } + + // [REG+disp8} + if (ModRM_Mod(instr->instr.ModRM) == 0b01) { + _DecodeDisplacement8(instr, p + (instr->len - init_len)); + return; + } + + // [REG+disp32} + if (ModRM_Mod(instr->instr.ModRM) == 0b10) { + _DecodeDisplacement32(instr, p + (instr->len - init_len)); + return; + } + + // REG + if (ModRM_Mod(instr->instr.ModRM) == 0b11) { + } +} + +void _DecodeOpEn_M(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +void _DecodeOpEn_RM(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +void _DecodeOpEn_MR(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +void _DecodeOpEn_M1(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +void _DecodeOpEn_MC(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +// ===== Decode Immediate Operand ===== + +void _DecodeImmedite(InstrMnemonic *instr, addr_t p, int sz) { + + instr->instr.ImmediateOffset = instr->len; + + OpcodeDecodeItem *item = &OpcodeDecodeTable[instr->instr.opcode1]; + if (sz == ImmSz_0) { + sz = item->ImmediteSz; + if (sz == (ImmSz_16 | ImmSz_32)) { + if (instr->instr.prefix == 0x66) { + sz = ImmSz_16; + } else { + sz = ImmSz_32; // Default Immedite Size + } + } + } + + if (sz == ImmSz_8) { + *(byte_t *)&instr->instr.Immediate = *(byte_t *)p; + instr->len += 1; + } else if (sz == ImmSz_16) { + *(word *)&instr->instr.Immediate = *(dword *)p; + instr->len += 2; + } else if (sz == ImmSz_32) { + *(dword *)&instr->instr.Immediate = *(dword *)p; + instr->len += 4; + } +} + +void _DecodeOpEn_I(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeImmedite(instr, p + 1, instr->ImmediteSz); +} + +void _DecodeOpEn_OI(InstrMnemonic *instr, addr_t p) { + _DecodeOpWithReg(instr, p); + _DecodeImmedite(instr, p + 1, instr->ImmediteSz); +} + +void _DecodeOpEn_D(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeImmedite(instr, p + 1, instr->ImmediteSz); +} + +// ===== Decode ModRM Immediate Operand ===== + +void _DecodeOpEn_RMI(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); + _DecodeImmedite(instr, p + 2, instr->ImmediteSz); +} + +void _DecodeOpEn_MI(InstrMnemonic *instr, addr_t p) { + _DecodeOpExtraOp(instr, p); + _DecodeModRM(instr, p + 1); + _DecodeImmedite(instr, p + 2, instr->ImmediteSz); +} + +// ===== Decode Specific Opcode ===== + +#define _xDecodeOpC8 1, 0, OpSz_0, ImmSz_0, _DecodeOpC8 +void _DecodeOpC8(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + + instr->len = instr->len + 2 + 1; +} + +// http://ref.x86asm.net/coder.html#x04 +OpcodeDecodeItem OpcodeDecodeTable[257] = { + + {0x00, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x01, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x02, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x03, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x04, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x05, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0x06, _xInvalidOpHanlder}, + {0x07, _xInvalidOpHanlder}, +#else + {0x06, _xDecodeOpEn_ZO}, + {0x07, _xDecodeOpEn_ZO}, +#endif + {0x08, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x09, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x0A, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x0B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x0C, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x0D, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0x0E, _xInvalidOpHanlder}, +#else + {0x0E, _xDecodeOpEn_ZO}, +#endif + {0x0F, _xDecodePrefix_0F}, + {0x10, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x11, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x12, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x13, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x14, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x15, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0x16, _xInvalidOpHanlder}, + {0x17, _xInvalidOpHanlder}, +#else + {0x16, _xDecodeOpEn_ZO}, + {0x17, _xDecodeOpEn_ZO}, +#endif + {0x18, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x19, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x1A, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x1B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x1C, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x1D, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0x1E, _xInvalidOpHanlder}, + {0x1F, _xInvalidOpHanlder}, +#else + {0x1E, _xDecodeOpEn_ZO}, + {0x1F, _xDecodeOpEn_ZO}, +#endif + {0x20, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x21, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x22, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x23, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x24, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x25, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x26, _xDecodeSegPrefix}, +#if defined(_M_X64) || defined(__x86_64__) + {0x27, _xInvalidOpHanlder}, +#else + {0x27, _xDecodeOpEn_ZO}, +#endif + {0x28, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x29, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x2A, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x2B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x2C, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x2D, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x2E, _xDecodeSegPrefix}, +#if defined(_M_X64) || defined(__x86_64__) + {0x2F, _xInvalidOpHanlder}, +#else + {0x2F, _xDecodeOpEn_ZO}, +#endif + {0x30, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x31, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x32, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x33, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x34, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x35, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x36, _xDecodeSegPrefix}, +#if defined(_M_X64) || defined(__x86_64__) + {0x37, _xInvalidOpHanlder}, +#else + {0x37, _xDecodeOpEn_ZO}, +#endif + {0x38, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x39, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x3A, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x3B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x3C, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x3D, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x3E, _xDecodeSegPrefix}, +#if defined(_M_X64) || defined(__x86_64__) + {0x3F, _xInvalidOpHanlder}, +#else + {0x3F, _xDecodeOpEn_ZO}, +#endif +#if defined(_M_X64) || defined(__x86_64__) // For REX Prefix + {0x40, _xDecodeREXPrefix}, + {0x41, _xDecodeREXPrefix}, + {0x42, _xDecodeREXPrefix}, + {0x43, _xDecodeREXPrefix}, + {0x44, _xDecodeREXPrefix}, + {0x45, _xDecodeREXPrefix}, + {0x46, _xDecodeREXPrefix}, + {0x47, _xDecodeREXPrefix}, + {0x48, _xDecodeREXPrefix}, + {0x49, _xDecodeREXPrefix}, + {0x4A, _xDecodeREXPrefix}, + {0x4B, _xDecodeREXPrefix}, + {0x4C, _xDecodeREXPrefix}, + {0x4D, _xDecodeREXPrefix}, + {0x4E, _xDecodeREXPrefix}, + {0x4F, _xDecodeREXPrefix}, +#else + {0x40, _xDecodeOpEn_O}, + {0x41, _xDecodeOpEn_O}, + {0x42, _xDecodeOpEn_O}, + {0x43, _xDecodeOpEn_O}, + {0x44, _xDecodeOpEn_O}, + {0x45, _xDecodeOpEn_O}, + {0x46, _xDecodeOpEn_O}, + {0x47, _xDecodeOpEn_O}, + {0x48, _xDecodeOpEn_O}, + {0x49, _xDecodeOpEn_O}, + {0x4A, _xDecodeOpEn_O}, + {0x4B, _xDecodeOpEn_O}, + {0x4C, _xDecodeOpEn_O}, + {0x4D, _xDecodeOpEn_O}, + {0x4E, _xDecodeOpEn_O}, + {0x4F, _xDecodeOpEn_O}, +#endif + {0x50, _xDecodeOpEn_O}, + {0x51, _xDecodeOpEn_O}, + {0x52, _xDecodeOpEn_O}, + {0x53, _xDecodeOpEn_O}, + {0x54, _xDecodeOpEn_O}, + {0x55, _xDecodeOpEn_O}, + {0x56, _xDecodeOpEn_O}, + {0x57, _xDecodeOpEn_O}, + {0x58, _xDecodeOpEn_O}, + {0x59, _xDecodeOpEn_O}, + {0x5A, _xDecodeOpEn_O}, + {0x5B, _xDecodeOpEn_O}, + {0x5C, _xDecodeOpEn_O}, + {0x5D, _xDecodeOpEn_O}, + {0x5E, _xDecodeOpEn_O}, + {0x5F, _xDecodeOpEn_O}, +#if defined(_M_X64) || defined(__x86_64__) + {0x60, _xInvalidOpHanlder}, + {0x61, _xInvalidOpHanlder}, + {0x62, _xInvalidOpHanlder}, +#else + {0x60, _xDecodeOpEn_ZO}, + {0x61, _xDecodeOpEn_ZO}, + {0x62, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, +#endif + {0x63, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x64, _xDecodeSegPrefix}, + {0x65, _xDecodeSegPrefix}, + {0x66, _xDecodePrefix_66}, + {0x67, _xDecodePrefix_67}, + {0x68, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x69, 2, OpEn_RMI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_RMI}, + {0x6A, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x6B, 1, OpEn_RMI, OpSz_16 | OpSz_32, ImmSz_8, _DecodeOpEn_RMI}, + {0x6C, _xDecodeOpEn_ZO}, + {0x6D, _xDecodeOpEn_ZO}, + {0x6E, _xDecodeOpEn_ZO}, + {0x6F, _xDecodeOpEn_ZO}, + {0x70, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x71, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x72, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x73, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x74, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x75, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x76, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x77, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x78, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x79, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7A, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7B, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7C, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7D, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7E, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7F, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x80, 2, OpEn_MI, OpSz_8, ImmSz_8, _DecodeOpEn_MI}, + {0x81, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_MI}, +#if defined(_M_X64) || defined(__x86_64__) + {0x82, _xInvalidOpHanlder}, +#else + {0x82, _xUnknownOpHanlder}, +#endif + {0x83, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_8, _DecodeOpEn_MI}, + {0x84, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x85, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x86, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x87, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x88, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x89, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x8A, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x8B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x8C, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x8D, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x8E, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x8F, 2, OpEn_M, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_M}, + {0x90, _xDecodeOpEn_ZO}, + {0x91, _xInvalidOpHanlder}, + {0x92, _xInvalidOpHanlder}, + {0x93, _xInvalidOpHanlder}, + {0x94, _xInvalidOpHanlder}, + {0x95, _xInvalidOpHanlder}, + {0x96, _xInvalidOpHanlder}, + {0x97, _xInvalidOpHanlder}, + {0x98, _xDecodeOpEn_ZO}, + {0x99, _xDecodeOpEn_ZO}, +#if defined(_M_X64) || defined(__x86_64__) + {0x9A, _xInvalidOpHanlder}, +#else + {0x9A, _xDecodeOpEn_ZO}, +#endif + {0x9B, _xDecodeOpEn_ZO}, + {0x9C, _xDecodeOpEn_ZO}, + {0x9D, _xDecodeOpEn_ZO}, + {0x9E, _xDecodeOpEn_ZO}, + {0x9F, _xDecodeOpEn_ZO}, + {0xA0, _xUnknownOpHanlder}, + {0xA1, _xUnknownOpHanlder}, + {0xA2, _xUnknownOpHanlder}, + {0xA3, _xUnknownOpHanlder}, + {0xA4, _xDecodeOpEn_ZO}, + {0xA5, _xDecodeOpEn_ZO}, + {0xA6, _xDecodeOpEn_ZO}, + {0xA7, _xDecodeOpEn_ZO}, + {0xA8, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xA9, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0xAA, _xDecodeOpEn_ZO}, + {0xAB, _xDecodeOpEn_ZO}, + {0xAC, _xDecodeOpEn_ZO}, + {0xAD, _xDecodeOpEn_ZO}, + {0xAE, _xDecodeOpEn_ZO}, + {0xAF, _xDecodeOpEn_ZO}, +#undef SAME_ITEM_LAZY +#define SAME_ITEM_LAZY 1, OpEn_OI, OpSz_0, ImmSz_8, _DecodeOpEn_OI + {0xB0, SAME_ITEM_LAZY}, + {0xB1, SAME_ITEM_LAZY}, + {0xB2, SAME_ITEM_LAZY}, + {0xB3, SAME_ITEM_LAZY}, + {0xB4, SAME_ITEM_LAZY}, + {0xB5, SAME_ITEM_LAZY}, + {0xB6, SAME_ITEM_LAZY}, + {0xB7, SAME_ITEM_LAZY}, +#undef SAME_ITEM_LAZY +#define SAME_ITEM_LAZY 1, OpEn_OI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_OI + {0xB8, SAME_ITEM_LAZY}, + {0xB9, SAME_ITEM_LAZY}, + {0xBA, SAME_ITEM_LAZY}, + {0xBB, SAME_ITEM_LAZY}, + {0xBC, SAME_ITEM_LAZY}, + {0xBD, SAME_ITEM_LAZY}, + {0xBE, SAME_ITEM_LAZY}, + {0xBF, SAME_ITEM_LAZY}, + {0xC0, 2, OpEn_MI, OpSz_8, ImmSz_8, _DecodeOpEn_MI}, + {0xC1, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_MI}, + {0xC2, 1, OpEn_I, OpSz_0, ImmSz_16, _DecodeOpEn_I}, + {0xC3, _xDecodeOpEn_ZO}, + {0xC4, _xInvalidOpHanlder}, + {0xC5, _xInvalidOpHanlder}, + {0xC6, _xUnknownOpHanlder}, + {0xC7, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_MI}, + {0xC8, _xDecodeOpC8}, + {0xC9, _xDecodeOpEn_ZO}, + {0xCA, 1, OpEn_I, OpSz_0, ImmSz_16, _DecodeOpEn_I}, + {0xCB, _xDecodeOpEn_ZO}, + {0xCC, _xDecodeOpEn_ZO}, + {0xCD, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0xCE, _xInvalidOpHanlder}, +#else + {0xCE, _xDecodeOpEn_ZO}, +#endif + {0xCF, _xDecodeOpEn_ZO}, + {0xD0, 1, OpEn_M1, OpSz_8, ImmSz_0, _DecodeOpEn_M1}, + {0xD1, 1, OpEn_M1, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_M1}, + {0xD2, 1, OpEn_MC, OpSz_8, ImmSz_0, _DecodeOpEn_MC}, + {0xD3, 1, OpEn_MC, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MC}, +#if defined(_M_X64) || defined(__x86_64__) + {0xD4, _xInvalidOpHanlder}, + {0xD5, _xInvalidOpHanlder}, +#else + {0xD4, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xD5, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, +#endif + {0xD6, _xInvalidOpHanlder}, + {0xD7, _xDecodeOpEn_ZO}, + {0xD8, _xUnknownOpHanlder}, + {0xD9, _xUnknownOpHanlder}, + {0xDA, _xUnknownOpHanlder}, + {0xDB, _xUnknownOpHanlder}, + {0xDC, _xUnknownOpHanlder}, + {0xDD, _xUnknownOpHanlder}, + {0xDE, _xUnknownOpHanlder}, + {0xDF, _xUnknownOpHanlder}, + {0xE0, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0xE1, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0xE2, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0xE3, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0xE4, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xE5, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xE6, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xE7, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xE8, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0xE9, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0xEA, _xInvalidOpHanlder}, +#else + {0xEA, _xUnknownOpHanlder}, +#endif + {0xEB, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xEC, _xDecodeOpEn_ZO}, + {0xED, _xDecodeOpEn_ZO}, + {0xEE, _xDecodeOpEn_ZO}, + {0xEF, _xDecodeOpEn_ZO}, + {0xF0, _xDecodePrefix}, + {0xF1, _xDecodeOpEn_ZO}, + {0xF2, _xDecodeOpEn_ZO}, +#ifdef DETOURS_X86 + {0xF3, _CopyF3}, +#else + {0xF3, _xDecodeOpEn_ZO}, +#endif + {0xF4, _xDecodeOpEn_ZO}, + {0xF5, _xDecodeOpEn_ZO}, + {0xF6, 2, OpEn_MI, OpSz_8, ImmSz_8, _DecodeOpEn_MI}, + {0xF7, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_MI}, + {0xF8, _xDecodeOpEn_ZO}, + {0xF9, _xDecodeOpEn_ZO}, + {0xFA, _xDecodeOpEn_ZO}, + {0xFB, _xDecodeOpEn_ZO}, + {0xFC, _xDecodeOpEn_ZO}, + {0xFD, _xDecodeOpEn_ZO}, + {0xFE, 2, OpEn_M, OpSz_8, ImmSz_0, _DecodeOpEn_M}, + {0xFF, 2, OpEn_M, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_M}, + {0, 0, 0, 0, 0}}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.h new file mode 100644 index 0000000..778fdb3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.h @@ -0,0 +1,142 @@ +#ifndef X86_OPCODE_DECODE_TABLE_H +#define X86_OPCODE_DECODE_TABLE_H + +#ifndef __addr_t_defined +#define __addr_t_defined +typedef unsigned long long addr_t; +#endif + +#ifndef __byte_defined +#define __byte_defined +typedef unsigned char byte_t; +#endif + +#ifndef __uint_defined +#define __uint_defined +typedef unsigned int uint; +#endif + +#ifndef __word_defined +#define __word_defined +typedef short word; +#endif + +#ifndef __dword_defined +#define __dword_defined +typedef int dword; +#endif + +enum OpcodeType { OpTy_Op1, OpTy_RegInOp1, OpTy_Op1ExtraOp }; + +struct Instr { + byte_t prefix; + + byte_t REX; + + union { + byte_t opcode[3]; + struct { + byte_t opcode1; + byte_t opcode2; + byte_t opcode3; + }; + }; + + union { + byte_t ModRM; + struct { + byte_t Mod : 2; + byte_t RegOpcode : 3; + byte_t RM : 3; + }; + }; + + union { + byte_t SIB; + struct { + byte_t base : 2; + byte_t index : 3; + byte_t scale : 3; + }; + }; + + byte_t Displacement[4]; + int DisplacementOffset; + + byte_t Immediate[4]; + int ImmediateOffset; +}; + +// clang-format off +enum OperandSize { + OpSz_0 = 0, + OpSz_8=1, + OpSz_16=2, + OpSz_32=4, + OpSz_64=8 +}; + +enum ImmediteSize { + ImmSz_0 = 0, + ImmSz_8=1, + ImmSz_16=2, + ImmSz_32=4, + ImmSz_64=8 +}; + +enum InstrFlag { + kNoFlag = 0, + kIPRelativeAddress = 1 +}; +// clang-format on + +struct InstrMnemonic { + uint len; + + int flag; + + OperandSize OperandSz; + + ImmediteSize ImmediteSz; + + struct Instr instr; +}; + +struct OpcodeDecodeItem { + unsigned char opcode; + + int FixedSize; + + int OpEn; + + int OperandSz; + + int ImmediteSz; + + void (*DecodeHandler)(InstrMnemonic *, addr_t); +}; + +// clang-format off +enum OperandEncodingType { + OpEn_NONE =0, + OpEn_ZO, + OpEn_M, + OpEn_I, + OpEn_D, + OpEn_O, + OpEn_RM, + OpEn_MR, + OpEn_MI, + OpEn_OI, + OpEn_M1, + OpEn_MC, + OpEn_RMI +}; + +// clang-format on + +extern OpcodeDecodeItem OpcodeDecodeTable[257]; + +void _DecodePrefix(InstrMnemonic *instr, addr_t p); + +#endif diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/build_config.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/build_config.h new file mode 100644 index 0000000..7c558c2 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/build_config.h @@ -0,0 +1,144 @@ +#ifndef BUILD_CONFIG_H +#define BUILD_CONFIG_H + +#if defined(__APPLE__) +// only include TargetConditions after testing ANDROID as some android builds +// on mac don't have this header available and it's not needed unless the target +// is really mac/ios. +#include +#define OS_MACOSX 1 +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#define OS_IOS 1 +#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#elif defined(__linux__) +#define OS_LINUX 1 +// include a system header to pull in features.h for glibc/uclibc macros. +#include +#if defined(__GLIBC__) && !defined(__UCLIBC__) +// we really are using glibc, not uClibc pretending to be glibc +#define LIBC_GLIBC 1 +#endif +#elif defined(_WIN32) +#define OS_WIN 1 +#elif defined(__Fuchsia__) +#define OS_FUCHSIA 1 +#elif defined(__FreeBSD__) +#define OS_FREEBSD 1 +#elif defined(__NetBSD__) +#define OS_NETBSD 1 +#elif defined(__OpenBSD__) +#define OS_OPENBSD 1 +#elif defined(__sun) +#define OS_SOLARIS 1 +#elif defined(__QNXNTO__) +#define OS_QNX 1 +#elif defined(_AIX) +#define OS_AIX 1 +#elif defined(__asmjs__) || defined(__wasm__) +#define OS_ASMJS +#else +#error Please add support for your platform in build/build_config.h +#endif +// NOTE: Adding a new port? Please follow +// https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md + +// For access to standard BSD features, use OS_BSD instead of a +// more specific macro. +#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD) +#define OS_BSD 1 +#endif + +// For access to standard POSIXish features, use OS_POSIX instead of a +// more specific macro. +#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || defined(OS_FREEBSD) || defined(OS_LINUX) || \ + defined(OS_MACOSX) || defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \ + defined(OS_SOLARIS) +#define OS_POSIX 1 +#endif + +// Compiler detection. Note: clang masquerades as GCC on POSIX and as MSVC on +// Windows. +#if defined(__GNUC__) +#define COMPILER_GCC 1 +#elif defined(_MSC_VER) +#define COMPILER_MSVC 1 +#else +#error Please add support for your compiler in build/build_config.h +#endif + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86_64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(_M_IX86) || defined(__i386__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__s390x__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390X 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__s390__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390 1 +#define ARCH_CPU_31_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__PPC64__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARMEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__aarch64__) || defined(_M_ARM64) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARM64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__) +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__MIPSEL__) +#if defined(__LP64__) +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS64EL 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#else +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPSEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#endif +#elif defined(__MIPSEB__) +#if defined(__LP64__) +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#else +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#endif +#else +#error Please add support for your architecture in build/build_config.h +#endif + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c new file mode 100644 index 0000000..5b8fc32 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c @@ -0,0 +1,564 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) + +#include "x86_insn_decode.h" + +#include "logging/logging.h" + +#define REX_W(byte) ((byte & 0b00001000) >> 3) +#define REX_R(byte) ((byte & 0b00000100) >> 2) +#define REX_X(byte) ((byte & 0b00000010) >> 1) +#define REX_B(byte) ((byte & 0b00000001) >> 0) + +#define ModRM_Mod(byte) ((byte & 0b11000000) >> 6) +#define ModRM_RegOpcode(byte) ((byte & 0b00111000) >> 3) +#define ModRM_RM(byte) (byte & 0b00000111) + +#define SIB_Scale(sib) ((sib & 0b11000000) >> 6) +#define SIB_Index(sib) ((sib & 0b00111000) >> 3) +#define SIB_Base(sib) ((sib & 0b00000111) >> 0) + +#if 0 +/* Build an encoding specification from scratch. */ +#define SPEC_MAKE(op, opr1, opr2, opr3, opr4) \ + ((uint64_t)(uint16_t)(int16_t)(op) | ((uint64_t)(opr1) << 16) | ((uint64_t)(opr2) << 24) | \ + ((uint64_t)(opr3) << 32) | ((uint64_t)(opr4) << 40)) + +/* Get the operation in an encoding specification. */ +#define SPEC_INSN(spec) ((int16_t)((spec)&0xffff)) + +/* Get the given operand (zero-based) in an encoding specification. */ +#define SPEC_OPERAND(spec, i) ((uint8_t)(((spec) >> (16 + (i)*8)) & 0xff)) + +/* Get the operands part of an encoding specification. */ +#define SPEC_OPERANDS(spec) ((spec)&0xffffffffffff0000ULL) + +/* Merges two encoding specifications. */ +#define SPEC_MERGE(spec1, spec2) ((spec1) | (spec2)) + +#define OP4(insn, oper1, oper2, oper3, oper4) SPEC_MAKE(I_##insn, O_##oper1, O_##oper2, O_##oper3, O_##oper4) +#define OP3(insn, oper1, oper2, oper3) OP4(insn, oper1, oper2, oper3, NONE) +#define OP2(insn, oper1, oper2) OP3(insn, oper1, oper2, NONE) +#define OP1(insn, oper1) OP2(insn, oper1, NONE) +#define OP0(insn) OP1(insn, NONE) +#define OP_EMPTY OP0(NONE) +#define OP_EMPTY_4 OP_EMPTY, OP_EMPTY, OP_EMPTY, OP_EMPTY +#define OP_EMPTY_8 OP_EMPTY_4, OP_EMPTY_4 +#endif + +#define op3_flag(x, f, o0, o1, o2) \ + { \ + .name = #x, .flags = (f), .operands[0] = {.data = #o0}, .operands[1] = {.data = #o1}, \ + .operands[2] = {.data = #o2}, \ + } +#define op2_flag(x, f, o0, o1) op3_flag(x, f, o0, o1, __) +#define op1_flag(x, f, o0) op2_flag(x, f, o0, __) +#define op0_flag(x, f) op1_flag(x, f, __) + +#define op3f op3_flag +#define op2f op2_flag +#define op1f op1_flag +#define op0f op0_flag + +#define op3(x, o0, o1, o2) op3f(x, 0, o0, o1, o2) +#define op2(x, o0, o1) op2f(x, 0, o0, o1) +#define op1(x, o0) op1f(x, 0, o0) +#define op0(x) op0f(x, 0) + +/* Opcode extension in modrm byte reg field. */ +#define foreach_x86_insn_modrm_reg_group \ + _(1) _(1a) _(2) _(3) _(4) _(5) _(6) _(7) _(8) _(9) _(10) _(11) _(12) _(13) _(14) _(15) _(16) _(p) +#define foreach_x86_insn_sse_group \ + _(10) _(28) _(50) _(58) _(60) _(68) _(70) _(78) _(c0) _(d0) _(d8) _(e0) _(e8) _(f0) _(f8) +enum { + X86_INSN_GROUP_START = 0, + +#define _(x) X86_INSN_MODRM_REG_GROUP_##x, + foreach_x86_insn_modrm_reg_group +#undef _ + + X86_INSN_SSE_GROUP_START = 19, +#define _(x) X86_INSN_SSE_GROUP_##x, + foreach_x86_insn_sse_group +#undef _ + + X86_INSN_GROUP_END = 35 +}; + +#define X86_INSN_GROUP_END_MASK ((1 << 6) - 1) +#define X86_INSN_FLAG_SET_GROUP(n) ((n) << 5) +#define X86_INSN_FLAG_GET_GROUP(f) (((f) >> 5) & X86_INSN_GROUP_END_MASK) + +enum { +#define _(x) X86_INSN_FLAG_MODRM_REG_GROUP_##x = X86_INSN_FLAG_SET_GROUP(X86_INSN_MODRM_REG_GROUP_##x), + foreach_x86_insn_modrm_reg_group +#undef _ + +#define _(x) X86_INSN_FLAG_SSE_GROUP_##x = X86_INSN_FLAG_SET_GROUP(X86_INSN_SSE_GROUP_##x), + foreach_x86_insn_sse_group +#undef _ +}; + +// clang-format off + +#define foreach_x86_operand_combine(x, op1_type, op2_type) op2(x, Eb, Gb), op2(x, Ev, Gv), op2(x, Gb, Eb), op2(x, Gv, Ev), op2(x, AL, Ib), op2(x, AX, Iz) + +#define foreach_x86_gp_reg _(AX) _(CX) _(DX) _(BX) _(SP) _(BP) _(SI) _(DI) + +#define foreach_x86_condition _(o) _(no) _(b) _(nb) _(z) _(nz) _(be) _(nbe) _(s) _(ns) _(p) _(np) _(l) _(nl) _(le) _(nle) + +// clang-format on + +#include "./x86_opcode_one_byte.c" +#include "./x86_opcode_two_byte.c" + +typedef struct { + x86_insn_spec_t insns[8]; +} x86_insn_group8_t; + +#include "./x86_opcode_modrm_reg_group.c" +#include "./x86_opcode_sse_group.c" + +#include "./x86_insn_reader.c" + +static x86_insn_prefix_t x86_insn_decode_prefix(x86_insn_reader_t *rd, x86_insn_decode_t *insn, x86_options_t *conf) { + /* Decode each byte until the byte is not a prefix or is an REX prefix, + * because an REX prefix is required to immediately preceed the opcode. + */ + x86_insn_prefix_t insn_prefix = 0; + for (;;) { + uint8_t c = peek_byte(rd); + x86_insn_prefix_t t = 0; + + /* Check for REX prefix if we're in 64-bit mode. */ + if (conf->mode == 64) { + if (c >= 0x40 && c <= 0x4f) { + uint8_t rex = read_byte(rd); + + if (REX_W(rex)) { + insn->flags |= X86_INSN_DECODE_FLAG_OPERAND_SIZE_64; + } + + insn->rex = rex; + + break; + } + } + + /* Check for legacy prefixes. */ + switch (c) { + case 0xF0: + t = INSN_PREFIX_LOCK; + break; + case 0xF2: + t = INSN_PREFIX_REPNE; + break; + case 0xF3: + t = INSN_PREFIX_REPE; + break; + case 0x2E: + t = INSN_PREFIX_CS; + break; + case 0x36: + t = INSN_PREFIX_SS; + break; + case 0x3E: + t = INSN_PREFIX_DS; + break; + case 0x26: + t = INSN_PREFIX_ES; + break; + case 0x64: + t = INSN_PREFIX_FS; + break; + case 0x65: + t = INSN_PREFIX_GS; + break; + case 0x66: + t = INSN_PREFIX_OPERAND_SIZE; + break; + case 0x67: + t = INSN_PREFIX_ADDRESS_SIZE; + break; + } + if (t == 0) + break; + + /* Consume 1 byte. */ + read_byte(rd); + insn_prefix |= t; + } + + return insn_prefix; +} + +int x86_insn_has_modrm_byte(x86_insn_spec_t *insn) { + int i; + for (i = 0; i < sizeof(insn->operands) / sizeof(x86_insn_operand_spec_t); i++) + switch (insn->operands[i].code) { + case 'G': + case 'E': + case 'M': + case 'R': + return 1; + } + return 0; +} + +int x86_insn_immediate_type(x86_insn_spec_t *insn) { + int i; + for (i = 0; i < sizeof(insn->operands); i++) { + switch (insn->operands[i].code) { + case 'J': + case 'I': + case 'O': + return insn->operands[i].type; + } + } + return 0; +} + +int x86_insn_has_immediate(x86_insn_spec_t *insn) { + int i; + for (i = 0; i < sizeof(insn->operands) / sizeof(x86_insn_operand_spec_t); i++) { + switch (insn->operands[i].code) { + case 'J': + case 'I': + case 'O': + return 1; + } + } + return 0; +} + +static uint8_t *x86_insn_decode_number(x86_insn_reader_t *rd, uint8_t number_bits, int64_t *out_number) { + int64_t disp = 0; + switch (number_bits) { + case 64: + disp = read_uint64(rd); + break; + case 32: + disp = read_uint32(rd); + break; + case 16: + disp = read_uint16(rd); + break; + case 8: + disp = read_uint8(rd); + break; + default: + UNREACHABLE(); + } + + *out_number = disp; + return NULL; +} + +void x86_insn_decode_modrm_sib(x86_insn_reader_t *rd, x86_insn_decode_t *insn, x86_options_t *conf) { + uint8_t mod, rm, reg; + + x86_insn_modrm_t modrm; + modrm.byte = read_byte(rd); + insn->modrm = modrm; + + mod = modrm.mode; + rm = (uint8_t)((REX_B(insn->rex) << 3) | modrm.rm); + reg = (uint8_t)((REX_R(insn->rex) << 3) | modrm.reg); + + x86_insn_operand_t *reg_op = &insn->operands[0]; + x86_insn_operand_t *mem_op = &insn->operands[1]; + + reg_op->reg = reg; + + if (mod == 3) { + mem_op->reg = rm; + return; + } + + uint8_t disp_bits = -1; + + insn->flags |= X86_INSN_DECODE_FLAG_IS_ADDRESS; + + uint8_t effective_address_bits; + if (conf->mode == 64) + effective_address_bits = (insn->prefix & INSN_PREFIX_ADDRESS_SIZE) ? 32 : 64; + else if (conf->mode == 32) + effective_address_bits = (insn->prefix & INSN_PREFIX_ADDRESS_SIZE) ? 16 : 32; + else { + ERROR_LOG("16-bit address mode not supported"); + } + + if (effective_address_bits == 32 || effective_address_bits == 64) { + mem_op->mem.base = rm; + + insn->flags |= X86_INSN_DECODE_FLAG_HAS_BASE; + + if (mod == 0 && (rm & 7) == 5) { + insn->flags = X86_INSN_DECODE_FLAG_IP_RELATIVE; + mem_op->mem.base = RIP; + disp_bits = 32; + } else if (mod == 0) { + disp_bits = 0; + } else if (mod == 1) { + disp_bits = 8; + } else if (mod == 2) { + disp_bits = 32; + } else { + disp_bits = 0; + } + + uint8_t has_sib = 0; + if ((rm & 7) == 4) { + ASSERT(modrm.rm == (rm & 7)); + has_sib = 1; + } + + if (has_sib) { + x86_insn_sib_t sib = {0}; + sib.byte = read_byte(rd); + insn->sib = sib; + + uint8_t base = (uint8_t)(sib.base | (REX_B(insn->rex) << 3)); + uint8_t index = (uint8_t)(sib.index | (REX_X(insn->rex) << 3)); + uint8_t scale = (uint8_t)(1 << sib.log2_scale); + + insn->flags |= X86_INSN_DECODE_FLAG_HAS_BASE; + + if (sib.index != X86_INSN_GP_REG_SP) { + insn->flags |= X86_INSN_DECODE_FLAG_HAS_INDEX; + } + + insn->operands[1].mem.base = base; + insn->operands[1].mem.index = index; + insn->operands[1].mem.scale = scale; + + if (sib.index == X86_INSN_GP_REG_SP) { + insn->operands[1].mem.index = RNone; + insn->operands[1].mem.scale = 0; + } + + // for 64 bit + if (effective_address_bits == 64) { + if (mem_op->mem.base == RBP || mem_op->mem.base == R13) { + if (mod == 0) { + mem_op->mem.base = RNone; + } + if (mod == 1) { + disp_bits = 8; + } else { + disp_bits = 32; + } + } + + if (sib.index != X86_INSN_GP_REG_SP) { + insn->flags |= X86_INSN_DECODE_FLAG_HAS_INDEX; + } + } + + // for 32 bit + if (effective_address_bits == 32) { + if (mem_op->mem.base == RBP) { + if (mod == 0) { + mem_op->mem.base = RNone; + } + if (mod == 1) { + disp_bits = 8; + } else { + disp_bits = 32; + } + } + } + } + } + + // for 16 bit + if (effective_address_bits == 16) { + switch (modrm.mode) { + case 0: + if (modrm.rm == 6) { + /* [disp16] */ + disp_bits = 16; + break; + } + /* fall through */ + case 1: + case 2: + switch (modrm.rm) { + case 0: /* [bx + si/di] */ + case 1: + mem_op->mem.base = X86_INSN_GP_REG_BX; + mem_op->mem.index = X86_INSN_GP_REG_SI + (modrm.rm & 1); + insn->flags |= X86_INSN_DECODE_FLAG_HAS_BASE | X86_INSN_DECODE_FLAG_HAS_INDEX; + break; + + case 2: /* [bp + si/di] */ + case 3: + mem_op->mem.base = X86_INSN_GP_REG_BP; + mem_op->mem.index = X86_INSN_GP_REG_SI + (modrm.rm & 1); + insn->flags |= X86_INSN_DECODE_FLAG_HAS_BASE | X86_INSN_DECODE_FLAG_HAS_INDEX; + break; + + case 4: /* [si/di] */ + case 5: + mem_op->mem.base = X86_INSN_GP_REG_SI + (modrm.rm & 1); + insn->flags |= X86_INSN_DECODE_FLAG_HAS_BASE; + break; + + case 6: /* [bp + disp] */ + mem_op->mem.base = X86_INSN_GP_REG_BP; + insn->flags |= X86_INSN_DECODE_FLAG_HAS_BASE; + break; + + case 7: /* [bx + disp] */ + mem_op->mem.base = X86_INSN_GP_REG_BX; + insn->flags |= X86_INSN_DECODE_FLAG_HAS_BASE; + break; + } + + if (modrm.mode != 0) + disp_bits = modrm.mode == 1 ? 8 : 16; + break; + } + } + + if (disp_bits != 0) { + // update displacement offset + insn->displacement_offset = (uint8_t)reader_offset(rd); + + int64_t disp; + x86_insn_decode_number(rd, disp_bits, &disp); + mem_op->mem.disp = disp; + } +} + +/* Decodes the opcode of an instruction and returns its encoding + * specification. + */ +static void x86_insn_decode_opcode(x86_insn_reader_t *rd, x86_insn_decode_t *insn, x86_options_t *conf) { + uint8_t opcode = read_byte(rd); + + x86_insn_spec_t insn_spec; + if (opcode == 0x0f) { + opcode = read_byte(rd); + insn_spec = x86_opcode_map_two_byte[opcode]; + } else { + insn_spec = x86_opcode_map_one_byte[opcode]; + } + + // check sse group + if (X86_INSN_FLAG_GET_GROUP(insn_spec.flags) > X86_INSN_SSE_GROUP_START) { + UNIMPLEMENTED(); + } + + if (X86_INSN_FLAG_GET_GROUP(insn_spec.flags) > X86_INSN_GROUP_START && + X86_INSN_FLAG_GET_GROUP(insn_spec.flags) < X86_INSN_SSE_GROUP_START) { + // get group index + int group_ndx = X86_INSN_FLAG_GET_GROUP(insn_spec.flags); + + // get gp insn index in group + x86_insn_modrm_t modrm; + modrm.byte = peek_byte(rd); + int insn_ndx = modrm.reg; + + // get insn in group + x86_insn_spec_t *group_insn = NULL; + group_insn = &x86_insn_modrm_reg_groups[group_ndx].insns[insn_ndx]; + + // update the insn spec + insn_spec.name = group_insn->name; + insn_spec.flags = group_insn->flags; + } + + insn->primary_opcode = opcode; + insn->insn_spec = insn_spec; +} + +uint8_t x86_insn_imm_bits(x86_insn_spec_t *insn, uint8_t operand_bits) { + uint8_t imm_bits = 0; + switch (x86_insn_immediate_type(insn)) { + case 'b': + imm_bits = 8; + break; + case 'w': + imm_bits = 16; + break; + case 'd': + imm_bits = 32; + break; + case 'q': + imm_bits = 64; + break; + + case 'z': + imm_bits = operand_bits; + if (imm_bits == 64) + imm_bits = 32; + break; + + case 'v': + imm_bits = operand_bits; + break; + + default: + imm_bits = 0; + break; + } + + return imm_bits; +} + +void x86_insn_decode_immediate(x86_insn_reader_t *rd, x86_insn_decode_t *insn, x86_options_t *conf) { + uint8_t effective_operand_bits; + if (conf->mode == 64 || conf->mode == 32) { + effective_operand_bits = (insn->prefix & INSN_PREFIX_OPERAND_SIZE) ? 16 : 32; + } + effective_operand_bits = (insn->prefix & INSN_PREFIX_OPERAND_SIZE) ? 16 : 32; + + if (insn->flags & X86_INSN_DECODE_FLAG_OPERAND_SIZE_64) + effective_operand_bits = 64; + + if (conf->mode == 64 && insn->insn_spec.flags & X86_INSN_SPEC_DEFAULT_64_BIT) + effective_operand_bits = 64; + + int64_t immediate = 0; + uint8_t imm_bits = x86_insn_imm_bits(&insn->insn_spec, effective_operand_bits); + if (imm_bits == 0) + return; + + // update immediate offset + insn->immediate_offset = (uint8_t)reader_offset(rd); + + x86_insn_decode_number(rd, imm_bits, &immediate); + insn->immediate = immediate; +} + +void x86_insn_decode(x86_insn_decode_t *insn, uint8_t *buffer, x86_options_t *conf) { + // init reader + x86_insn_reader_t rd; + init_reader(&rd, buffer, buffer + 15); + + // decode prefix + insn->prefix = x86_insn_decode_prefix(&rd, insn, conf); + + // decode insn specp/x in + x86_insn_decode_opcode(&rd, insn, conf); + + if (x86_insn_has_modrm_byte(&insn->insn_spec)) { + // decode insn modrm sib (operand register, disp) + x86_insn_decode_modrm_sib(&rd, insn, conf); + } + + if (x86_insn_has_immediate(&insn->insn_spec)) { + // decode insn immeidate + x86_insn_decode_immediate(&rd, insn, conf); + } + +#if 1 + DEBUG_LOG("[x86 insn] %s", insn->insn_spec.name); +#endif + + // set insn length + insn->length = rd.buffer_cursor - rd.buffer; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h new file mode 100644 index 0000000..b08ee9b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h @@ -0,0 +1,200 @@ +#ifndef X86_INSN_DECODE_H +#define X86_INSN_DECODE_H + +#include +#include "dobby/common.h" + +typedef enum { + X86_INSN_SPEC_DEFAULT_64_BIT = 1 << 0, +} x86_insn_spec_flag_t; + +typedef enum { + X86_INSN_DECODE_FLAG_HAS_BASE = 1 << 0, + + X86_INSN_DECODE_FLAG_HAS_INDEX = 1 << 1, + + X86_INSN_DECODE_FLAG_IS_ADDRESS = 1 << 2, + + X86_INSN_DECODE_FLAG_IP_RELATIVE = 1 << 3, + + X86_INSN_DECODE_FLAG_OPERAND_SIZE_64 = 1 << 4, +} x86_insn_decode_flag_t; + +typedef enum { + INSN_PREFIX_NONE = 0, + + /* Group 1: lock and repeat prefixes */ + INSN_PREFIX_GROUP1 = 0x07, + INSN_PREFIX_LOCK = 0x01, /* F0 */ + INSN_PREFIX_REPNZ = 0x02, /* F2 */ + INSN_PREFIX_REPNE = INSN_PREFIX_REPNZ, + INSN_PREFIX_REP = 0x04, /* F3 */ + INSN_PREFIX_REPZ = INSN_PREFIX_REP, + INSN_PREFIX_REPE = INSN_PREFIX_REPZ, + + /* Group 2: segment override or branch hints */ + INSN_PREFIX_GROUP2 = 0x01f8, + INSN_PREFIX_ES = 0x0008, /* 26 */ + INSN_PREFIX_CS = 0x0010, /* 2E */ + INSN_PREFIX_SS = 0x0020, /* 36 */ + INSN_PREFIX_DS = 0x0040, /* 3E */ + INSN_PREFIX_FS = 0x0080, /* 64 */ + INSN_PREFIX_GS = 0x0100, /* 65 */ + INSN_PREFIX_BRANCH_TAKEN = INSN_PREFIX_CS, /* 2E */ + INSN_PREFIX_BRANCH_NOT_TAKEN = INSN_PREFIX_DS, /* 3E */ + + /* Group 3: operand-size override */ + INSN_PREFIX_OPERAND_SIZE = 0x0200, /* 66 */ + + /* Group 4: address-size override */ + INSN_PREFIX_ADDRESS_SIZE = 0x0400 /* 67 */ +} x86_insn_prefix_t; + +typedef union { + struct { + uint8_t code; + uint8_t type; + }; + uint8_t data[2]; +} x86_insn_operand_spec_t; + +typedef struct { + // insn name + char *name; + + // insn max 3 operands + x86_insn_operand_spec_t operands[3]; + + // insn flag + uint16_t flags; +#define X86_INSN_FLAG_SET_SSE_GROUP(n) ((n) << 5) +#define X86_INSN_FLAG_GET_SSE_GROUP(f) (((f) >> 5) & 0x1f) +#define X86_INSN_FLAG_SET_MODRM_REG_GROUP(n) (((n)&0x3f) << 10) +#define X86_INSN_FLAG_GET_MODRM_REG_GROUP(f) (((f) >> 10) & 0x3f) +} x86_insn_spec_t; + +#define foreach_x86_gp_register _(AX) _(CX) _(DX) _(BX) _(SP) _(BP) _(SI) _(DI) + +typedef enum { +#define _(r) X86_INSN_GP_REG_##r, + foreach_x86_gp_register +#undef _ +} x86_insn_gp_register_t; + +typedef enum { + RNone = 0, + RAX, + RBX, + RCX, + RDX, + RDI, + RSI, + RBP, + RSP, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, + RIP +} x86_ia32e_register_t; + +typedef union { + struct { + uint8_t rm : 3; + uint8_t reg : 3; + uint8_t mode : 2; + }; + uint8_t byte; +} x86_insn_modrm_t; + +typedef union { + struct { + uint8_t base : 3; + uint8_t index : 3; + uint8_t log2_scale : 2; + }; + uint8_t byte; +} x86_insn_sib_t; + +typedef struct { + uint8_t reg; + + struct { + uint8_t base; + uint8_t index; + uint8_t scale; + uint32_t disp; + } mem; +} x86_insn_operand_t; + +typedef struct x86_insn_decode_t { + // insn flag + uint32_t flags; + + // insn length + uint32_t length; + + // insn displacement offset + uint8_t displacement_offset; + + // insn immediate offset + uint8_t immediate_offset; + + // Registers in instruction + // [0] is modrm reg field + // [1] is base reg + // [2] is index reg + // union { + // struct { + // uint8_t modrm_reg; + // uint8_t op_base_reg; + // uint8_t op_index_reg; + // }; + // uint8_t regs[3]; + // }; + + x86_insn_operand_t operands[3]; + + struct { // insn field combine + // insn prefix + x86_insn_prefix_t prefix; + + // insn rex + uint8_t rex; + + // insn primary opcode + uint8_t primary_opcode; + + // insn modrm + x86_insn_modrm_t modrm; + + // insn sib + x86_insn_sib_t sib; + + // insn operand imm + int64_t immediate; + }; + + // insn pre-spec + x86_insn_spec_t insn_spec; +} x86_insn_decode_t; + +typedef struct x86_options_t { + int mode; /* 16, 32 or 64 bit */ +} x86_options_t; + +#ifdef __cplusplus +extern "C" { +#endif + +void x86_insn_decode(x86_insn_decode_t *insn, uint8_t *buffer, x86_options_t *conf); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_reader.c b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_reader.c new file mode 100644 index 0000000..214c060 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_reader.c @@ -0,0 +1,87 @@ +/* Specialized instruction reader. */ +typedef struct x86_insn_reader_t { + const unsigned char *prefix; /* pointer to beginning of instruction */ + const unsigned char *opcode; /* pointer to opcode */ + const unsigned char *modrm; /* pointer to modrm byte */ + + unsigned char buffer[20]; /* buffer used when few bytes left */ + const unsigned char *buffer_cursor; /* pointer to buffer_cursor of insn + 1 */ +} x86_insn_reader_t; + +/* Initializes a bytecode reader to read code from a given part of memory. */ +static void init_reader(x86_insn_reader_t *rd, const unsigned char *begin, const unsigned char *buffer_cursor) { + if (buffer_cursor - begin < sizeof(rd->buffer)) { + memset(rd->buffer, 0xcc, sizeof(rd->buffer)); /* debug token */ + memcpy(rd->buffer, begin, buffer_cursor - begin); + rd->prefix = rd->buffer; + } else { + rd->prefix = begin; + } + rd->opcode = rd->modrm = rd->buffer_cursor = rd->prefix; +} + +uint32_t reader_offset(x86_insn_reader_t *rd) { + return rd->buffer_cursor - rd->buffer; +} + +static uint8_t peek_byte(const x86_insn_reader_t *rd) { + return *rd->buffer_cursor; +} + +#define read_uint8 read_byte +static uint8_t read_byte(x86_insn_reader_t *rd) { + DEBUG_LOG("[x86 insn reader] %p - 1", rd->buffer_cursor); + + const unsigned char *p = rd->buffer_cursor; + rd->buffer_cursor++; + return *p; +} + +#define read_uint16 read_word +static uint16_t read_word(x86_insn_reader_t *rd) { + DEBUG_LOG("[x86 insn reader] %p - 2", rd->buffer_cursor); + + const unsigned char *p = rd->buffer_cursor; + rd->buffer_cursor += 2; + return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8)); +} + +#define read_uint32 read_dword +static uint32_t read_dword(x86_insn_reader_t *rd) { + DEBUG_LOG("[x86 insn reader] %p - 4", rd->buffer_cursor); + + const unsigned char *p = rd->buffer_cursor; + rd->buffer_cursor += 4; + return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); +} + +#define read_uint64 read_qword +static uint64_t read_qword(x86_insn_reader_t *rd) { + DEBUG_LOG("[x86 insn reader] %p - 8", rd->buffer_cursor); + + uint64_t *p = (uint64_t *)rd->buffer_cursor; + rd->buffer_cursor += 4; + return p[0]; +} + +static uint32_t read_imm(x86_insn_reader_t *rd, int size) { + DEBUG_LOG("[x86 insn reader] %p", rd->buffer_cursor); + + return (size == 8) ? read_byte(rd) : (size == 16) ? read_word(rd) : (size == 32) ? read_dword(rd) : 0; +} + +static unsigned char read_modrm(x86_insn_reader_t *rd) { + if (rd->buffer_cursor == rd->modrm) + rd->buffer_cursor++; + return *rd->modrm; +} + +/* Marks the next byte as ModR/M. */ +static void continue_modrm(x86_insn_reader_t *rd) { + rd->modrm = rd->buffer_cursor; +} + +/* Marks the next byte as opcode. */ +static void continue_opcode(x86_insn_reader_t *rd) { + rd->modrm = rd->opcode = rd->buffer_cursor; +} diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_modrm_reg_group.c b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_modrm_reg_group.c new file mode 100644 index 0000000..352ea1a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_modrm_reg_group.c @@ -0,0 +1,218 @@ +/* Escape groups are indexed by modrm reg field. */ +static x86_insn_group8_t x86_insn_modrm_reg_groups[] = { + [X86_INSN_MODRM_REG_GROUP_1].insns = + { + op0(add), + op0(or), + op0(adc), + op0(sbb), + op0(and), + op0(sub), + op0(xor), + op0(cmp), + }, + + [X86_INSN_MODRM_REG_GROUP_1a].insns = + { + op0f(pop, X86_INSN_SPEC_DEFAULT_64_BIT), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_2].insns = + { + op0(rol), + op0(ror), + op0(rcl), + op0(rcr), + op0(shl), + op0(shr), + op0(sal), + op0(sar), + }, + + [X86_INSN_MODRM_REG_GROUP_3].insns = + { + op0(test), + op0(test), + op0(not ), + op0(neg), + op0(mul), + op0(imul), + op0(div), + op0(idiv), + }, + + [X86_INSN_MODRM_REG_GROUP_4].insns = + { + op0(inc), + op0(dec), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_5].insns = + { + op1(inc, Ev), + op1(dec, Ev), + op1f(call, X86_INSN_SPEC_DEFAULT_64_BIT, Ev), + op1(call, Mp), + op1f(jmp, X86_INSN_SPEC_DEFAULT_64_BIT, Ev), + op1(jmp, Mp), + op1f(push, X86_INSN_SPEC_DEFAULT_64_BIT, Ev), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_6].insns = + { + op1(sldt, Ev), + op1(str, Ev), + op1(lldt, Ev), + op1(ltr, Ev), + op1(verr, Ev), + op1(verw, Ev), + op0(bad), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_7].insns = + { + op1(sgdt, Mv), + op1(sidt, Mv), + op1(lgdt, Mv), + op1(lidt, Mv), + op1(smsw, Ev), + op0(bad), + op1(lmsw, Ew), + op1(invlpg, Mv), + }, + + [X86_INSN_MODRM_REG_GROUP_8].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(bt, Ev, Ib), + op2(bts, Ev, Ib), + op2(btr, Ev, Ib), + op2(btc, Ev, Ib), + }, + + [X86_INSN_MODRM_REG_GROUP_9].insns = + { + op0(bad), + op1(cmpxchg, Mx), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_10].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_11].insns = + { + op0(mov), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_12].insns = + { + op0(bad), + op0(bad), + op2(psrlw, Rm, Ib), + op0(bad), + op2(psraw, Rm, Ib), + op0(bad), + op2(psllw, Rm, Ib), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_13].insns = + { + op0(bad), + op0(bad), + op2(psrld, Rm, Ib), + op0(bad), + op2(psrad, Rm, Ib), + op0(bad), + op2(pslld, Rm, Ib), + op0(bad), + }, + + [X86_INSN_MODRM_REG_GROUP_14].insns = + { + op0(bad), + op0(bad), + op2(psrlq, Rm, Ib), + op0f(bad, 0), + op0(bad), + op0(bad), + op2(psllq, Rm, Ib), + op0f(bad, 0), + }, + + [X86_INSN_MODRM_REG_GROUP_15].insns = + { + op1(fxsave, Mv), + op1(fxrstor, Mv), + op1(ldmxcsr, Mv), + op1(stmxcsr, Mv), + op0(bad), + op1(lfence, Mv), + op1(mfence, Mv), + op1(sfence, Mv), + }, + + [X86_INSN_MODRM_REG_GROUP_16].insns = + { + op1(prefetch_nta, Mv), + op1(prefetch_t0, Mv), + op1(prefetch_t1, Mv), + op1(prefetch_t2, Mv), + op1(prefetch_nop, Mv), + op1(prefetch_nop, Mv), + op1(prefetch_nop, Mv), + op1(prefetch_nop, Mv), + }, + + [X86_INSN_MODRM_REG_GROUP_p].insns = + { + op1(prefetch_exclusive, Mv), + op1(prefetch_modified, Mv), + op1(prefetch_nop, Mv), + op1(prefetch_modified, Mv), + op1(prefetch_nop, Mv), + op1(prefetch_nop, Mv), + op1(prefetch_nop, Mv), + op1(prefetch_nop, Mv), + }, +}; diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_one_byte.c b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_one_byte.c new file mode 100644 index 0000000..44b1462 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_one_byte.c @@ -0,0 +1,215 @@ +// clang-format off +static x86_insn_spec_t x86_opcode_map_one_byte[256] = { + /* 0x00 */ + foreach_x86_operand_combine(add, op_dst, op_src), + op0(push_es), + op0f(pop_es, X86_INSN_SPEC_DEFAULT_64_BIT), + foreach_x86_operand_combine(or, op_dst, op_src), + op0f(push_cs, X86_INSN_SPEC_DEFAULT_64_BIT), + op0(escape_two_byte), + + /* 0x10 */ + foreach_x86_operand_combine(adc, op_dst, op_src), + op0(push_ss), + op0(pop_ss), + foreach_x86_operand_combine(sbb, op_dst, op_src), + op0(push_ds), + op0(pop_ds), + + /* 0x20 */ + foreach_x86_operand_combine(and, op_dst, op_src), + op0(segment_es), + op0(daa), + foreach_x86_operand_combine(sub, op_dst, op_src), + op0(segment_cs), + op0(das), + + /* 0x30 */ + foreach_x86_operand_combine(xor, op_dst, op_src), + op0(segment_ss), + op0(aaa), + foreach_x86_operand_combine(cmp, op_src, op_src), + op0(segment_ds), + op0(aas), + +/* 0x40 */ +#define _(r) op1(inc, r), + foreach_x86_gp_reg +#undef _ +#define _(r) op1(dec, r), + foreach_x86_gp_reg +#undef _ + +/* 0x50 */ +#define _(r) op1f(push, X86_INSN_SPEC_DEFAULT_64_BIT, r), + foreach_x86_gp_reg +#undef _ +#define _(r) op1f(pop, X86_INSN_SPEC_DEFAULT_64_BIT, r), + foreach_x86_gp_reg +#undef _ + + /* 0x60 */ + op0(pusha), + op0(popa), + op2(bound, Gv, Ma), + op2(movsxd, Gv, Ed), + op0(segment_fs), + op0(segment_gs), + op0(operand_type), + op0(address_size), + op1f(push, X86_INSN_SPEC_DEFAULT_64_BIT, Iz), + op3(imul, Gv, Ev, Iz), + op1f(push, X86_INSN_SPEC_DEFAULT_64_BIT, Ib), + op3(imul, Gv, Ev, Ib), + op1(insb, DX), + op1(insw, DX), + op1(outsb, DX), + op1(outsw, DX), + +/* 0x70 */ +#define _(x) op1(j##x, Jb), + foreach_x86_condition +#undef _ + + /* 0x80 */ + op2f(modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib), + op2f(modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Iz), + op2f(modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Eb, Ib), + op2f(modrm_group_1, X86_INSN_FLAG_MODRM_REG_GROUP_1, Ev, Ib), + op2(test, Eb, Gb), + op2(test, Ev, Gv), + op2(xchg, Eb, Gb), + op2(xchg, Ev, Gv), + op2(mov, Eb, Gb), + op2(mov, Ev, Gv), + op2(mov, Gb, Eb), + op2(mov, Gv, Ev), + op2(mov, Ev, Sw), + op2(lea, Gv, Ev), + op2(mov, Sw, Ew), + op1f(modrm_group_1a, X86_INSN_FLAG_MODRM_REG_GROUP_1a, Ev), + + /* 0x90 */ + op0(nop), + op1(xchg, CX), + op1(xchg, DX), + op1(xchg, BX), + op1(xchg, SP), + op1(xchg, BP), + op1(xchg, SI), + op1(xchg, DI), + op0(cbw), + op0(cwd), + op1(call, Ap), + op0(wait), + op0(pushf), + op0(popf), + op0(sahf), + op0(lahf), + + /* 0xa0 */ + op2(mov, AL, Ob), + op2(mov, AX, Ov), + op2(mov, Ob, AL), + op2(mov, Ov, AX), + op0(movsb), + op0(movsw), + op0(cmpsb), + op0(cmpsw), + op2(test, AL, Ib), + op2(test, AX, Iz), + op1(stosb, AL), + op1(stosw, AX), + op1(lodsb, AL), + op1(lodsw, AX), + op1(scasb, AL), + op1(scasw, AX), + + /* 0xb0 */ + op2(mov, AL, Ib), + op2(mov, CL, Ib), + op2(mov, DL, Ib), + op2(mov, BL, Ib), + op2(mov, AH, Ib), + op2(mov, CH, Ib), + op2(mov, DH, Ib), + op2(mov, BH, Ib), +#define _(r) op2(mov, r, Iv), + foreach_x86_gp_reg +#undef _ + + /* 0xc0 */ + op2f(modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, Ib), + op2f(modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, Ib), + op1(ret, Iw), + op0(ret), + op2(les, Gz, Mp), + op2(lds, Gz, Mp), + op2f(modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Eb, Ib), + op2f(modrm_group_11, X86_INSN_FLAG_MODRM_REG_GROUP_11, Ev, Iz), + op2(enter, Iw, Ib), + op0(leave), + op1(ret, Iw), + op0(ret), + op0(int3), + op1(int, Ib), + op0(into), + op0(iret), + + /* 0xd0 */ + op2f(modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, 1b), + op2f(modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, 1b), + op2f(modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Eb, CL), + op2f(modrm_group_2, X86_INSN_FLAG_MODRM_REG_GROUP_2, Ev, CL), + op0(aam), + op0(aad), + op0(salc), + op0(xlat), + /* FIXME x87 */ + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + + /* 0xe0 */ + op1(loopnz, Jb), + op1(loopz, Jb), + op1(loop, Jb), + op1(jcxz, Jb), + op2(in, AL, Ib), + op2(in, AX, Ib), + op2(out, Ib, AL), + op2(out, Ib, AX), + op1f(call, X86_INSN_SPEC_DEFAULT_64_BIT, Jz), + op1f(jmp, X86_INSN_SPEC_DEFAULT_64_BIT, Jz), + op1(jmp, Ap), + op1(jmp, Jb), + op2(in, AL, DX), + op2(in, AX, DX), + op2(out, DX, AL), + op2(out, DX, AX), + + /* 0xf0 */ + op0(lock), + op0(int1), + op0(repne), + op0(rep), + op0(hlt), + op0(cmc), + op0f(modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3), + op0f(modrm_group_3, X86_INSN_FLAG_MODRM_REG_GROUP_3), + op0(clc), + op0(stc), + op0(cli), + op0(sti), + op0(cld), + op0(std), + op1f(modrm_group_4, X86_INSN_FLAG_MODRM_REG_GROUP_4, Eb), + op0f(modrm_group_5, X86_INSN_FLAG_MODRM_REG_GROUP_5), +}; + +// clang-format on \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_sse_group.c b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_sse_group.c new file mode 100644 index 0000000..8e56a9b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_sse_group.c @@ -0,0 +1,545 @@ +static x86_insn_group8_t x86_insn_sse_groups_repz[] = { + [X86_INSN_SSE_GROUP_10].insns = + { + op2(movss, Gx, Ex), + op2(movss, Ex, Gx), + op2(movsldup, Gx, Ex), + op0(bad), + op0(bad), + op0(bad), + op2(movshdup, Gx, Ex), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_28].insns = + { + op0(bad), + op0(bad), + op2(cvtsi2ss, Gx, Ev), + op0(bad), + op2(cvttss2si, Gv, Ex), + op2(cvtss2si, Gv, Ex), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_50].insns = + { + op0(bad), + op2(sqrtss, Gx, Ex), + op2(rsqrtps, Gx, Ex), + op2(rcpss, Gx, Ex), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_58].insns = + { + op2(addss, Gx, Ex), + op2(mulss, Gx, Ex), + op2(cvtss2sd, Gx, Ex), + op2(cvttps2dq, Gx, Ex), + op2(subss, Gx, Ex), + op2(minss, Gx, Ex), + op2(divss, Gx, Ex), + op2(maxss, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_60].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_68].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(movdqu, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_70].insns = + { + op3(pshufhw, Gx, Ex, Ib), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_78].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(movq, Gx, Ex), + op2(movdqu, Ex, Gx), + }, + + [X86_INSN_SSE_GROUP_c0].insns = + { + op0(bad), + op0(bad), + op3(cmpss, Gx, Ex, Ib), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_d0].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(movq2dq, Gx, Em), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_d8].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_e0].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(cvtdq2pd, Gx, Ex), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_e8].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_f0].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_f8].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, +}; + +static x86_insn_group8_t x86_insn_sse_groups_operand_size[] = { + [X86_INSN_SSE_GROUP_10].insns = + { + op2(movupd, Gx, Ex), + op2(movupd, Ex, Gx), + op2(movlpd, Gx, Ex), + op2(movlpd, Ex, Gx), + op2(unpcklpd, Gx, Ex), + op2(unpckhpd, Gx, Ex), + op2(movhpd, Gx, Mx), + op2(movhpd, Mx, Gx), + }, + + [X86_INSN_SSE_GROUP_28].insns = + { + op2(movapd, Gx, Ex), + op2(movapd, Ex, Gx), + op2(cvtpi2pd, Gx, Ex), + op2(movntpd, Mx, Gx), + op2(cvttpd2pi, Gx, Mx), + op2(cvtpd2pi, Gx, Mx), + op2(ucomisd, Gx, Ex), + op2(comisd, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_50].insns = + { + op2(movmskpd, Gd, Rx), + op2(sqrtpd, Gx, Ex), + op0(bad), + op0(bad), + op2(andpd, Gx, Ex), + op2(andnpd, Gx, Ex), + op2(orpd, Gx, Ex), + op2(xorpd, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_58].insns = + { + op2(addpd, Gx, Ex), + op2(mulpd, Gx, Ex), + op2(cvtpd2ps, Gx, Ex), + op2(cvtps2dq, Gx, Ex), + op2(subpd, Gx, Ex), + op2(minpd, Gx, Ex), + op2(divpd, Gx, Ex), + op2(maxpd, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_60].insns = + { + op2(punpcklbw, Gx, Ex), + op2(punpcklwd, Gx, Ex), + op2(punpckldq, Gx, Ex), + op2(packsswb, Gx, Ex), + op2(pcmpgtb, Gx, Ex), + op2(pcmpgtw, Gx, Ex), + op2(pcmpgtd, Gx, Ex), + op2(packuswb, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_68].insns = + { + op2(punpckhbw, Gx, Ex), + op2(punpckhwd, Gx, Ex), + op2(punpckhdq, Gx, Ex), + op2(packssdw, Gx, Ex), + op2(punpcklqdq, Gx, Ex), + op2(punpckhqdq, Gx, Ex), + op2(movd, Gx, Ev), + op2(movdqa, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_70].insns = + { + op3(pshufd, Gx, Ex, Ib), + op0f(modrm_group_12, X86_INSN_FLAG_MODRM_REG_GROUP_12), + op0f(modrm_group_13, X86_INSN_FLAG_MODRM_REG_GROUP_13), + op0f(modrm_group_14, X86_INSN_FLAG_MODRM_REG_GROUP_14), + op2(pcmpeqb, Gx, Ex), + op2(pcmpeqw, Gx, Ex), + op2(pcmpeqd, Gx, Ex), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_78].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(haddpd, Gx, Ex), + op2(hsubpd, Gx, Ex), + op2(movd, Ev, Gx), + op2(movdqa, Ex, Gx), + }, + + [X86_INSN_SSE_GROUP_c0].insns = + { + op0(bad), + op0(bad), + op3(cmppd, Gx, Ex, Ib), + op0(bad), + op3(pinsrw, Gx, Ew, Ib), + op3(pextrw, Gd, Gx, Ib), + op3(shufpd, Gx, Ex, Ib), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_d0].insns = + { + op2(addsubpd, Gx, Ex), + op2(psrlw, Gx, Ex), + op2(psrld, Gx, Ex), + op2(psrlq, Gx, Ex), + op2(paddq, Gx, Ex), + op2(pmullw, Gx, Ex), + op2(movq, Ex, Gx), + op2(pmovmskb, Gd, Rx), + }, + + [X86_INSN_SSE_GROUP_d8].insns = + { + op2(psubusb, Gx, Ex), + op2(psubusw, Gx, Ex), + op2(pminub, Gx, Ex), + op2(pand, Gx, Ex), + op2(paddusb, Gx, Ex), + op2(paddusw, Gx, Ex), + op2(pmaxub, Gx, Ex), + op2(pandn, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_e0].insns = + { + op2(pavgb, Gx, Ex), + op2(psraw, Gx, Ex), + op2(psrad, Gx, Ex), + op2(pavgw, Gx, Ex), + op2(pmulhuw, Gx, Ex), + op2(pmulhw, Gx, Ex), + op2(cvttpd2dq, Gx, Ex), + op2(movntdq, Mx, Gx), + }, + + [X86_INSN_SSE_GROUP_e8].insns = + { + op2(psubsb, Gx, Ex), + op2(psubsw, Gx, Ex), + op2(pminsw, Gx, Ex), + op2(por, Gx, Ex), + op2(paddsb, Gx, Ex), + op2(paddsw, Gx, Ex), + op2(pmaxsw, Gx, Ex), + op2(pxor, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_f0].insns = + { + op0(bad), + op2(psllw, Gx, Ex), + op2(pslld, Gx, Ex), + op2(psllq, Gx, Ex), + op2(pmuludq, Gx, Ex), + op2(pmaddwd, Gx, Ex), + op2(psadbw, Gx, Ex), + op2(maskmovdqu, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_f8].insns = + { + op2(psubb, Gx, Ex), + op2(psubw, Gx, Ex), + op2(psubd, Gx, Ex), + op2(psubq, Gx, Ex), + op2(paddb, Gx, Ex), + op2(paddw, Gx, Ex), + op2(paddd, Gx, Ex), + op0(bad), + }, +}; + +static x86_insn_group8_t x86_insn_sse_groups_repnz[] = { + [X86_INSN_SSE_GROUP_10].insns = + { + op2(movsd, Gx, Ex), + op2(movsd, Ex, Gx), + op2(movddup, Gx, Ex), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_28].insns = + { + op0(bad), + op0(bad), + op2(cvtsi2sd, Gx, Ev), + op0(bad), + op2(cvttsd2si, Gv, Ex), + op2(cvtsd2si, Gv, Ex), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_50].insns = + { + op0(bad), + op2(sqrtsd, Gx, Ex), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_58].insns = + { + op2(addsd, Gx, Ex), + op2(mulsd, Gx, Ex), + op2(cvtsd2ss, Gx, Ex), + op0(bad), + op2(subsd, Gx, Ex), + op2(minsd, Gx, Ex), + op2(divsd, Gx, Ex), + op2(maxsd, Gx, Ex), + }, + + [X86_INSN_SSE_GROUP_60].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_68].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_70].insns = + { + op3(pshuflw, Gx, Ex, Ib), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_78].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(haddps, Gx, Ex), + op2(hsubps, Gx, Ex), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_c0].insns = + { + op0(bad), + op0(bad), + op3(cmpsd, Gx, Ex, Ib), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_d0].insns = + { + op2(addsubps, Gx, Ex), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(movdq2q, Gm, Ex), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_d8].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_e0].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2(cvtpd2dq, Gx, Ex), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_e8].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_f0].insns = + { + op2(lddqu, Gx, Mx), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, + + [X86_INSN_SSE_GROUP_f8].insns = + { + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + }, +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_two_byte.c b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_two_byte.c new file mode 100644 index 0000000..900d0b3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_opcode_two_byte.c @@ -0,0 +1,249 @@ + +// clang-format off +static x86_insn_spec_t x86_opcode_map_two_byte[256] = { + /* 0x00 */ + op0f(modrm_group_6, X86_INSN_FLAG_MODRM_REG_GROUP_6), + op0f(modrm_group_7, X86_INSN_FLAG_MODRM_REG_GROUP_7), + op2(lar, Gv, Ew), + op2(lsl, Gv, Ew), + op0(bad), + op0(syscall), + op0(clts), + op0(sysret), + op0(invd), + op0(wbinvd), + op0(bad), + op0(ud2), + op0(bad), + op0f(modrm_group_p, X86_INSN_FLAG_MODRM_REG_GROUP_p), + op0(femms), + op0(escape_3dnow), + + /* 0x10 */ + op2f(movups, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), + op2f(movups, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), + op2f(movlps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), + op2f(movlps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), + op2f(unpcklps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), + op2f(unpckhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), + op2f(movhps, X86_INSN_FLAG_SSE_GROUP_10, Ex, Gx), + op2f(movhps, X86_INSN_FLAG_SSE_GROUP_10, Gx, Ex), + op0f(modrm_group_16, X86_INSN_FLAG_MODRM_REG_GROUP_16), + op0(nop), + op0(nop), + op0(nop), + op0(nop), + op0(nop), + op0(nop), + op0(nop), + + /* 0x20 */ + op2(mov, Rv, Cv), + op2(mov, Rv, Dv), + op2(mov, Cv, Rv), + op2(mov, Dv, Rv), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op2f(movaps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), + op2f(movaps, X86_INSN_FLAG_SSE_GROUP_28, Ex, Gx), + op2f(cvtpi2ps, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), + op2f(movntps, X86_INSN_FLAG_SSE_GROUP_28, Mx, Gx), + op2f(cvttps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), + op2f(cvtps2pi, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), + op2f(ucomiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), + op2f(comiss, X86_INSN_FLAG_SSE_GROUP_28, Gx, Ex), + + /* 0x30 */ + op0(wrmsr), + op0(rdtsc), + op0(rdmsr), + op0(rdpmc), + op0(sysenter), + op0(sysexit), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + op0(bad), + +/* 0x40 */ +#define _(x) op2(cmov##x, Gv, Ev), + foreach_x86_condition +#undef _ + + /* 0x50 */ + op2f(movmskps, X86_INSN_FLAG_SSE_GROUP_50, Gd, Rx), + op2f(sqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), + op2f(rsqrtps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), + op2f(rcpps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), + op2f(andps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), + op2f(andnps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), + op2f(orps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), + op2f(xorps, X86_INSN_FLAG_SSE_GROUP_50, Gx, Ex), + op2f(addps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), + op2f(mulps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), + op2f(cvtps2pd, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), + op2f(cvtdq2ps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), + op2f(subps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), + op2f(minps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), + op2f(divps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), + op2f(maxps, X86_INSN_FLAG_SSE_GROUP_58, Gx, Ex), + + /* 0x60 */ + op2f(punpcklbw, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), + op2f(punpcklwd, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), + op2f(punpckldq, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), + op2f(packsswb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), + op2f(pcmpgtb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), + op2f(pcmpgtw, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), + op2f(pcmpgtd, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), + op2f(packuswb, X86_INSN_FLAG_SSE_GROUP_60, Gm, Em), + op2f(punpckhbw, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), + op2f(punpckhwd, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), + op2f(punpckhdq, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), + op2f(packssdw, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_68), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_68), + op2f(movd, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), + op2f(movq, X86_INSN_FLAG_SSE_GROUP_68, Gm, Em), + + /* 0x70 */ + op3f(pshufw, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em, Ib), + op0f(modrm_group_12, X86_INSN_FLAG_MODRM_REG_GROUP_12), + op0f(modrm_group_13, X86_INSN_FLAG_MODRM_REG_GROUP_13), + op0f(modrm_group_14, X86_INSN_FLAG_MODRM_REG_GROUP_14), + op2f(pcmpeqb, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em), + op2f(pcmpeqw, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em), + op2f(pcmpeqd, X86_INSN_FLAG_SSE_GROUP_70, Gm, Em), + op0f(emms, X86_INSN_FLAG_SSE_GROUP_70), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_78), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_78), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_78), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_78), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_78), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_78), + op2f(movd, X86_INSN_FLAG_SSE_GROUP_78, Em, Gm), + op2f(movq, X86_INSN_FLAG_SSE_GROUP_78, Em, Gm), + +/* 0x80 */ +#define _(x) op1(jmp##x, Jz), + foreach_x86_condition +#undef _ + +/* 0x90 */ +#define _(x) op1(set##x, Eb), + foreach_x86_condition +#undef _ + + /* 0xa0 */ + op0(push_fs), + op0(pop_fs), + op0(cpuid), + op2(bt, Ev, Gv), + op3(shld, Ev, Gv, Ib), + op3(shld, Ev, Gv, CL), + op0(bad), + op0(bad), + op0(push_gs), + op0(pop_gs), + op0(rsm), + op2(bts, Ev, Gv), + op3(shrd, Ev, Gv, Ib), + op3(shrd, Ev, Gv, CL), + op0f(modrm_group_15, X86_INSN_FLAG_MODRM_REG_GROUP_15), + op2(imul, Gv, Ev), + + /* 0xb0 */ + op2(cmpxchg, Eb, Gb), + op2(cmpxchg, Ev, Gv), + op2(lss, Gz, Mp), + op2(btr, Ev, Gv), + op2(lfs, Gz, Mp), + op2(lgs, Gz, Mp), + op2(movzbl, Gv, Eb), + op2(movzwl, Gv, Ew), + op0(bad), + op0f(modrm_group_10, X86_INSN_FLAG_MODRM_REG_GROUP_10), + op2f(modrm_group_8, X86_INSN_FLAG_MODRM_REG_GROUP_8, Ev, Ib), + op2(btc, Ev, Gv), + op2(bsf, Gv, Ev), + op2(bsr, Gv, Ev), + op2(movsx, Gv, Eb), + op2(movsx, Gv, Ew), + + /* 0xc0 */ + op2(xadd, Eb, Gb), + op2(xadd, Ev, Gv), + op3f(cmpps, X86_INSN_FLAG_SSE_GROUP_c0, Gx, Ex, Ib), + op2(movnti, Mv, Gv), + op3f(pinsrw, X86_INSN_FLAG_SSE_GROUP_c0, Gm, Ew, Ib), + op3f(pextrw, X86_INSN_FLAG_SSE_GROUP_c0, Gd, Rm, Ib), + op3f(shufps, X86_INSN_FLAG_SSE_GROUP_c0, Gx, Ex, Ib), + op1f(modrm_group_9, X86_INSN_FLAG_MODRM_REG_GROUP_9, Mx), +#define _(r) op1(bswap, r), + foreach_x86_gp_reg +#undef _ + + /* 0xd0 */ + op0f(bad, X86_INSN_FLAG_SSE_GROUP_d0), + op2f(psrlw, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), + op2f(psrld, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), + op2f(psrlq, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), + op2f(paddq, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), + op2f(pmullw, X86_INSN_FLAG_SSE_GROUP_d0, Gm, Em), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_d0), + op2f(pmovmskb, X86_INSN_FLAG_SSE_GROUP_d0, Gd, Rm), + op2f(psubusb, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), + op2f(psubusw, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), + op2f(pminub, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), + op2f(pand, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), + op2f(paddusb, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), + op2f(paddusw, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), + op2f(pmaxub, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), + op2f(pandn, X86_INSN_FLAG_SSE_GROUP_d8, Gm, Em), + + /* 0xe0 */ + op2f(pavgb, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), + op2f(psraw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), + op2f(psrad, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), + op2f(pavgw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), + op2f(pmulhuw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), + op2f(pmulhw, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), + op2f(bad, X86_INSN_FLAG_SSE_GROUP_e0, Gm, Em), + op2f(movntq, X86_INSN_FLAG_SSE_GROUP_e0, Mm, Gm), + op2f(psubsb, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), + op2f(psubsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), + op2f(pminsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), + op2f(por, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), + op2f(paddsb, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), + op2f(paddsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), + op2f(pmaxsw, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), + op2f(pxor, X86_INSN_FLAG_SSE_GROUP_e8, Gm, Em), + + /* 0xf0 */ + op0f(bad, X86_INSN_FLAG_SSE_GROUP_f0), + op2f(psllw, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), + op2f(pslld, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), + op2f(psllq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), + op2f(pmuludq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), + op2f(pmaddwd, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), + op2f(psadbw, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), + op2f(maskmovq, X86_INSN_FLAG_SSE_GROUP_f0, Gm, Em), + op2f(psubb, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), + op2f(psubw, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), + op2f(psubd, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), + op2f(psubq, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), + op2f(paddb, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), + op2f(paddw, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), + op2f(paddd, X86_INSN_FLAG_SSE_GROUP_f8, Gm, Em), + op0f(bad, X86_INSN_FLAG_SSE_GROUP_f8), +}; + +// clang-format on \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InterceptEntry.cpp b/app/src/main/cpp/Dobby/source/InterceptEntry.cpp new file mode 100644 index 0000000..9a84405 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptEntry.cpp @@ -0,0 +1,16 @@ +#include "InterceptEntry.h" +#include "Interceptor.h" + +InterceptEntry::InterceptEntry(InterceptEntryType type, addr_t address) { + this->type = type; + +#if defined(TARGET_ARCH_ARM) + if (address % 2) { + address -= 1; + this->thumb_mode = true; + } +#endif + + this->patched_addr = address; + this->id = Interceptor::SharedInstance()->count(); +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InterceptEntry.h b/app/src/main/cpp/Dobby/source/InterceptEntry.h new file mode 100644 index 0000000..2955635 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptEntry.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include "dobby/common.h" + +typedef enum { kFunctionInlineHook, kInstructionInstrument } InterceptEntryType; + +class InterceptRouting; + +typedef struct InterceptEntry { + uint32_t id; + InterceptEntryType type; + InterceptRouting *routing; + + union { + addr_t addr; + addr_t patched_addr; + }; + uint32_t patched_size; + + addr_t relocated_addr; + uint32_t relocated_size; + + uint8_t origin_insns[256]; + uint32_t origin_insn_size; + + bool thumb_mode; + + InterceptEntry(InterceptEntryType type, addr_t address); +} InterceptEntry; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.cpp b/app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.cpp new file mode 100644 index 0000000..5299e62 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.cpp @@ -0,0 +1,97 @@ +#include "dobby/dobby_internal.h" + +#include "InterceptRouting/InterceptRouting.h" +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +using namespace zz; + +void log_hex_format(uint8_t *buffer, uint32_t buffer_size) { + char output[1024] = {0}; + for (int i = 0; i < buffer_size && i < sizeof(output); i++) { + snprintf(output + strlen(output), 3, "%02x ", *((uint8_t *)buffer + i)); + } + DEBUG_LOG("%s", output); +}; + +void InterceptRouting::Prepare() { +} + +// generate relocated code +bool InterceptRouting::GenerateRelocatedCode() { + uint32_t tramp_size = GetTrampolineBuffer()->GetBufferSize(); + origin_ = new CodeMemBlock(entry_->patched_addr, tramp_size); + relocated_ = new CodeMemBlock(); + + auto buffer = (void *)entry_->patched_addr; +#if defined(TARGET_ARCH_ARM) + if (entry_->thumb_mode) { + buffer = (void *)((addr_t)buffer + 1); + } +#endif + GenRelocateCodeAndBranch(buffer, origin_, relocated_); + if (relocated_->size == 0) { + ERROR_LOG("[insn relocate]] failed"); + return false; + } + + // set the relocated instruction address + entry_->relocated_addr = relocated_->addr; + + // save original prologue + memcpy((void *)entry_->origin_insns, (void *)origin_->addr, origin_->size); + entry_->origin_insn_size = origin_->size; + + // log + DEBUG_LOG("[insn relocate] origin %p - %d", origin_->addr, origin_->size); + log_hex_format((uint8_t *)origin_->addr, origin_->size); + + DEBUG_LOG("[insn relocate] relocated %p - %d", relocated_->addr, relocated_->size); + log_hex_format((uint8_t *)relocated_->addr, relocated_->size); + + return true; +} + +bool InterceptRouting::GenerateTrampolineBuffer(addr_t src, addr_t dst) { + // if near branch trampoline plugin enabled + if (RoutingPluginManager::near_branch_trampoline) { + auto plugin = static_cast(RoutingPluginManager::near_branch_trampoline); + if (plugin->GenerateTrampolineBuffer(this, src, dst) == false) { + DEBUG_LOG("Failed enable near branch trampoline plugin"); + } + } + + if (GetTrampolineBuffer() == nullptr) { + auto tramp_buffer = GenerateNormalTrampolineBuffer(src, dst); + SetTrampolineBuffer(tramp_buffer); + } + return true; +} + +// active routing, patch origin instructions as trampoline +void InterceptRouting::Active() { + auto ret = DobbyCodePatch((void *)entry_->patched_addr, trampoline_buffer_->GetBuffer(), + trampoline_buffer_->GetBufferSize()); + if (ret == -1) { + ERROR_LOG("[intercept routing] active failed"); + return; + } + DEBUG_LOG("[intercept routing] active"); +} + +void InterceptRouting::Commit() { + this->Active(); +} + +#if 0 +int InterceptRouting::PredefinedTrampolineSize() { +#if __arm64__ + return 12; +#elif __arm__ + return 8; +#endif +} +#endif + +InterceptEntry *InterceptRouting::GetInterceptEntry() { + return entry_; +}; diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.h b/app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.h new file mode 100644 index 0000000..e21ca32 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.h @@ -0,0 +1,62 @@ +#pragma once + +#include "InterceptEntry.h" +#include "MemoryAllocator/AssemblyCodeBuilder.h" +#include "InstructionRelocation/InstructionRelocation.h" +#include "TrampolineBridge/Trampoline/Trampoline.h" + +class InterceptRouting { +public: + explicit InterceptRouting(InterceptEntry *entry) : entry_(entry) { + entry->routing = this; + + origin_ = nullptr; + relocated_ = nullptr; + + trampoline_ = nullptr; + trampoline_buffer_ = nullptr; + trampoline_target_ = 0; + } + + virtual void DispatchRouting() = 0; + + virtual void Prepare(); + + virtual void Active(); + + void Commit(); + + InterceptEntry *GetInterceptEntry(); + + void SetTrampolineBuffer(CodeBufferBase *buffer) { + trampoline_buffer_ = buffer; + } + + CodeBufferBase *GetTrampolineBuffer() { + return trampoline_buffer_; + } + + void SetTrampolineTarget(addr_t address) { + trampoline_target_ = address; + } + + addr_t GetTrampolineTarget() { + return trampoline_target_; + } + +protected: + bool GenerateRelocatedCode(); + + bool GenerateTrampolineBuffer(addr_t src, addr_t dst); + +protected: + InterceptEntry *entry_; + + CodeMemBlock *origin_; + CodeMemBlock *relocated_; + + CodeMemBlock *trampoline_; + // trampoline buffer before active + CodeBufferBase *trampoline_buffer_; + addr_t trampoline_target_; +}; diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHook.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHook.cc new file mode 100644 index 0000000..8694f01 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHook.cc @@ -0,0 +1,52 @@ +#include "dobby/dobby_internal.h" + +#include "Interceptor.h" +#include "InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHookRouting.h" + +PUBLIC int DobbyHook(void *address, dobby_dummy_func_t replace_func, dobby_dummy_func_t *origin_func) { + if (!address) { + ERROR_LOG("function address is 0x0"); + return -1; + } + +#if defined(__APPLE__) && defined(__arm64__) + address = pac_strip(address); + replace_func = pac_strip(replace_func); +#endif + +#if defined(ANDROID) + void *page_align_address = (void *)ALIGN_FLOOR(address, OSMemory::PageSize()); + if (!OSMemory::SetPermission(page_align_address, OSMemory::PageSize(), kReadExecute)) { + return -1; + } +#endif + + DEBUG_LOG("----- [DobbyHook:%p] -----", address); + + // check if already register + auto entry = Interceptor::SharedInstance()->find((addr_t)address); + if (entry) { + ERROR_LOG("%p already been hooked.", address); + return -1; + } + + entry = new InterceptEntry(kFunctionInlineHook, (addr_t)address); + + auto *routing = new FunctionInlineHookRouting(entry, replace_func); + routing->Prepare(); + routing->DispatchRouting(); + + // set origin func entry with as relocated instructions + if (origin_func) { + *origin_func = (dobby_dummy_func_t)entry->relocated_addr; +#if defined(__APPLE__) && defined(__arm64__) + *origin_func = pac_sign(*origin_func); +#endif + } + + routing->Commit(); + + Interceptor::SharedInstance()->add(entry); + + return 0; +} diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHookRouting.h b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHookRouting.h new file mode 100644 index 0000000..65722c2 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHookRouting.h @@ -0,0 +1,22 @@ +#pragma once + +#include "dobby/dobby_internal.h" + +#include "InterceptRouting/InterceptRouting.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +class FunctionInlineHookRouting : public InterceptRouting { +public: + FunctionInlineHookRouting(InterceptEntry *entry, dobby_dummy_func_t replace_func) : InterceptRouting(entry) { + this->replace_func = replace_func; + } + + void DispatchRouting() override; + +private: + void BuildRouting(); + +private: + dobby_dummy_func_t replace_func; +}; diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/RoutingImpl.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/RoutingImpl.cc new file mode 100644 index 0000000..4408d55 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/RoutingImpl.cc @@ -0,0 +1,22 @@ +#include "dobby/dobby_internal.h" +#include "InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHookRouting.h" + +void FunctionInlineHookRouting::BuildRouting() { + SetTrampolineTarget((addr_t)replace_func); + + // generate trampoline buffer, run before GenerateRelocatedCode + addr_t from = entry_->patched_addr; +#if defined(TARGET_ARCH_ARM) + if (entry_->thumb_mode) + from += 1; +#endif + addr_t to = GetTrampolineTarget(); + GenerateTrampolineBuffer(from, to); +} + +void FunctionInlineHookRouting::DispatchRouting() { + BuildRouting(); + + // generate relocated code which size == trampoline size + GenerateRelocatedCode(); +} diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/FunctionWrapperExport.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/FunctionWrapperExport.cc new file mode 100644 index 0000000..20232b0 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/FunctionWrapperExport.cc @@ -0,0 +1,27 @@ +#include "dobby/dobby_internal.h" + +#include "logging/logging.h" + +#include "Interceptor.h" +#include "InterceptRouting/InterceptRouting.h" + +#include "function-wrapper.h" + +PUBLIC int DobbyWrap(void *function_address, PreCallTy pre_call, PostCallTy post_call) { + DEBUG_LOG("Initialize 'DobbyWrap' hook at %p", function_address); + + Interceptor *interceptor = Interceptor::SharedInstance(); + + InterceptEntry *entry = new InterceptEntry(); + entry->id = interceptor->entries->getCount(); + entry->type = kFunctionWrapper; + entry->function_address = function_address; + + FunctionWrapperRouting *routing = new FunctionWrapperRouting(entry); + routing->DispatchRouting(); + interceptor->addHookEntry(entry); + routing->Commit(); + + DEBUG_LOG("Finalize %p", function_address); + return 0; +} diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.cc new file mode 100644 index 0000000..6a8a294 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.cc @@ -0,0 +1,38 @@ +#include "dobby/dobby_internal.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +#include "intercept_routing_handler.h" + +#include "function-wrapper.h" + +void FunctionWrapperRouting::DispatchRouting() { + Prepare(); + BuildPreCallRouting(); + BuildPostCallRouting(); +} + +// Add pre_call(prologue) handler before running the origin function, +void FunctionWrapperRouting::BuildPreCallRouting() { + // create closure trampoline jump to prologue_routing_dispath with the `entry_` data + ClosureTrampolineEntry *cte = ClosureTrampoline::CreateClosureTrampoline(entry_, (void *)prologue_routing_dispatch); + this->prologue_dispatch_bridge = cte->address; + + DEBUG_LOG("Create pre call closure trampoline to 'prologue_routing_dispatch' at %p", cte->address); +} + +// Add post_call(epilogue) handler before `Return` of the origin function, as implementation is replace the origin +// `Return Address` of the function. +void FunctionWrapperRouting::BuildPostCallRouting() { + // create closure trampoline jump to prologue_routing_dispath with the `entry_` data + ClosureTrampolineEntry *closure_trampoline_entry; + // format trampoline + closure_trampoline_entry = ClosureTrampoline::CreateClosureTrampoline(entry_, (void *)epilogue_routing_dispatch); + DEBUG_LOG("Create post call closure trampoline to 'prologue_routing_dispatch' at %p", + closure_trampoline_entry->address); + + this->SetTrampolineTarget(closure_trampoline_entry->address); + this->epilogue_dispatch_bridge = closure_trampoline_entry->address; + + GenerateTrampolineBuffer(entry_->target_address, GetTrampolineTarget()); +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.h b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.h new file mode 100644 index 0000000..362b750 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.h @@ -0,0 +1,40 @@ +#ifndef FUNCTION_WRAPPER_H +#define FUNCTION_WRAPPER_H + +#include "dobby/dobby_internal.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" +#include "InterceptRouting/InterceptRouting.h" +#include "Interceptor.h" + +#if TARGET_ARCH_IA32 +#elif TARGET_ARCH_X64 +#include "InterceptRouting/x64/X64InterceptRouting.h" +#elif TARGET_ARCH_ARM64 +#include "InterceptRouting/arm64/ARM64InterceptRouting.h" +#elif TARGET_ARCH_ARM +#else +#error "unsupported architecture" +#endif + +class FunctionWrapperRouting : public InterceptRouting { +public: + FunctionWrapperRouting(InterceptEntry *entry) : InterceptRouting(entry) { + } + + void DispatchRouting(); + + void *GetTrampolineTarget(); + +private: + void BuildPreCallRouting(); + + void BuildPostCallRouting(); + +private: + void *prologue_dispatch_bridge; + + void *epilogue_dispatch_bridge; +}; + +#endif diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.cc new file mode 100644 index 0000000..55de2fb --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.cc @@ -0,0 +1,79 @@ + +#include "dobby/dobby_internal.h" + +#include "logging/logging.h" + +#include "intercept_routing_handler.h" + +#include "function-wrapper.h" +#include "intercept_routing_handler.h" + +#include "MultiThreadSupport/ThreadSupport.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h" + +void pre_call_forward_handler(DobbyRegisterContext *ctx, InterceptEntry *entry) { + FunctionWrapperRouting *routing = (FunctionWrapperRouting *)entry->routing; + + StackFrame *stackframe = new StackFrame(); + // create stack frame as common variable between pre_call and post_call + ThreadSupport::PushStackFrame(stackframe); + + // run the `pre_call` before execute origin function which has been relocated(fixed) + if (routing->pre_call) { + PreCallTy pre_call; + InterceptEntry entry; + entry.hook_id = entry->id; + entry.target_address = entry->target_address; + pre_call = routing->pre_call; + // run the pre_call with the power of accessing all registers + (*pre_call)(ctx, (const InterceptEntry *)&entry); + } + + // save the origin ret address, and use in `post_call_forword_handler` + stackframe->orig_ret = get_func_ret_address(ctx); + + // set the prologue bridge next hop address with the patched instructions has been relocated + set_routing_bridge_next_hop(ctx, entry->relocated_origin_function); + + // replace the function ret address with our epilogue_routing_dispatch + set_func_ret_address(ctx, entry->epilogue_dispatch_bridge); +} + +void post_call_forward_handler(DobbyRegisterContext *ctx, InterceptEntry *entry) { + FunctionWrapperRouting *routing = (FunctionWrapperRouting *)entry->routing; + + // pop stack frame as common variable between pre_call and post_call + StackFrame *stackframe = ThreadSupport::PopStackFrame(); + + // run the `post_call`, and access all the register value, as the origin function done, + if (routing->post_call) { + PostCallTy post_call; + InterceptEntry entry; + entry.hook_id = entry->id; + entry.target_address = entry->target_address; + post_call = routing->post_call; + + // run the post_call with the power of accessing all registers + (*post_call)(ctx, (const InterceptEntry *)&entry); + } + + // set epilogue bridge next hop address with origin ret address, restore the call. + set_routing_bridge_next_hop(ctx, stackframe->orig_ret); +} + +// run the user handler **before run the origin-instructions(which have been relocated)** +void prologue_routing_dispatch(DobbyRegisterContext *ctx, ClosureTrampolineEntry *closure_trampoline_entry) { + DEBUG_LOG("Catch prologue dispatch"); + InterceptEntry *entry = static_cast(closure_trampoline_entry->carry_data); + pre_call_forward_handler(ctx, entry); + return; +} + +// run the user handler **before the function return** by replace the lr register +void epilogue_routing_dispatch(DobbyRegisterContext *ctx, ClosureTrampolineEntry *closure_trampoline_entry) { + DEBUG_LOG("Catch epilogue dispatch"); + InterceptEntry *entry = static_cast(closure_trampoline_entry->carry_data); + post_call_forward_handler(ctx, entry); + return; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.h b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.h new file mode 100644 index 0000000..6aaea3f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.h @@ -0,0 +1,27 @@ +#ifndef FUNCTION_WRAPPER_INTERCEPT_ROUTING_HANDLER_H +#define FUNCTION_WRAPPER_INTERCEPT_ROUTING_HANDLER_H + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" +#include "Interceptor.h" +#include "dobby/dobby_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +// Dispatch the routing befor running the origin function +void prologue_routing_dispatch(DobbyRegisterContext *ctx, ClosureTrampolineEntry *entry); + +// Dispatch the routing before the function return . (as it's implementation by relpace `return address` in the stack +// ,or LR register) +void epilogue_routing_dispatch(DobbyRegisterContext *ctx, ClosureTrampolineEntry *entry); + +void pre_call_forward_handler(DobbyRegisterContext *ctx, InterceptEntry *entry); + +void post_call_forward_handler(DobbyRegisterContext *ctx, InterceptEntry *entry); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrument.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrument.cc new file mode 100644 index 0000000..9c28ca3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrument.cc @@ -0,0 +1,42 @@ +#include "dobby/dobby_internal.h" + +#include "Interceptor.h" +#include "InterceptRouting/InterceptRouting.h" +#include "InterceptRouting/Routing/InstructionInstrument/InstructionInstrumentRouting.h" + +PUBLIC int DobbyInstrument(void *address, dobby_instrument_callback_t pre_handler) { + if (!address) { + ERROR_LOG("address is 0x0.\n"); + return -1; + } + +#if defined(__APPLE__) && defined(__arm64__) + address = pac_strip(address); +#endif + +#if defined(ANDROID) + void *page_align_address = (void *)ALIGN_FLOOR(address, OSMemory::PageSize()); + if (!OSMemory::SetPermission(page_align_address, OSMemory::PageSize(), kReadExecute)) { + return -1; + } +#endif + + DEBUG_LOG("\n\n----- [DobbyInstrument:%p] -----", address); + + auto entry = Interceptor::SharedInstance()->find((addr_t)address); + if (entry) { + ERROR_LOG("%s already been instrumented.", address); + return -1; + } + + entry = new InterceptEntry(kInstructionInstrument, (addr_t)address); + + auto routing = new InstructionInstrumentRouting(entry, pre_handler, nullptr); + routing->Prepare(); + routing->DispatchRouting(); + routing->Commit(); + + Interceptor::SharedInstance()->add(entry); + + return 0; +} diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrumentRouting.h b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrumentRouting.h new file mode 100644 index 0000000..60a9a2e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrumentRouting.h @@ -0,0 +1,30 @@ +#pragma once + +#include "dobby/dobby_internal.h" + +#include "InterceptRouting/InterceptRouting.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +class InstructionInstrumentRouting : public InterceptRouting { +public: + InstructionInstrumentRouting(InterceptEntry *entry, dobby_instrument_callback_t pre_handler, + dobby_instrument_callback_t post_handler) + : InterceptRouting(entry) { + this->prologue_dispatch_bridge = nullptr; + this->pre_handler = pre_handler; + this->post_handler = post_handler; + } + + void DispatchRouting() override; + +private: + void BuildRouting(); + +public: + dobby_instrument_callback_t pre_handler; + dobby_instrument_callback_t post_handler; + +private: + void *prologue_dispatch_bridge; +}; diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/RoutingImpl.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/RoutingImpl.cc new file mode 100644 index 0000000..bd12545 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/RoutingImpl.cc @@ -0,0 +1,40 @@ + +#include "dobby/dobby_internal.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +#include "InterceptRouting/Routing/InstructionInstrument/InstructionInstrumentRouting.h" +#include "InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.h" + +// create closure trampoline jump to prologue_routing_dispatch with the `entry_` data +void InstructionInstrumentRouting::BuildRouting() { + void *handler = (void *)instrument_routing_dispatch; +#if defined(__APPLE__) && defined(__arm64__) + handler = pac_strip(handler); +#endif + auto closure_trampoline = ClosureTrampoline::CreateClosureTrampoline(entry_, handler); + this->SetTrampolineTarget((addr_t)closure_trampoline->address); + DEBUG_LOG("[closure trampoline] closure trampoline: %p, data: %p", closure_trampoline->address, entry_); + + // generate trampoline buffer, before `GenerateRelocatedCode` + addr_t from = entry_->patched_addr; +#if defined(TARGET_ARCH_ARM) + if (entry_->thumb_mode) + from += 1; +#endif + addr_t to = GetTrampolineTarget(); + GenerateTrampolineBuffer(from, to); +} + +void InstructionInstrumentRouting::DispatchRouting() { + BuildRouting(); + + // generate relocated code which size == trampoline size + GenerateRelocatedCode(); +} + +#if 0 +void *InstructionInstrumentRouting::GetTrampolineTarget() { + return this->prologue_dispatch_bridge; +} +#endif diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.cc new file mode 100644 index 0000000..3e38122 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.cc @@ -0,0 +1,21 @@ +#include "dobby/dobby_internal.h" + +#include "InterceptRouting/Routing/InstructionInstrument/InstructionInstrumentRouting.h" +#include "InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h" + +void instrument_forward_handler(InterceptEntry *entry, DobbyRegisterContext *ctx) { + auto routing = static_cast(entry->routing); + if (routing->pre_handler) { + auto handler = (dobby_instrument_callback_t)routing->pre_handler; + (*handler)((void *)entry->patched_addr, ctx); + } + + // set prologue bridge next hop address as relocated instructions + set_routing_bridge_next_hop(ctx, (void *)entry->relocated_addr); +} + +void instrument_routing_dispatch(InterceptEntry *entry, DobbyRegisterContext *ctx) { + instrument_forward_handler(entry, ctx); +} diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.h b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.h new file mode 100644 index 0000000..640bfec --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.h @@ -0,0 +1,7 @@ +#pragma once + +#include "dobby/dobby_internal.h" + +extern "C" { +void instrument_routing_dispatch(InterceptEntry *entry, DobbyRegisterContext *ctx); +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.cc new file mode 100644 index 0000000..fab0a12 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.cc @@ -0,0 +1,50 @@ +#include "InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.h" + +#include "dobby/dobby_internal.h" + +#include "MemoryAllocator/NearMemoryAllocator.h" + +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +using namespace zz; + +PUBLIC void dobby_enable_near_branch_trampoline() { + RoutingPluginInterface *plugin = new NearBranchTrampolinePlugin; + RoutingPluginManager::registerPlugin("near_branch_trampoline", plugin); + RoutingPluginManager::near_branch_trampoline = plugin; +} + +PUBLIC void dobby_disable_near_branch_trampoline() { + NearBranchTrampolinePlugin *plugin = (NearBranchTrampolinePlugin *)RoutingPluginManager::near_branch_trampoline; + delete plugin; + RoutingPluginManager::near_branch_trampoline = NULL; +} + +#if 0 +int NearBranchTrampolinePlugin::PredefinedTrampolineSize() { +#if __arm64__ + return 4; +#elif __arm__ + return 4; +#endif +} +#endif + +extern CodeBufferBase *GenerateNearTrampolineBuffer(InterceptRouting *routing, addr_t from, addr_t to); +bool NearBranchTrampolinePlugin::GenerateTrampolineBuffer(InterceptRouting *routing, addr_t src, addr_t dst) { + CodeBufferBase *trampoline_buffer; + trampoline_buffer = GenerateNearTrampolineBuffer(routing, src, dst); + if (trampoline_buffer == NULL) + return false; + routing->SetTrampolineBuffer(trampoline_buffer); + return true; +} + +// generate trampoline, patch the original entry +bool NearBranchTrampolinePlugin::Active(InterceptRouting *routing) { + addr_t src, dst; + InterceptEntry *entry = routing->GetInterceptEntry(); + src = (addr_t)entry->patched_addr; + dst = (addr_t)routing->GetTrampolineTarget(); + return true; +} diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.h b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.h new file mode 100644 index 0000000..58bee51 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.h @@ -0,0 +1,15 @@ +#pragma once + +#include "dobby/dobby_internal.h" + +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +class NearBranchTrampolinePlugin : public RoutingPluginInterface { + bool Prepare(InterceptRouting *routing) { + return false; + }; + + bool Active(InterceptRouting *routing); + + bool GenerateTrampolineBuffer(InterceptRouting *routing, addr_t src, addr_t dst); +}; diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/near_trampoline_arm64.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/near_trampoline_arm64.cc new file mode 100644 index 0000000..4b16c92 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/near_trampoline_arm64.cc @@ -0,0 +1,81 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-arm64.h" +#include "core/codegen/codegen-arm64.h" + +#include "MemoryAllocator/NearMemoryAllocator.h" +#include "InstructionRelocation/arm64/InstructionRelocationARM64.h" +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +using namespace zz::arm64; + +#define ARM64_B_XXX_RANGE ((1 << 25) << 2) // signed + +// If BranchType is B_Branch and the branch_range of `B` is not enough +// build the transfer to forward the b branch +static AssemblyCode *GenerateFastForwardTrampoline(addr_t src, addr_t dst) { + TurboAssembler turbo_assembler_(nullptr); +#define _ turbo_assembler_. + + // [adrp + add + br branch] + auto tramp_size = 3 * 4; + auto tramp_mem = NearMemoryAllocator::SharedAllocator()->allocateNearExecMemory(tramp_size, src, ARM64_B_XXX_RANGE); + if (tramp_mem == nullptr) { + ERROR_LOG("search near code block failed"); + return nullptr; + } + + // Use adrp + add branch + uint64_t distance = llabs((int64_t)(tramp_mem - dst)); + uint64_t adrp_range = ((uint64_t)1 << (2 + 19 + 12 - 1)); + if (distance < adrp_range) { + // use adrp + add + br branch == (3 * 4) trampoline size + _ AdrpAdd(TMP_REG_0, (uint64_t)tramp_mem, dst); + _ br(TMP_REG_0); + DEBUG_LOG("forward trampoline use [adrp, add, br]"); + } else { + // use mov + br == (4 * 5) trampoline size + _ Mov(TMP_REG_0, dst); + _ br(TMP_REG_0); + DEBUG_LOG("forward trampoline use [mov, br]"); + + auto tramp_size = turbo_assembler_.GetCodeBuffer()->GetBufferSize(); + tramp_mem = NearMemoryAllocator::SharedAllocator()->allocateNearExecMemory(tramp_size, src, ARM64_B_XXX_RANGE); + if (tramp_mem == nullptr) { + ERROR_LOG("Can't found near code chunk"); + return nullptr; + } + } + + turbo_assembler_.SetRealizedAddress((void *)tramp_mem); + + AssemblyCode *code = nullptr; + code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + return code; +} + +CodeBufferBase *GenerateNearTrampolineBuffer(InterceptRouting *routing, addr_t src, addr_t dst) { + CodeBufferBase *result = nullptr; + + TurboAssembler turbo_assembler_((void *)src); +#define _ turbo_assembler_. + + // branch to trampoline_target directly + if (llabs((long long)dst - (long long)src) < ARM64_B_XXX_RANGE) { + _ b(dst - src); + } else { + auto fast_forward_trampoline = GenerateFastForwardTrampoline(src, dst); + if (!fast_forward_trampoline) + return nullptr; + _ b(fast_forward_trampoline->addr - src); + } + + // free the original trampoline + result = turbo_assembler_.GetCodeBuffer()->Copy(); + return result; +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.cc b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.cc new file mode 100644 index 0000000..e6e8ffd --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.cc @@ -0,0 +1,11 @@ +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +tinystl::vector RoutingPluginManager::plugins; + +RoutingPluginInterface *RoutingPluginManager::near_branch_trampoline = NULL; + +void RoutingPluginManager::registerPlugin(const char *name, RoutingPluginInterface *plugin) { + DEBUG_LOG("register %s plugin", name); + + RoutingPluginManager::plugins.push_back(plugin); +} diff --git a/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.h b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.h new file mode 100644 index 0000000..17ac97e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.h @@ -0,0 +1,30 @@ +#pragma once + +#include "dobby/dobby_internal.h" + +#include "InterceptRouting/InterceptRouting.h" + +class RoutingPluginInterface { +public: + // @Return: if false will continue to iter next plugin + virtual bool Prepare(InterceptRouting *routing) = 0; + + // @Return: if false will continue to iter next plugin + virtual bool Active(InterceptRouting *routing) = 0; + + // @Return: if false will continue to iter next plugin + virtual bool GenerateTrampolineBuffer(InterceptRouting *routing, addr_t src, addr_t dst) = 0; + +private: + char name_[256]; +}; + +class RoutingPluginManager { +public: + static void registerPlugin(const char *name, RoutingPluginInterface *plugin); + +public: + static tinystl::vector plugins; + + static RoutingPluginInterface *near_branch_trampoline; +}; diff --git a/app/src/main/cpp/Dobby/source/Interceptor.cpp b/app/src/main/cpp/Dobby/source/Interceptor.cpp new file mode 100644 index 0000000..7cfe113 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Interceptor.cpp @@ -0,0 +1,40 @@ +#include "Interceptor.h" + +Interceptor *Interceptor::instance = nullptr; + +Interceptor *Interceptor::SharedInstance() { + if (Interceptor::instance == nullptr) { + Interceptor::instance = new Interceptor(); + } + return Interceptor::instance; +} + +InterceptEntry *Interceptor::find(addr_t addr) { + for (auto *entry : entries) { + if (entry->patched_addr == addr) { + return entry; + } + } + return nullptr; +} + +void Interceptor::add(InterceptEntry *entry) { + entries.push_back(entry); +} + +void Interceptor::remove(addr_t addr) { + for (auto iter = entries.begin(); iter != entries.end(); iter++) { + if ((*iter)->patched_addr == addr) { + entries.erase(iter); + break; + } + } +} + +const InterceptEntry *Interceptor::getEntry(int i) { + return entries[i]; +} + +int Interceptor::count() { + return entries.size(); +} diff --git a/app/src/main/cpp/Dobby/source/Interceptor.h b/app/src/main/cpp/Dobby/source/Interceptor.h new file mode 100644 index 0000000..467a229 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Interceptor.h @@ -0,0 +1,25 @@ +#pragma once + +#include "dobby/dobby_internal.h" +#include "InterceptEntry.h" + +class Interceptor { +public: + static Interceptor *SharedInstance(); + +public: + InterceptEntry *find(addr_t addr); + + void remove(addr_t addr); + + void add(InterceptEntry *entry); + + const InterceptEntry *getEntry(int i); + + int count(); + +private: + static Interceptor *instance; + + tinystl::vector entries; +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.cc b/app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.cc new file mode 100644 index 0000000..f0bffea --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.cc @@ -0,0 +1,34 @@ +#include "MemoryAllocator/AssemblyCodeBuilder.h" + +#include "dobby/dobby_internal.h" +#include "PlatformUnifiedInterface/ExecMemory/CodePatchTool.h" + +AssemblyCode *AssemblyCodeBuilder::FinalizeFromTurboAssembler(AssemblerBase *assembler) { + auto buffer = (CodeBufferBase *)assembler->GetCodeBuffer(); + auto realized_addr = (addr_t)assembler->GetRealizedAddress(); +#if defined(TEST_WITH_UNICORN) + // impl: unicorn emulator map memory + realized_addr = 0; +#endif + if (!realized_addr) { + size_t buffer_size = 0; + buffer_size = buffer->GetBufferSize(); +#if TARGET_ARCH_ARM + // extra bytes for align needed + buffer_size += 4; +#endif + + auto block = MemoryAllocator::SharedAllocator()->allocateExecBlock(buffer_size); + if (block == nullptr) + return nullptr; + + realized_addr = block->addr; + assembler->SetRealizedAddress((void *)realized_addr); + } + + // Realize the buffer code to the executable memory address, remove the external label, etc + DobbyCodePatch((void *)realized_addr, buffer->GetBuffer(), buffer->GetBufferSize()); + + auto block = new AssemblyCode(realized_addr, buffer->GetBufferSize()); + return block; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.h new file mode 100644 index 0000000..d704c56 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.h @@ -0,0 +1,14 @@ +#pragma once + +#include "PlatformUnifiedInterface/MemoryAllocator.h" + +#include "core/assembler/assembler.h" + +using namespace zz; + +using AssemblyCode = CodeMemBlock; + +class AssemblyCodeBuilder { +public: + static AssemblyCode *FinalizeFromTurboAssembler(AssemblerBase *assembler); +}; diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.cc b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.cc new file mode 100644 index 0000000..eab9622 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.cc @@ -0,0 +1,53 @@ +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +CodeBufferBase *CodeBufferBase::Copy() { + CodeBufferBase *result = new CodeBufferBase(); + result->EmitBuffer(GetBuffer(), GetBufferSize()); + return result; +} + +void CodeBufferBase::Emit8(uint8_t data) { + Emit(data); +} + +void CodeBufferBase::Emit16(uint16_t data) { + Emit(data); +} + +void CodeBufferBase::Emit32(uint32_t data) { + Emit(data); +} + +void CodeBufferBase::Emit64(uint64_t data) { + Emit(data); +} + +void CodeBufferBase::EmitBuffer(uint8_t *buffer, int buffer_size) { + buffer_.insert(buffer_.end(), buffer, buffer + buffer_size); +} + +uint8_t *CodeBufferBase::GetBuffer() { + return buffer_.data(); +} + +size_t CodeBufferBase::GetBufferSize() { + return buffer_.size(); +} + +#if 0 // Template Advanced won't enable even in userspace +template T CodeBufferBase::Load(int offset) { + return *reinterpret_cast(buffer + offset); +} + +template void CodeBufferBase::Store(int offset, T value) { + *reinterpret_cast(buffer + offset) = value; +} + +template void CodeBufferBase::Emit(T value) { + // Ensure the free space enough for the template T value + ensureCapacity(sizeof(T) + GetBufferSize()); + + *reinterpret_cast(buffer_cursor) = value; + buffer_cursor += sizeof(T); +} +#endif diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.h new file mode 100644 index 0000000..61b5889 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.h @@ -0,0 +1,40 @@ +#pragma once + +#include "dobby/common.h" + +class CodeBufferBase { +public: + CodeBufferBase() { + } + +public: + virtual CodeBufferBase *Copy(); + + void Emit8(uint8_t data); + + void Emit16(uint16_t data); + + void Emit32(uint32_t data); + + void Emit64(uint64_t data); + + template T Load(int offset) { + return *(T *)(buffer_.data() + offset); + } + + template void Store(int offset, T value) { + *(T *)(buffer_.data() + offset) = value; + } + + template void Emit(T value) { + EmitBuffer((uint8_t *)&value, sizeof(value)); + } + + void EmitBuffer(uint8_t *buffer, int len); + + uint8_t *GetBuffer(); + size_t GetBufferSize(); + +private: + tinystl::vector buffer_; +}; diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-arm.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-arm.h new file mode 100644 index 0000000..0255947 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-arm.h @@ -0,0 +1,59 @@ +#ifndef CODE_BUFFER_ARM_H +#define CODE_BUFFER_ARM_H + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +typedef int32_t arm_inst_t; +typedef int16_t thumb1_inst_t; +typedef int32_t thumb2_inst_t; + +class CodeBuffer : public CodeBufferBase { + enum ExecuteState { ARMExecuteState, ThumbExecuteState }; + +public: + CodeBuffer() : CodeBufferBase() { + } + +public: + arm_inst_t LoadARMInst(uint32_t offset) { + return *reinterpret_cast(GetBuffer() + offset); + } + + thumb1_inst_t LoadThumb1Inst(uint32_t offset) { + return *reinterpret_cast(GetBuffer() + offset); + } + + thumb2_inst_t LoadThumb2Inst(uint32_t offset) { + return *reinterpret_cast(GetBuffer() + offset); + } + + void RewriteAddr(uint32_t offset, addr32_t addr) { + memcpy(GetBuffer() + offset, &addr, sizeof(addr)); + } + + void RewriteARMInst(uint32_t offset, arm_inst_t instr) { + *reinterpret_cast(GetBuffer() + offset) = instr; + } + + void RewriteThumb1Inst(uint32_t offset, thumb1_inst_t instr) { + *reinterpret_cast(GetBuffer() + offset) = instr; + } + + void RewriteThumb2Inst(uint32_t offset, thumb2_inst_t instr) { + memcpy(GetBuffer() + offset, &instr, sizeof(instr)); + } + + void EmitARMInst(arm_inst_t instr) { + Emit(instr); + } + + void EmitThumb1Inst(thumb1_inst_t instr) { + Emit(instr); + } + + void EmitThumb2Inst(thumb2_inst_t instr) { + Emit(instr); + } +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-arm64.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-arm64.h new file mode 100644 index 0000000..266e10b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-arm64.h @@ -0,0 +1,24 @@ +#ifndef CODE_BUFFER_ARM64_H +#define CODE_BUFFER_ARM64_H + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +typedef int32_t arm64_inst_t; + +class CodeBuffer : public CodeBufferBase { + +public: + CodeBuffer() : CodeBufferBase() { + } + +public: + arm64_inst_t LoadInst(uint32_t offset) { + return *reinterpret_cast(GetBuffer() + offset); + } + + void RewriteInst(uint32_t offset, arm64_inst_t instr) { + *reinterpret_cast(GetBuffer() + offset) = instr; + } +}; + +#endif diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x64.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x64.h new file mode 100644 index 0000000..913ee8a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x64.h @@ -0,0 +1,17 @@ +#ifndef CODE_BUFFER_X64_H +#define CODE_BUFFER_X64_H + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +class CodeBuffer : public CodeBufferBase { +public: + CodeBuffer() : CodeBufferBase() { + } + +public: + void FixBindLabel(int offset, int32_t disp) { + Store(offset, disp); + } +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.cc b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.cc new file mode 100644 index 0000000..e7e0734 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.cc @@ -0,0 +1,18 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "MemoryAllocator/CodeBuffer/code-buffer-x86.h" + +void CodeBuffer::Emit32(int32_t data) { + ensureCapacity(GetBufferSize() + sizeof(int32_t)); + *reinterpret_cast(getCursor()) = data; + buffer_cursor += sizeof(int32_t); + return; +} + +void CodeBuffer::FixBindLabel(int offset, int32_t disp) { + *reinterpret_cast(buffer + offset) = disp; + return; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.h new file mode 100644 index 0000000..43b2f35 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.h @@ -0,0 +1,12 @@ +#pragma once + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +class CodeBuffer : public CodeBufferBase { +public: + CodeBuffer() : CodeBufferBase() { + } + +public: + void FixBindLabel(int offset, int32_t disp); +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_arm.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_arm.h new file mode 100644 index 0000000..0fe8346 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_arm.h @@ -0,0 +1,56 @@ +#pragma once + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +typedef int32_t arm_inst_t; +typedef int16_t thumb1_inst_t; +typedef int32_t thumb2_inst_t; + +class CodeBuffer : public CodeBufferBase { + enum ExecuteState { ARMExecuteState, ThumbExecuteState }; + +public: + CodeBuffer() : CodeBufferBase() { + } + +public: + arm_inst_t LoadARMInst(uint32_t offset) { + return *reinterpret_cast(GetBuffer() + offset); + } + + thumb1_inst_t LoadThumb1Inst(uint32_t offset) { + return *reinterpret_cast(GetBuffer() + offset); + } + + thumb2_inst_t LoadThumb2Inst(uint32_t offset) { + return *reinterpret_cast(GetBuffer() + offset); + } + + void RewriteAddr(uint32_t offset, addr32_t addr) { + memcpy(GetBuffer() + offset, &addr, sizeof(addr)); + } + + void RewriteARMInst(uint32_t offset, arm_inst_t instr) { + *reinterpret_cast(GetBuffer() + offset) = instr; + } + + void RewriteThumb1Inst(uint32_t offset, thumb1_inst_t instr) { + *reinterpret_cast(GetBuffer() + offset) = instr; + } + + void RewriteThumb2Inst(uint32_t offset, thumb2_inst_t instr) { + memcpy(GetBuffer() + offset, &instr, sizeof(instr)); + } + + void EmitARMInst(arm_inst_t instr) { + Emit(instr); + } + + void EmitThumb1Inst(thumb1_inst_t instr) { + Emit(instr); + } + + void EmitThumb2Inst(thumb2_inst_t instr) { + Emit(instr); + } +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_arm64.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_arm64.h new file mode 100644 index 0000000..4c8d803 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_arm64.h @@ -0,0 +1,21 @@ +#pragma once + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +typedef int32_t arm64_inst_t; + +class CodeBuffer : public CodeBufferBase { + +public: + CodeBuffer() : CodeBufferBase() { + } + +public: + arm64_inst_t LoadInst(uint32_t offset) { + return *reinterpret_cast(GetBuffer() + offset); + } + + void RewriteInst(uint32_t offset, arm64_inst_t instr) { + *reinterpret_cast(GetBuffer() + offset) = instr; + } +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x64.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x64.h new file mode 100644 index 0000000..d80dfb8 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x64.h @@ -0,0 +1,14 @@ +#pragma once + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +class CodeBuffer : public CodeBufferBase { +public: + CodeBuffer() : CodeBufferBase() { + } + +public: + void FixBindLabel(int offset, int32_t disp) { + Store(offset, disp); + } +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x86.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x86.h new file mode 100644 index 0000000..d80dfb8 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x86.h @@ -0,0 +1,14 @@ +#pragma once + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +class CodeBuffer : public CodeBufferBase { +public: + CodeBuffer() : CodeBufferBase() { + } + +public: + void FixBindLabel(int offset, int32_t disp) { + Store(offset, disp); + } +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/MemoryAllocator.cc b/app/src/main/cpp/Dobby/source/MemoryAllocator/MemoryAllocator.cc new file mode 100644 index 0000000..13ff04a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/MemoryAllocator.cc @@ -0,0 +1,106 @@ +#include "dobby/dobby_internal.h" + +#include "PlatformUnifiedInterface/MemoryAllocator.h" + +MemBlock *MemoryArena::allocMemBlock(size_t size) { + // insufficient memory + if (this->end - this->cursor_addr < size) { + return nullptr; + } + + auto result = new MemBlock(cursor_addr, size); + cursor_addr += size; + return result; +} + +MemoryAllocator *MemoryAllocator::shared_allocator = nullptr; +MemoryAllocator *MemoryAllocator::SharedAllocator() { + if (MemoryAllocator::shared_allocator == nullptr) { + MemoryAllocator::shared_allocator = new MemoryAllocator(); + } + return MemoryAllocator::shared_allocator; +} + +CodeMemoryArena *MemoryAllocator::allocateCodeMemoryArena(uint32_t size) { + CHECK_EQ(size % OSMemory::PageSize(), 0); + uint32_t arena_size = size; + auto arena_addr = OSMemory::Allocate(arena_size, kNoAccess); + OSMemory::SetPermission(arena_addr, arena_size, kReadExecute); + + auto result = new CodeMemoryArena((addr_t)arena_addr, (size_t)arena_size); + code_arenas.push_back(result); + return result; +} + +CodeMemBlock *MemoryAllocator::allocateExecBlock(uint32_t size) { + CodeMemBlock *block = nullptr; + for (auto iter = code_arenas.begin(); iter != code_arenas.end(); iter++) { + auto arena = static_cast(*iter); + block = arena->allocMemBlock(size); + if (block) + break; + } + if (!block) { + // allocate new arena + auto arena_size = ALIGN_CEIL(size, OSMemory::PageSize()); + auto arena = allocateCodeMemoryArena(arena_size); + block = arena->allocMemBlock(size); + CHECK_NOT_NULL(block); + } + + DEBUG_LOG("[memory allocator] allocate exec memory at: %p, size: %p", block->addr, block->size); + return block; +} + +uint8_t *MemoryAllocator::allocateExecMemory(uint32_t size) { + auto block = allocateExecBlock(size); + return (uint8_t *)block->addr; +} +uint8_t *MemoryAllocator::allocateExecMemory(uint8_t *buffer, uint32_t buffer_size) { + auto mem = allocateExecMemory(buffer_size); + auto ret = DobbyCodePatch(mem, buffer, buffer_size); + CHECK_EQ(ret, 0); + return mem; +} + +DataMemoryArena *MemoryAllocator::allocateDataMemoryArena(uint32_t size) { + DataMemoryArena *result = nullptr; + + uint32_t buffer_size = ALIGN_CEIL(size, OSMemory::PageSize()); + void *buffer = OSMemory::Allocate(buffer_size, kNoAccess); + OSMemory::SetPermission(buffer, buffer_size, kReadWrite); + + result = new DataMemoryArena((addr_t)buffer, (size_t)buffer_size); + data_arenas.push_back(result); + return result; +} + +DataMemBlock *MemoryAllocator::allocateDataBlock(uint32_t size) { + CodeMemBlock *block = nullptr; + for (auto iter = data_arenas.begin(); iter != data_arenas.end(); iter++) { + auto arena = static_cast(*iter); + block = arena->allocMemBlock(size); + if (block) + break; + } + if (!block) { + // allocate new arena + auto arena = allocateCodeMemoryArena(size); + block = arena->allocMemBlock(size); + CHECK_NOT_NULL(block); + } + + DEBUG_LOG("[memory allocator] allocate data memory at: %p, size: %p", block->addr, block->size); + return block; +} + +uint8_t *MemoryAllocator::allocateDataMemory(uint32_t size) { + auto block = allocateDataBlock(size); + return (uint8_t *)block->addr; +} + +uint8_t *MemoryAllocator::allocateDataMemory(uint8_t *buffer, uint32_t buffer_size) { + auto mem = allocateDataMemory(buffer_size); + memcpy(mem, buffer, buffer_size); + return mem; +} diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.cc b/app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.cc new file mode 100644 index 0000000..27b5cf7 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.cc @@ -0,0 +1,235 @@ +#include "NearMemoryAllocator.h" + +#include "dobby/dobby_internal.h" + +#include "PlatformUtil/ProcessRuntimeUtility.h" + +using namespace zz; + +#define KB (1024uLL) +#define MB (1024uLL * KB) +#define GB (1024uLL * MB) + +#if defined(WIN32) +static const void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { + if (!haystack || !needle) { + return haystack; + } else { + const char *h = (const char *)haystack; + const char *n = (const char *)needle; + size_t l = needlelen; + const char *r = h; + while (l && (l <= haystacklen)) { + if (*n++ != *h++) { + r = h; + n = (const char *)needle; + l = needlelen; + } else { + --l; + } + --haystacklen; + } + return l ? nullptr : r; + } +} +#endif + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +NearMemoryAllocator *NearMemoryAllocator::shared_allocator = nullptr; +NearMemoryAllocator *NearMemoryAllocator::SharedAllocator() { + if (NearMemoryAllocator::shared_allocator == nullptr) { + NearMemoryAllocator::shared_allocator = new NearMemoryAllocator(); + } + return NearMemoryAllocator::shared_allocator; +} + +MemBlock *NearMemoryAllocator::allocateNearBlockFromDefaultAllocator(uint32_t size, addr_t pos, size_t search_range, + bool executable) { + addr_t min_valid_addr, max_valid_addr; + min_valid_addr = pos - search_range; + max_valid_addr = pos + search_range; + + auto allocateFromDefaultArena = [&](MemoryArena *arena, uint32_t size) -> addr_t { + addr_t unused_mem_start = arena->cursor_addr; + addr_t unused_mem_end = arena->addr + arena->size; + + // check if unused region total out of search range + if (unused_mem_end < min_valid_addr || unused_mem_start > max_valid_addr) + return 0; + + unused_mem_start = max(unused_mem_start, min_valid_addr); + unused_mem_end = min(unused_mem_end, max_valid_addr); + + // check if invalid + if (unused_mem_start >= unused_mem_end) + return 0; + + // check if has sufficient memory + if (unused_mem_end - unused_mem_start < size) + return 0; + + DEBUG_LOG("[near memory allocator] unused memory from default allocator %p(%p), within pos: %p, serach_range: %p", + unused_mem_start, size, pos, search_range); + return unused_mem_start; + }; + + MemoryArena *arena = nullptr; + addr_t unused_mem = 0; + if (executable) { + for (auto iter = default_allocator->code_arenas.begin(); iter != default_allocator->code_arenas.end(); iter++) { + arena = *iter; + unused_mem = allocateFromDefaultArena(arena, size); + if (!unused_mem) + continue; + + break; + } + } else { + for (auto iter = default_allocator->data_arenas.begin(); iter != default_allocator->data_arenas.end(); iter++) { + arena = *iter; + unused_mem = allocateFromDefaultArena(arena, size); + if (unused_mem) + continue; + } + } + + if (!unused_mem) + return nullptr; + + // skip placeholder block + // FIXME: allocate the placeholder but mark it as freed + auto placeholder_block_size = unused_mem - arena->cursor_addr; + arena->allocMemBlock(placeholder_block_size); + + auto block = arena->allocMemBlock(size); + return block; +} + +MemBlock *NearMemoryAllocator::allocateNearBlockFromUnusedRegion(uint32_t size, addr_t pos, size_t search_range, + bool executable) { + + addr_t min_valid_addr, max_valid_addr; + min_valid_addr = pos - search_range; + max_valid_addr = pos + search_range; + + auto check_has_sufficient_memory_between_region = [&](MemRegion region, MemRegion next_region, + uint32_t size) -> addr_t { + addr_t unused_mem_start = region.start + region.size; + addr_t unused_mem_end = next_region.start; + + // check if unused region total out of search range + if (unused_mem_end < min_valid_addr || unused_mem_start > max_valid_addr) + return 0; + + // align + unused_mem_start = ALIGN_FLOOR(unused_mem_start, 4); + + unused_mem_start = max(unused_mem_start, min_valid_addr); + unused_mem_end = min(unused_mem_end, max_valid_addr); + + // check if invalid + if (unused_mem_start >= unused_mem_end) + return 0; + + // check if has sufficient memory + if (unused_mem_end - unused_mem_start < size) + return 0; + + DEBUG_LOG("[near memory allocator] unused memory from unused region %p(%p), within pos: %p, serach_range: %p", + unused_mem_start, size, pos, search_range); + return unused_mem_start; + }; + + addr_t unused_mem = 0; + auto regions = ProcessRuntimeUtility::GetProcessMemoryLayout(); + for (size_t i = 0; i + 1 < regions.size(); i++) { + unused_mem = check_has_sufficient_memory_between_region(regions[i], regions[i + 1], size); + if (unused_mem == 0) + continue; + break; + } + + if (!unused_mem) + return nullptr; + + auto unused_arena_first_page_addr = (addr_t)ALIGN_FLOOR(unused_mem, OSMemory::PageSize()); + auto unused_arena_end_page_addr = ALIGN_FLOOR(unused_mem + size, OSMemory::PageSize()); + auto unused_arena_size = unused_arena_end_page_addr - unused_arena_first_page_addr + OSMemory::PageSize(); + auto unused_arena_addr = unused_arena_first_page_addr; + + if (OSMemory::Allocate(unused_arena_size, kNoAccess, (void *)unused_arena_addr) == nullptr) { + ERROR_LOG("[near memory allocator] allocate fixed page failed %p", unused_arena_addr); + return nullptr; + } + + auto register_near_arena = [&](addr_t arena_addr, size_t arena_size) -> MemoryArena * { + MemoryArena *arena = nullptr; + if (executable) { + arena = new CodeMemoryArena(arena_addr, arena_size); + default_allocator->code_arenas.push_back(arena); + } else { + arena = new DataMemoryArena(arena_addr, arena_size); + default_allocator->data_arenas.push_back(arena); + } + OSMemory::SetPermission((void *)arena->addr, arena->size, executable ? kReadExecute : kReadWrite); + return arena; + }; + + auto unused_arena = register_near_arena(unused_arena_addr, unused_arena_size); + + // skip placeholder block + // FIXME: allocate the placeholder but mark it as freed + auto placeholder_block_size = unused_mem - unused_arena->cursor_addr; + unused_arena->allocMemBlock(placeholder_block_size); + + auto block = unused_arena->allocMemBlock(size); + return block; +} + +MemBlock *NearMemoryAllocator::allocateNearBlock(uint32_t size, addr_t pos, size_t search_range, bool executable) { + MemBlock *result = nullptr; + result = allocateNearBlockFromDefaultAllocator(size, pos, search_range, executable); + if (!result) { + result = allocateNearBlockFromUnusedRegion(size, pos, search_range, executable); + } + + if (!result) { + ERROR_LOG("[near memory allocator] allocate near block failed (%p, %p, %p)", size, pos, search_range); + } + return result; +} + +uint8_t *NearMemoryAllocator::allocateNearExecMemory(uint32_t size, addr_t pos, size_t search_range) { + auto block = allocateNearBlock(size, pos, search_range, true); + if (!block) + return nullptr; + + DEBUG_LOG("[near memory allocator] allocate exec memory at: %p, size: %p", block->addr, block->size); + return (uint8_t *)block->addr; +} + +uint8_t *NearMemoryAllocator::allocateNearExecMemory(uint8_t *buffer, uint32_t buffer_size, addr_t pos, + size_t search_range) { + auto mem = allocateNearExecMemory(buffer_size, pos, search_range); + auto ret = DobbyCodePatch(mem, buffer, buffer_size); + CHECK_EQ(ret, 0); + return mem; +} + +uint8_t *NearMemoryAllocator::allocateNearDataMemory(uint32_t size, addr_t pos, size_t search_range) { + auto block = allocateNearBlock(size, pos, search_range, false); + if (!block) + return nullptr; + + DEBUG_LOG("[near memory allocator] allocate data memory at: %p, size: %p", block->addr, block->size); + return (uint8_t *)block->addr; +} + +uint8_t *NearMemoryAllocator::allocateNearDataMemory(uint8_t *buffer, uint32_t buffer_size, addr_t pos, + size_t search_range) { + auto mem = allocateNearExecMemory(buffer_size, pos, search_range); + memcpy(mem, buffer, buffer_size); + return mem; +} diff --git a/app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.h b/app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.h new file mode 100644 index 0000000..701cba8 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.h @@ -0,0 +1,30 @@ +#pragma once + +#include "PlatformUnifiedInterface/MemoryAllocator.h" + +#include "dobby/common.h" + +class NearMemoryAllocator { +public: + MemoryAllocator *default_allocator; + NearMemoryAllocator() { + default_allocator = MemoryAllocator::SharedAllocator(); + } + +private: + static NearMemoryAllocator *shared_allocator; + +public: + static NearMemoryAllocator *SharedAllocator(); + +public: + MemBlock *allocateNearBlock(uint32_t size, addr_t pos, size_t search_range, bool executable); + MemBlock *allocateNearBlockFromDefaultAllocator(uint32_t size, addr_t pos, size_t search_range, bool executable); + MemBlock *allocateNearBlockFromUnusedRegion(uint32_t size, addr_t pos, size_t search_range, bool executable); + + uint8_t *allocateNearExecMemory(uint32_t size, addr_t pos, size_t search_range); + uint8_t *allocateNearExecMemory(uint8_t *buffer, uint32_t buffer_size, addr_t pos, size_t search_range); + + uint8_t *allocateNearDataMemory(uint32_t size, addr_t pos, size_t search_range); + uint8_t *allocateNearDataMemory(uint8_t *buffer, uint32_t buffer_size, addr_t pos, size_t search_range); +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/ClearCacheTool.h b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/ClearCacheTool.h new file mode 100644 index 0000000..ee407cb --- /dev/null +++ b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/ClearCacheTool.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void ClearCache(void *start, void *end); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/CodePatchTool.h b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/CodePatchTool.h new file mode 100644 index 0000000..410f0d8 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/CodePatchTool.h @@ -0,0 +1,3 @@ +#pragma once + +int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size); \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/MemoryAllocator.h b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/MemoryAllocator.h new file mode 100644 index 0000000..56da2e5 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/MemoryAllocator.h @@ -0,0 +1,101 @@ +#pragma once + +#include "dobby/common.h" + +struct MemRange { + addr_t start; + addr_t end; + size_t size; + + MemRange(addr_t start, size_t size) : start(start), end(0), size(size) { + end = start + size; + } + + void reset(addr_t start, size_t size) { + this->start = start; + this->size = size; + end = start + size; + } +}; + +struct MemBlock : MemRange { + addr_t addr; + + MemBlock() : MemRange(0, 0), addr(0) { + } + + MemBlock(addr_t addr, size_t size) : MemRange(addr, size), addr(addr) { + } + + void reset(addr_t addr, size_t size) { + MemRange::reset(addr, size); + this->addr = addr; + } +}; + +struct MemoryArena : MemRange { + addr_t addr; + addr_t cursor_addr; + + tinystl::vector memory_blocks; + + MemoryArena(addr_t addr, size_t size) : MemRange(addr, size), addr(addr), cursor_addr(addr) { + } + + virtual MemBlock *allocMemBlock(size_t size); +}; + +using CodeMemBlock = MemBlock; +using CodeMemoryArena = MemoryArena; + +#if 0 +struct CodeMemoryArena : MemoryArena { + CodeMemoryArena(addr_t addr, size_t size) : MemoryArena(addr, size) { + } + + CodeMemBlock *allocateCodeMemBlock(size_t size) { + return allocMemBlock(size); + } +}; +#endif + +using DataMemBlock = MemBlock; +using DataMemoryArena = MemoryArena; + +#if 0 +struct DataMemoryArena : MemoryArena { +public: + DataMemoryArena(addr_t addr, size_t size) : MemoryArena(addr, size) { + } + + DataMemBlock *allocateDataMemBlock(size_t size) { + return allocMemBlock(size); + } +}; +#endif + +class NearMemoryAllocator; +class MemoryAllocator { + friend class NearMemoryAllocator; + +private: + tinystl::vector code_arenas; + tinystl::vector data_arenas; + +private: + static MemoryAllocator *shared_allocator; + +public: + static MemoryAllocator *SharedAllocator(); + +public: + CodeMemoryArena *allocateCodeMemoryArena(uint32_t size); + CodeMemBlock *allocateExecBlock(uint32_t size); + uint8_t *allocateExecMemory(uint32_t size); + uint8_t *allocateExecMemory(uint8_t *buffer, uint32_t buffer_size); + + DataMemoryArena *allocateDataMemoryArena(uint32_t size); + DataMemBlock *allocateDataBlock(uint32_t size); + uint8_t *allocateDataMemory(uint32_t size); + uint8_t *allocateDataMemory(uint8_t *buffer, uint32_t buffer_size); +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/platform.h b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/platform.h new file mode 100644 index 0000000..77a05ff --- /dev/null +++ b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/platform.h @@ -0,0 +1,82 @@ +#pragma once + +#include "dobby/common.h" + +namespace base { + +class ThreadLocalStorageInterface { + using LocalStorageKey = int32_t; + + static LocalStorageKey CreateThreadLocalKey(); + + static void DeleteThreadLocalKey(LocalStorageKey key); + + static void *GetThreadLocal(LocalStorageKey key); + + static int GetThreadLocalInt(LocalStorageKey key) { + return static_cast(reinterpret_cast(GetThreadLocal(key))); + } + + static void SetThreadLocal(LocalStorageKey key, void *value); + + static void SetThreadLocalInt(LocalStorageKey key, int value) { + SetThreadLocal(key, reinterpret_cast(static_cast(value))); + } + + static bool HasThreadLocal(LocalStorageKey key) { + return GetThreadLocal(key) != nullptr; + } +}; + +typedef void *ThreadHandle; + +class ThreadInterface { +public: + class Delegate { + public: + virtual void ThreadMain() = 0; + }; + +public: + static bool Create(Delegate *delegate, ThreadHandle *handle); + + static int CurrentId(); + + static void SetName(const char *); +}; +} // namespace base + +class OSThread : public base::ThreadInterface, public base::ThreadInterface::Delegate { + base::ThreadHandle handle_; + + char name_[256]; + +public: + OSThread(const char *name); + + bool Start(); +}; + +enum MemoryPermission { kNoAccess, kRead, kReadWrite, kReadWriteExecute, kReadExecute }; + +class OSMemory { +public: + static int PageSize(); + + static void *Allocate(size_t size, MemoryPermission access); + + static void *Allocate(size_t size, MemoryPermission access, void *fixed_address); + + static bool Free(void *address, size_t size); + + static bool Release(void *address, size_t size); + + static bool SetPermission(void *address, size_t size, MemoryPermission access); +}; + +class OSPrint { +public: + static void Print(const char *format, ...); + + static void VPrint(const char *format, va_list args); +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/PlatformUtil/ProcessRuntimeUtility.h b/app/src/main/cpp/Dobby/source/PlatformUtil/ProcessRuntimeUtility.h new file mode 100644 index 0000000..7f85a46 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/PlatformUtil/ProcessRuntimeUtility.h @@ -0,0 +1,26 @@ +#pragma once + +#include "PlatformUnifiedInterface/MemoryAllocator.h" + +#include "PlatformUnifiedInterface/platform.h" + +typedef struct _RuntimeModule { + char path[1024]; + void *load_address; +} RuntimeModule; + +struct MemRegion : MemRange { + MemoryPermission permission; + + MemRegion(addr_t addr, size_t size, MemoryPermission perm) : MemRange(addr, size), permission(perm) { + } +}; + +class ProcessRuntimeUtility { +public: + static const tinystl::vector &GetProcessMemoryLayout(); + + static const tinystl::vector &GetProcessModuleMap(); + + static RuntimeModule GetProcessModule(const char *name); +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h new file mode 100644 index 0000000..160ba0a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h @@ -0,0 +1,39 @@ +#pragma once + +#include "dobby/dobby_internal.h" + +#ifdef ENABLE_CLOSURE_TRAMPOLINE_TEMPLATE +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus +void closure_trampoline_template(); +void closure_bridge_template(); +#ifdef __cplusplus +} +#endif //__cplusplus +#endif + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +typedef struct { + void *address; + int size; + void *carry_handler; + void *carry_data; +} ClosureTrampolineEntry; + +asm_func_t get_closure_bridge(); + +#ifdef __cplusplus +} +#endif //__cplusplus + +class ClosureTrampoline { +private: + static tinystl::vector *trampolines_; + +public: + static ClosureTrampolineEntry *CreateClosureTrampoline(void *carry_data, void *carry_handler); +}; diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/ClosureTrampolineARM.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/ClosureTrampolineARM.cc new file mode 100644 index 0000000..76e2ea2 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/ClosureTrampolineARM.cc @@ -0,0 +1,49 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-arm.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +using namespace zz; +using namespace zz::arm; + +ClosureTrampolineEntry *ClosureTrampoline::CreateClosureTrampoline(void *carry_data, void *carry_handler) { + ClosureTrampolineEntry *tramp_entry = nullptr; + tramp_entry = new ClosureTrampolineEntry; + +#ifdef ENABLE_CLOSURE_TRAMPOLINE_TEMPLATE +#define CLOSURE_TRAMPOLINE_SIZE (7 * 4) + // use closure trampoline template code, find the executable memory and patch it. + auto code = AssemblyCodeBuilder::FinalizeCodeFromAddress(closure_trampoline_template, CLOSURE_TRAMPOLINE_SIZE); +#else +// use assembler and codegen modules instead of template_code +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" +#define _ turbo_assembler_. + TurboAssembler turbo_assembler_(0); + + AssemblerPseudoLabel entry_label(0); + AssemblerPseudoLabel forward_bridge_label(0); + + _ Ldr(r12, &entry_label); + _ Ldr(pc, &forward_bridge_label); + _ PseudoBind(&entry_label); + _ EmitAddress((uint32_t)(uintptr_t)tramp_entry); + _ PseudoBind(&forward_bridge_label); + _ EmitAddress((uint32_t)(uintptr_t)get_closure_bridge()); + + auto closure_tramp = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + tramp_entry->address = (void *)closure_tramp->addr; + tramp_entry->size = closure_tramp->size; + tramp_entry->carry_data = carry_data; + tramp_entry->carry_handler = carry_handler; + + delete closure_tramp; + + return tramp_entry; +#endif +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/closure_bridge_arm.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/closure_bridge_arm.cc new file mode 100644 index 0000000..e41500d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/closure_bridge_arm.cc @@ -0,0 +1,90 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-arm.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h" + +using namespace zz; +using namespace zz::arm; + +static asm_func_t closure_bridge = nullptr; + +asm_func_t get_closure_bridge() { + + // if already initialized, just return. + if (closure_bridge) + return closure_bridge; + +// check if enable the inline-assembly closure_bridge_template +#if ENABLE_CLOSURE_BRIDGE_TEMPLATE + extern void closure_bridge_tempate(); + closure_bridge = closure_bridge_template; +// otherwise, use the Assembler build the closure_bridge +#else +#define _ turbo_assembler_. + TurboAssembler turbo_assembler_(0); + + _ sub(sp, sp, Operand(14 * 4)); + _ str(lr, MemOperand(sp, 13 * 4)); + _ str(r12, MemOperand(sp, 12 * 4)); + _ str(r11, MemOperand(sp, 11 * 4)); + _ str(r10, MemOperand(sp, 10 * 4)); + _ str(r9, MemOperand(sp, 9 * 4)); + _ str(r8, MemOperand(sp, 8 * 4)); + _ str(r7, MemOperand(sp, 7 * 4)); + _ str(r6, MemOperand(sp, 6 * 4)); + _ str(r5, MemOperand(sp, 5 * 4)); + _ str(r4, MemOperand(sp, 4 * 4)); + _ str(r3, MemOperand(sp, 3 * 4)); + _ str(r2, MemOperand(sp, 2 * 4)); + _ str(r1, MemOperand(sp, 1 * 4)); + _ str(r0, MemOperand(sp, 0 * 4)); + + // store sp + _ add(r0, sp, Operand(14 * 4)); + _ sub(sp, sp, Operand(8)); + _ str(r0, MemOperand(sp, 4)); + + // stack align + _ sub(sp, sp, Operand(8)); + + _ mov(r0, Operand(sp)); + _ mov(r1, Operand(r12)); + _ CallFunction(ExternalReference((void *)common_closure_bridge_handler)); + + // stack align + _ add(sp, sp, Operand(8)); + + // restore sp placeholder stack + _ add(sp, sp, Operand(8)); + + _ ldr(r0, MemOperand(sp, 4, PostIndex)); + _ ldr(r1, MemOperand(sp, 4, PostIndex)); + _ ldr(r2, MemOperand(sp, 4, PostIndex)); + _ ldr(r3, MemOperand(sp, 4, PostIndex)); + _ ldr(r4, MemOperand(sp, 4, PostIndex)); + _ ldr(r5, MemOperand(sp, 4, PostIndex)); + _ ldr(r6, MemOperand(sp, 4, PostIndex)); + _ ldr(r7, MemOperand(sp, 4, PostIndex)); + _ ldr(r8, MemOperand(sp, 4, PostIndex)); + _ ldr(r9, MemOperand(sp, 4, PostIndex)); + _ ldr(r10, MemOperand(sp, 4, PostIndex)); + _ ldr(r11, MemOperand(sp, 4, PostIndex)); + _ ldr(r12, MemOperand(sp, 4, PostIndex)); + _ ldr(lr, MemOperand(sp, 4, PostIndex)); + + // auto switch A32 & T32 with `least significant bit`, refer `docs/A32_T32_states_switch.md` + _ mov(pc, Operand(r12)); + + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + closure_bridge = (asm_func_t)code->addr; + + DEBUG_LOG("[closure bridge] closure bridge at %p", closure_bridge); +#endif + return closure_bridge; +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-bridge-template-arm.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-bridge-template-arm.cc new file mode 100644 index 0000000..8e41525 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-bridge-template-arm.cc @@ -0,0 +1,65 @@ +// .section __TEXT,__text,regular,pure_instructions +// .ios_version_min 11, 0 + +#if defined(ENABLE_CLOSURE_BRIDGE_TEMPLATE) + +#if defined(__WIN32__) || defined(__APPLE__) +#define cdecl(s) "_" s +#else +#define cdecl(s) s +#endif + +#define xASM(x) __asm(x) + +__attribute__((naked)) void closure_bridge_template() { + xASM(".arm"); + xASM("sub sp, sp, #(14*4)"); + xASM("str lr, [sp, #(13*4)]"); + xASM("str r12, [sp, #(12*4)]"); + xASM("str r11, [sp, #(11*4)]"); + xASM("str r10, [sp, #(10*4)]"); + xASM("str r9, [sp, #(9*4)]"); + xASM("str r8, [sp, #(8*4)]"); + xASM("str r7, [sp, #(7*4)]"); + xASM("str r6, [sp, #(6*4)]"); + xASM("str r5, [sp, #(5*4)]"); + xASM("str r4, [sp, #(4*4)]"); + xASM("str r3, [sp, #(3*4)]"); + xASM("str r2, [sp, #(2*4)]"); + xASM("str r1, [sp, #(1*4)]"); + xASM("str r0, [sp, #(0*4)]"); + + // dummy align + xASM("sub sp, sp, #8"); + + xASM("mov r0, sp"); + xASM("mov r1, r12"); + xASM("bl " cdecl("common_closure_bridge_handler")); + + // dummy align + xASM("add sp, sp, #8"); + + xASM("ldr r0, [sp], #4"); + xASM("ldr r1, [sp], #4"); + xASM("ldr r2, [sp], #4"); + xASM("ldr r3, [sp], #4"); + xASM("ldr r4, [sp], #4"); + xASM("ldr r5, [sp], #4"); + xASM("ldr r6, [sp], #4"); + xASM("ldr r7, [sp], #4"); + xASM("ldr r8, [sp], #4"); + xASM("ldr r9, [sp], #4"); + xASM("ldr r10, [sp], #4"); + xASM("ldr r11, [sp], #4"); + xASM("ldr r12, [sp], #4"); + xASM("ldr lr, [sp], #4"); + +#if 1 + xASM("str r12, [sp, #-4]"); + xASM("ldr pc, [sp, #-4]"); +#else + xASM("mov pc, r12"); +#endif +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-trampoline-template-arm.S b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-trampoline-template-arm.S new file mode 100644 index 0000000..5be6ac8 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-trampoline-template-arm.S @@ -0,0 +1,34 @@ +// .section __TEXT,__text,regular,pure_instructions + +#if defined(ENABLE_CLOSURE_BRIDGE_TEMPLATE) + +#if defined(__WIN32__) || defined(__APPLE__) +#define cdecl(s) _##s +#else +#define cdecl(s) s +#endif + +.align 4 + +#if !defined(ENABLE_CLOSURE_TRAMPOLINE_CARRY_OBJECT_PTR) + +// closure trampoline carray the object pointer, and fetch required members at the runtime assembly code. +// #include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" +// #define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT)) +#define OFFSETOF_ClourseTrampolineEntry_carry_data 4 +#define OFFSETOF_ClourseTrampolineEntry_carry_handler 0 + .globl + cdecl(closure_trampoline_template) cdecl(closure_trampoline_template) + : ldr r12, +ClourseTrampolineEntryPtr ldr pc, [ r12, #0 ] ClourseTrampolineEntryPtr :.long 0 + +#else + + ; +closure trampoline just carray the required members from the object..globl cdecl(closure_trampoline_template) + cdecl(closure_trampoline_template) + : ldr r12, += carry_data ldr pc, = carry_handler carry_data :.long 0 carry_handler :.long 0 +#endif + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/helper_arm.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/helper_arm.cc new file mode 100644 index 0000000..bf2123d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/helper_arm.cc @@ -0,0 +1,13 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM) + +#include "dobby/dobby_internal.h" + +void set_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address) { + *reinterpret_cast(&ctx->general.regs.r12) = address; +} + +void get_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address) { +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/ClosureTrampolineARM64.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/ClosureTrampolineARM64.cc new file mode 100644 index 0000000..9dfc44c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/ClosureTrampolineARM64.cc @@ -0,0 +1,63 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-arm64.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +using namespace zz; +using namespace zz::arm64; + +// // tips +// _ ldr(TMP_REG_1, OFFSETOF(ClosureTrampolineEntry, carry_data)); +// _ ldr(TMP_REG_0, OFFSETOF(ClosureTrampolineEntry, carry_handler)); + +// use assembler and codegen modules instead of template_code +ClosureTrampolineEntry *ClosureTrampoline::CreateClosureTrampoline(void *carry_data, void *carry_handler) { + ClosureTrampolineEntry *tramp_entry = nullptr; + tramp_entry = new ClosureTrampolineEntry; + +#define _ turbo_assembler_. + TurboAssembler turbo_assembler_(0); + + AssemblerPseudoLabel entry_label(0); + AssemblerPseudoLabel forward_bridge_label(0); + + // prologue: alloc stack, store lr + _ sub(SP, SP, 2 * 8); + _ str(x30, MemOperand(SP, 8)); + + // store data at stack + _ Ldr(TMP_REG_0, &entry_label); + _ str(TMP_REG_0, MemOperand(SP, 0)); + + _ Ldr(TMP_REG_0, &forward_bridge_label); + _ blr(TMP_REG_0); + + // epilogue: release stack(won't restore lr) + _ ldr(x30, MemOperand(SP, 8)); + _ add(SP, SP, 2 * 8); + + // branch to next hop + _ br(TMP_REG_0); + + _ PseudoBind(&entry_label); + _ EmitInt64((uint64_t)tramp_entry); + _ PseudoBind(&forward_bridge_label); + _ EmitInt64((uint64_t)get_closure_bridge()); + + auto closure_tramp = AssemblyCodeBuilder::FinalizeFromTurboAssembler(static_cast(&turbo_assembler_)); + + tramp_entry->address = (void *)closure_tramp->addr; + tramp_entry->size = closure_tramp->size; + tramp_entry->carry_data = carry_data; + tramp_entry->carry_handler = carry_handler; + + delete closure_tramp; + + return tramp_entry; +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/closure_bridge_arm64.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/closure_bridge_arm64.cc new file mode 100644 index 0000000..5e1bc0e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/closure_bridge_arm64.cc @@ -0,0 +1,159 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler.h" +#include "core/assembler/assembler-arm64.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h" + +using namespace zz; +using namespace zz::arm64; + +static asm_func_t closure_bridge = nullptr; + +asm_func_t get_closure_bridge() { + // if already initialized, just return. + if (closure_bridge) + return closure_bridge; + +// check if enable the inline-assembly closure_bridge_template +#if ENABLE_CLOSURE_BRIDGE_TEMPLATE + extern void closure_bridge_tempate(); + closure_bridge = closure_bridge_template; +// otherwise, use the Assembler build the closure_bridge +#else +#define _ turbo_assembler_. +#define MEM(reg, offset) MemOperand(reg, offset) + TurboAssembler turbo_assembler_(0); + +#if defined(FULL_FLOATING_POINT_REGISTER_PACK) + + _ sub(SP, SP, 24 * 16); + _ stp(Q(30), Q(31), MEM(SP, 22 * 16)); + _ stp(Q(28), Q(29), MEM(SP, 20 * 16)); + _ stp(Q(26), Q(27), MEM(SP, 18 * 16)); + _ stp(Q(24), Q(25), MEM(SP, 16 * 16)); + _ stp(Q(22), Q(23), MEM(SP, 14 * 16)); + _ stp(Q(20), Q(21), MEM(SP, 12 * 16)); + _ stp(Q(18), Q(19), MEM(SP, 10 * 16)); + _ stp(Q(16), Q(17), MEM(SP, 8 * 16)); + _ stp(Q(14), Q(15), MEM(SP, 6 * 16)); + _ stp(Q(12), Q(13), MEM(SP, 4 * 16)); + _ stp(Q(10), Q(11), MEM(SP, 2 * 16)); + _ stp(Q(8), Q(9), MEM(SP, 0 * 16)); + +#endif + + // save {q0-q7} + _ sub(SP, SP, 8 * 16); + _ stp(Q(6), Q(7), MEM(SP, 6 * 16)); + _ stp(Q(4), Q(5), MEM(SP, 4 * 16)); + _ stp(Q(2), Q(3), MEM(SP, 2 * 16)); + _ stp(Q(0), Q(1), MEM(SP, 0 * 16)); + + // save {x1-x30} + _ sub(SP, SP, 30 * 8); + _ stp(X(29), X(30), MEM(SP, 28 * 8)); + _ stp(X(27), X(28), MEM(SP, 26 * 8)); + _ stp(X(25), X(26), MEM(SP, 24 * 8)); + _ stp(X(23), X(24), MEM(SP, 22 * 8)); + _ stp(X(21), X(22), MEM(SP, 20 * 8)); + _ stp(X(19), X(20), MEM(SP, 18 * 8)); + _ stp(X(17), X(18), MEM(SP, 16 * 8)); + _ stp(X(15), X(16), MEM(SP, 14 * 8)); + _ stp(X(13), X(14), MEM(SP, 12 * 8)); + _ stp(X(11), X(12), MEM(SP, 10 * 8)); + _ stp(X(9), X(10), MEM(SP, 8 * 8)); + _ stp(X(7), X(8), MEM(SP, 6 * 8)); + _ stp(X(5), X(6), MEM(SP, 4 * 8)); + _ stp(X(3), X(4), MEM(SP, 2 * 8)); + _ stp(X(1), X(2), MEM(SP, 0 * 8)); + + // save {x0} + _ sub(SP, SP, 2 * 8); + _ str(x0, MEM(SP, 8)); + + // calculate original sp + _ add(TMP_REG_0, SP, 2 * 8); // closure trampoline reserved + _ add(TMP_REG_0, TMP_REG_0, 2 * 8 + 30 * 8 + 8 * 16); // x0, x1-x30, q0-q7 reserved +#if defined(FULL_FLOATING_POINT_REGISTER_PACK) + _ add(TMP_REG_0, TMP_REG_0, 24 * 16); // q8-q31 reserved +#endif + + // alloc stack, store original sp + _ sub(SP, SP, 2 * 8); + _ str(TMP_REG_0, MEM(SP, 8)); + +#if defined(FULL_FLOATING_POINT_REGISTER_PACK) +#define REGISTER_CONTEXT_SIZE (sizeof(DobbyRegisterContext)) +#else +#define REGISTER_CONTEXT_SIZE (sizeof(DobbyRegisterContext) - 24 * 16) +#endif + // create function arm64 call convention + _ mov(x0, SP); // arg1: register context + // load package(closure trampoline entry reserved) + _ ldr(x1, MEM(SP, REGISTER_CONTEXT_SIZE + 0)); // arg2: closure trampoline entry + _ CallFunction(ExternalReference((void *)common_closure_bridge_handler)); + + // restore sp placeholder stack + _ add(SP, SP, 2 * 8); + + // restore {x0} + _ ldr(X(0), MEM(SP, 8)); + _ add(SP, SP, 2 * 8); + +#define MEM_EXT(reg, offset, addrmode) MemOperand(reg, offset, addrmode) + // restore {x1-x30} + _ ldp(X(1), X(2), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(3), X(4), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(5), X(6), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(7), X(8), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(9), X(10), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(11), X(12), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(13), X(14), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(15), X(16), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(17), X(18), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(19), X(20), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(21), X(22), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(23), X(24), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(25), X(26), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(27), X(28), MEM_EXT(SP, 16, PostIndex)); + _ ldp(X(29), X(30), MEM_EXT(SP, 16, PostIndex)); + + // restore {q0-q7} + _ ldp(Q(0), Q(1), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(2), Q(3), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(4), Q(5), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(6), Q(7), MEM_EXT(SP, 32, PostIndex)); + +#if defined(FULL_FLOATING_POINT_REGISTER_PACK) + _ ldp(Q(8), Q(9), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(10), Q(11), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(12), Q(13), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(14), Q(15), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(16), Q(17), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(18), Q(19), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(20), Q(21), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(22), Q(23), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(24), Q(25), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(26), Q(27), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(28), Q(29), MEM_EXT(SP, 32, PostIndex)); + _ ldp(Q(30), Q(31), MEM_EXT(SP, 32, PostIndex)); +#endif + + // _ brk(0); // for debug + + // return to closure trampoline, but TMP_REG_0, had been modified with next hop address + _ ret(); // AKA br x30 + + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + closure_bridge = (asm_func_t)code->addr; + + DEBUG_LOG("[closure bridge] closure bridge at %p", closure_bridge); +#endif + return closure_bridge; +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-bridge-template-arm64.c b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-bridge-template-arm64.c new file mode 100644 index 0000000..ee5323e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-bridge-template-arm64.c @@ -0,0 +1,103 @@ +#if defined(ENABLE_CLOSURE_BRIDGE_TEMPLATE) + +#if defined(__WIN32__) || defined(__APPLE__) +#define xcdecl(s) "_" s +#else +#define xcdecl(s) s +#endif + +#define xASM(x) __asm(x) + +__attribute__((naked)) void closure_bridge_template() { + // DO NOT USE prologue + // x29 == fp, x30 == lr + // xASM("stp x29, x30, [sp, #-16]!"); + // xASM("mov x29, sp"); + + // save {q0-q7} + xASM("sub sp, sp, #(8*16)"); + xASM("stp q6, q7, [sp, #(6*16)]"); + xASM("stp q4, q5, [sp, #(4*16)]"); + xASM("stp q2, q3, [sp, #(2*16)]"); + xASM("stp q0, q1, [sp, #(0*16)]"); + + // save {x1-x30} + xASM("sub sp, sp, #(30*8)"); + // stp fp, lr, [sp, #(28*8)]"); + xASM("stp x29, x30, [sp, #(28*8)]"); + xASM("stp x27, x28, [sp, #(26*8)]"); + xASM("stp x25, x26, [sp, #(24*8)]"); + xASM("stp x23, x24, [sp, #(22*8)]"); + xASM("stp x21, x22, [sp, #(20*8)]"); + xASM("stp x19, x20, [sp, #(18*8)]"); + xASM("stp x17, x18, [sp, #(16*8)]"); + xASM("stp x15, x16, [sp, #(14*8)]"); + xASM("stp x13, x14, [sp, #(12*8)]"); + xASM("stp x11, x12, [sp, #(10*8)]"); + xASM("stp x9, x10, [sp, #(8*8)]"); + xASM("stp x7, x8, [sp, #(6*8)]"); + xASM("stp x5, x6, [sp, #(4*8)]"); + xASM("stp x3, x4, [sp, #(2*8)]"); + xASM("stp x1, x2, [sp, #(0*8)]"); + +#if 1 + // save {x0} + xASM("sub sp, sp, #(2*8)"); + xASM("str x0, [sp, #8]"); +#else + // save {x0, sp} + // save x0 and reserve sp, but this is trick + xASM("sub sp, sp, #(2*8)"); + xASM("str x0, [sp, #8]"); + // save origin sp + xASM("add x1, sp, #0x190"); + xASM("str x1, [sp, #0]"); +#endif + + // ======= Jump to UnifiedInterface Bridge Handle ======= + + // prepare args + // @x0: data_address + // @x1: DobbyRegisterContext stack address + xASM("mov x0, sp"); + xASM("mov x1, x14"); + xASM("bl " xcdecl("common_closure_bridge_handler")); + + // ======= DobbyRegisterContext Restore ======= + // restore x0 + xASM("ldr x0, [sp, #8]"); + xASM("add sp, sp, #(2*8)"); + + // restore {x1-x30} + xASM("ldp x1, x2, [sp], #16"); + xASM("ldp x3, x4, [sp], #16"); + xASM("ldp x5, x6, [sp], #16"); + xASM("ldp x7, x8, [sp], #16"); + xASM("ldp x9, x10, [sp], #16"); + xASM("ldp x11, x12, [sp], #16"); + xASM("ldp x13, x14, [sp], #16"); + xASM("ldp x15, x16, [sp], #16"); + xASM("ldp x17, x18, [sp], #16"); + xASM("ldp x19, x20, [sp], #16"); + xASM("ldp x21, x22, [sp], #16"); + xASM("ldp x23, x24, [sp], #16"); + xASM("ldp x25, x26, [sp], #16"); + xASM("ldp x27, x28, [sp], #16"); + // ldp fp, lr, [sp], #16"); + xASM("ldp x29, x30, [sp], #16"); + + // restore {q0-q7} + xASM("ldp q0, q1, [sp], #32"); + xASM("ldp q2, q3, [sp], #32"); + xASM("ldp q4, q5, [sp], #32"); + xASM("ldp q6, q7, [sp], #32"); + + // DO NOT USE epilog + // x29 == fp, x30 == lr + // xASM("mov sp, x29"); + // xASM("ldp x29, x30, [sp], #16"); + + xASM("br x15"); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-trampoline-template-arm64.S b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-trampoline-template-arm64.S new file mode 100644 index 0000000..2d54868 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-trampoline-template-arm64.S @@ -0,0 +1,36 @@ +// .section __TEXT,__text,regular,pure_instructions + +#if defined(ENABLE_CLOSURE_BRIDGE_TEMPLATE) + +#if defined(__WIN32__) || defined(__APPLE__) +#define cdecl(s) _##s +#else +#define cdecl(s) s +#endif + +.align 4 + +#if !defined(ENABLE_CLOSURE_TRAMPOLINE_CARRY_OBJECT_PTR) + +// closure trampoline carray the object pointer, and fetch required members at the runtime assembly code. +// #include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" +// #define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT)) +#define OFFSETOF_ClourseTrampolineEntry_carry_data 8 +#define OFFSETOF_ClourseTrampolineEntry_carry_handler 0 + .globl + cdecl(closure_trampoline_template) cdecl(closure_trampoline_template) + : ldr x17, +ClourseTrampolineEntryPtr ldr x16, OFFSETOF_ClourseTrampolineEntry_carry_data ldr x17, +OFFSETOF_ClourseTrampolineEntry_carry_handler br x17 ClourseTrampolineEntryPtr :.long 0.long 0 + +#else + + ; +closure trampoline just carray the required members from the object..globl cdecl(closure_trampoline_template) + cdecl(closure_trampoline_template) + : ldr x16, += carry_data ldr x17, = carry_handler br x17 carry_data :.long 0.long 0 carry_handler :.long 0.long 0 + +#endif + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/dynamic-closure-trampoline-template-arm64.S b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/dynamic-closure-trampoline-template-arm64.S new file mode 100644 index 0000000..a52032f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/dynamic-closure-trampoline-template-arm64.S @@ -0,0 +1,33 @@ +// .section __TEXT,__text,regular,pure_instructions + +// For iOS, we can't allocate executable memory, but we can use `remap` doing some trick. +// For details, please refer `libffi` + +#if defined(ENABLE_CLOSURE_BRIDGE_TEMPLATE) + +#if defined(__WIN32__) || defined(__APPLE__) +#define cdecl(s) _##s +#else +#define cdecl(s) s +#endif + +#define PAGE_MAX_SIZE 4096 +#define PAGE_MAX_SHIFT 14 + +.align PAGE_MAX_SHIFT.globl cdecl(dynamic_closure_trampoline_table_page) cdecl(dynamic_closure_trampoline_table_page) + :.rept(PAGE_MAX_SIZE - 4 * 4) / + 8 // sub dynamic_closure_trampoline_forward size + adr x16, +#0 b cdecl(dynamic_closure_trampoline_forward) + .endr + + cdecl(dynamic_closure_trampoline_forward) + : sub x16, +x16, #0x4000 // [DynamicClosureTrampoline **] + ldr x16, +[ x16, #0 ] // [DynamicClosureTrampoline *] + ldr x17, +[ x16, #0 ] // trampolineTo + br x17 + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/helper_arm64.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/helper_arm64.cc new file mode 100644 index 0000000..c0046d4 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/helper_arm64.cc @@ -0,0 +1,17 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "core/assembler/assembler-arm64.h" + +#include "dobby/dobby_internal.h" + +using namespace zz::arm64; + +void set_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address) { + *reinterpret_cast(&ctx->general.x[TMP_REG_0.code()]) = address; +} + +void get_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address) { +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.cc new file mode 100644 index 0000000..b1be02a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.cc @@ -0,0 +1,22 @@ +#include "logging/logging.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h" + +PUBLIC void common_closure_bridge_handler(DobbyRegisterContext *ctx, ClosureTrampolineEntry *entry) { + DEBUG_LOG("common bridge handler: carry data: %p, carry handler: %p", (InterceptEntry *)entry->carry_data, + entry->carry_handler); + + typedef void (*routing_handler_t)(InterceptEntry *, DobbyRegisterContext *); + auto routing_handler = (routing_handler_t)entry->carry_handler; + +#if defined(__APPLE__) && __arm64e__ +#if __has_feature(ptrauth_calls) + uint64_t discriminator = 0; + // discriminator = __builtin_ptrauth_type_discriminator(__typeof(routing_handler)); + routing_handler = (__typeof(routing_handler))__builtin_ptrauth_sign_unauthenticated((void *)routing_handler, + ptrauth_key_asia, discriminator); +#endif +#endif + + routing_handler((InterceptEntry *)entry->carry_data, ctx); +} diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h new file mode 100644 index 0000000..c02eca3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h @@ -0,0 +1,17 @@ +#ifndef CLOSURE_TRAMPOLINE_COMMON_HANDLER_H +#define CLOSURE_TRAMPOLINE_COMMON_HANDLER_H + +#include "dobby/dobby_internal.h" + +#include "Interceptor.h" +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +extern "C" { +void common_closure_bridge_handler(DobbyRegisterContext *ctx, ClosureTrampolineEntry *entry); +} + +void get_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address); + +void set_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address); + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/ClosureTrampolineX64.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/ClosureTrampolineX64.cc new file mode 100644 index 0000000..f3abb2d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/ClosureTrampolineX64.cc @@ -0,0 +1,45 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_X64) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-x64.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +using namespace zz; +using namespace zz::x64; + +ClosureTrampolineEntry *ClosureTrampoline::CreateClosureTrampoline(void *carry_data, void *carry_handler) { + ClosureTrampolineEntry *tramp_entry = nullptr; + tramp_entry = new ClosureTrampolineEntry; + + auto tramp_size = 32; + auto tramp_mem = MemoryAllocator::SharedAllocator()->allocateExecMemory(tramp_size); + if (tramp_mem == nullptr) { + return nullptr; + } +#define _ turbo_assembler_. +#define __ turbo_assembler_.GetCodeBuffer()-> + TurboAssembler turbo_assembler_(0); + + uint8_t *push_rip_6 = (uint8_t *)"\xff\x35\x06\x00\x00\x00"; + uint8_t *jmp_rip_8 = (uint8_t *)"\xff\x25\x08\x00\x00\x00"; + + __ EmitBuffer(push_rip_6, 6); + __ EmitBuffer(jmp_rip_8, 6); + __ Emit64((uint64_t)tramp_entry); + __ Emit64((uint64_t)get_closure_bridge()); + + tramp_entry->address = tramp_mem; + tramp_entry->size = tramp_size; + tramp_entry->carry_data = carry_data; + tramp_entry->carry_handler = carry_handler; + + auto closure_tramp_buffer = static_cast(turbo_assembler_.GetCodeBuffer()); + DobbyCodePatch(tramp_mem, (uint8_t *)closure_tramp_buffer->GetBuffer(), closure_tramp_buffer->GetBufferSize()); + + return tramp_entry; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/closure_bridge_x64.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/closure_bridge_x64.cc new file mode 100644 index 0000000..c0c5ffc --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/closure_bridge_x64.cc @@ -0,0 +1,141 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_X64) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-x64.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h" + +using namespace zz; +using namespace zz::x64; + +static asm_func_t closure_bridge = nullptr; + +asm_func_t get_closure_bridge() { + // if already initialized, just return. + if (closure_bridge) + return closure_bridge; + +// Check if enable the inline-assembly closure_bridge_template +#if ENABLE_CLOSURE_BRIDGE_TEMPLATE + + extern void closure_bridge_tempate(); + closure_bridge = closure_bridge_template; + +#else + +// otherwise, use the Assembler build the closure_bridge +#define _ turbo_assembler_. +#define __ turbo_assembler_.GetCodeBuffer()-> + + uint8_t *pushfq = (uint8_t *)"\x9c"; + uint8_t *popfq = (uint8_t *)"\x9d"; + + TurboAssembler turbo_assembler_(0); + + // save flags register + __ EmitBuffer(pushfq, 1); + // align rsp 16-byte + _ sub(rsp, Immediate(8, 32)); + + // general register + _ sub(rsp, Immediate(16 * 8, 32)); + _ mov(Address(rsp, 8 * 0), rax); + _ mov(Address(rsp, 8 * 1), rbx); + _ mov(Address(rsp, 8 * 2), rcx); + _ mov(Address(rsp, 8 * 3), rdx); + _ mov(Address(rsp, 8 * 4), rbp); + _ mov(Address(rsp, 8 * 5), rsp); + _ mov(Address(rsp, 8 * 6), rdi); + _ mov(Address(rsp, 8 * 7), rsi); + _ mov(Address(rsp, 8 * 8), r8); + _ mov(Address(rsp, 8 * 9), r9); + _ mov(Address(rsp, 8 * 10), r10); + _ mov(Address(rsp, 8 * 11), r11); + _ mov(Address(rsp, 8 * 12), r12); + _ mov(Address(rsp, 8 * 13), r13); + _ mov(Address(rsp, 8 * 14), r14); + _ mov(Address(rsp, 8 * 15), r15); + + // save origin sp + _ mov(rax, rsp); + _ add(rax, Immediate(8 + 8 + 8 + 16 * 8, 32)); + _ sub(rsp, Immediate(2 * 8, 32)); + _ mov(Address(rsp, 8), rax); + + // ======= Jump to UnifiedInterface Bridge Handle ======= + + // prepare args + // @rdi: data_address + // @rsi: DobbyRegisterContext stack address + _ mov(rdi, rsp); + _ mov(rsi, Address(rsp, 8 + 8 + 16 * 8 + 2 * 8)); + + // [!!!] As we can't detect the sp is aligned or not, check if need stack align + { + // mov rax, rsp + __ EmitBuffer((uint8_t *)"\x48\x89\xE0", 3); + // and rax, 0xF + __ EmitBuffer((uint8_t *)"\x48\x83\xE0\x0F", 4); + // cmp rax, 0x0 + __ EmitBuffer((uint8_t *)"\x48\x83\xF8\x00", 4); + // jnz [stack_align_call_bridge] + __ EmitBuffer((uint8_t *)"\x75\x15", 2); + } + + // LABEL: call_bridge + _ CallFunction(ExternalReference((void *)common_closure_bridge_handler)); + + // jmp [restore_stack_register] + __ EmitBuffer((uint8_t *)"\xE9\x12\x00\x00\x00", 5); + + // LABEL: stack_align_call_bridge + // push rax + __ EmitBuffer((uint8_t *)"\x50", 1); + _ CallFunction(ExternalReference((void *)common_closure_bridge_handler)); + // pop rax + __ EmitBuffer((uint8_t *)"\x58", 1); + + // ======= DobbyRegisterContext Restore ======= + + // restore sp placeholder stack + _ add(rsp, Immediate(2 * 8, 32)); + + // general register + _ pop(rax); + _ pop(rbx); + _ pop(rcx); + _ pop(rdx); + _ pop(rbp); + _ add(rsp, Immediate(8, 32)); // => pop rsp + _ pop(rdi); + _ pop(rsi); + _ pop(r8); + _ pop(r9); + _ pop(r10); + _ pop(r11); + _ pop(r12); + _ pop(r13); + _ pop(r14); + _ pop(r15); + + // align rsp 16-byte + _ add(rsp, Immediate(8, 32)); + // restore flags register + __ EmitBuffer(popfq, 1); + + // trick: use the 'carry_data' stack(remain at closure trampoline) placeholder, as the return address + _ ret(); + + _ RelocBind(); + + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + closure_bridge = (asm_func_t)code->addr; + + DEBUG_LOG("[closure bridge] closure bridge at %p", closure_bridge); +#endif + return closure_bridge; +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-bridge-template-x64.c b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-bridge-template-x64.c new file mode 100644 index 0000000..d59dcdc --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-bridge-template-x64.c @@ -0,0 +1,70 @@ +#if defined(ENABLE_CLOSURE_BRIDGE_TEMPLATE) + +#if defined(__WIN32__) || defined(__APPLE__) +#define xcdecl(s) "_" s +#else +#define xcdecl(s) s +#endif + +#define xASM(x) __asm(x) + +__attribute__((naked)) void closure_bridge_template() { + // flags register + xASM("pushfq"); + + // general register + xASM("sub rsp, #(16*8)"); + xASM("mov [rsp+16*0], rax"); + xASM("mov [rsp+16*1], rbx"); + xASM("mov [rsp+16*2], rcx"); + xASM("mov [rsp+16*3], rdx"); + xASM("mov [rsp+16*4], rbp"); + xASM("mov [rsp+16*5], rsp"); + xASM("mov [rsp+16*6], rdi"); + xASM("mov [rsp+16*7], rsi"); + xASM("mov [rsp+16*8], r8"); + xASM("mov [rsp+16*9], r9"); + xASM("mov [rsp+16*10], r10"); + xASM("mov [rsp+16*11], r11"); + xASM("mov [rsp+16*12], r12"); + xASM("mov [rsp+16*13], r13"); + xASM("mov [rsp+16*14], r14"); + xASM("mov [rsp+16*15], r15"); + + // ======= Jump to UnifiedInterface Bridge Handle ======= + + // prepare args + // @rdi: data_address + // @rsi: DobbyRegisterContext stack address + xASM("mov rdi, rsp"); + xASM("mov rsi, [rsp-16*8]"); + xASM("call " xcdecl("common_closure_bridge_handler")); + + // ======= DobbyRegisterContext Restore ======= + + // general register + xASM("pop r15"); + xASM("pop r14"); + xASM("pop r13"); + xASM("pop r12"); + xASM("pop r11"); + xASM("pop r10"); + xASM("pop r9"); + xASM("pop r8"); + xASM("pop rsi"); + xASM("pop rdi"); + xASM("pop rsp"); + xASM("pop rbp"); + xASM("pop rdx"); + xASM("pop rcx"); + xASM("pop rbx"); + xASM("pop rax"); + + // flags register + xASM("popfq"); + + // trick: use the 'carry_data' placeholder, as the return address + xASM("ret"); +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-trampoline-template-x64.S b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-trampoline-template-x64.S new file mode 100644 index 0000000..c544dc8 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-trampoline-template-x64.S @@ -0,0 +1,17 @@ +#if defined(ENABLE_CLOSURE_BRIDGE_TEMPLATE) + +#if defined(__WIN32__) || defined(__APPLE__) +#define cdecl(s) _##s +#else +#define cdecl(s) s +#endif + +.align 4 + + ; +closure trampoline just carray the required members from the object. + .globl + cdecl(closure_trampoline_template) cdecl(closure_trampoline_template) + : push[rip + 6 + 6] jmp[rip + 6 + 8] carry_data :.long 0.long 0 carry_handler :.long 0.long 0 + +#endif diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/helper_x64.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/helper_x64.cc new file mode 100644 index 0000000..1f59357 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/helper_x64.cc @@ -0,0 +1,17 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_X64) + +#include "dobby/dobby_internal.h" + +void set_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address) { + addr_t rsp = ctx->rsp; + + // ClosureTrampolineEntry reserved stack + addr_t entry_placeholder_stack_addr = rsp - 8; + *(addr_t *)entry_placeholder_stack_addr = (addr_t)address; +} + +void get_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address) { +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/ClosureTrampolineX86.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/ClosureTrampolineX86.cc new file mode 100644 index 0000000..c29b190 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/ClosureTrampolineX86.cc @@ -0,0 +1,44 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-ia32.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h" + +using namespace zz; +using namespace zz::x86; + +ClosureTrampolineEntry *ClosureTrampoline::CreateClosureTrampoline(void *carry_data, void *carry_handler) { + ClosureTrampolineEntry *tramp_entry = nullptr; + tramp_entry = new ClosureTrampolineEntry; + + auto tramp_size = 32; + auto tramp_mem = MemoryAllocator::SharedAllocator()->allocateExecMemory(tramp_size); + if (tramp_mem == nullptr) { + return nullptr; + } + +#define _ turbo_assembler_. +#define __ turbo_assembler_.GetCodeBuffer()-> + TurboAssembler turbo_assembler_(tramp_mem); + + int32_t offset = (int32_t)((uintptr_t)get_closure_bridge() - ((uintptr_t)tramp_mem + 18)); + + _ sub(esp, Immediate(4, 32)); + _ mov(Address(esp, 4 * 0), Immediate((int32_t)(uintptr_t)tramp_entry, 32)); + _ jmp(Immediate(offset, 32)); + + tramp_entry->address = tramp_mem; + tramp_entry->size = tramp_size; + tramp_entry->carry_data = carry_data; + tramp_entry->carry_handler = carry_handler; + + auto closure_tramp_buffer = static_cast(turbo_assembler_.GetCodeBuffer()); + DobbyCodePatch(tramp_mem, (uint8_t *)closure_tramp_buffer->GetBuffer(), closure_tramp_buffer->GetBufferSize()); + + return tramp_entry; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/closure_bridge_x86.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/closure_bridge_x86.cc new file mode 100644 index 0000000..acc940a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/closure_bridge_x86.cc @@ -0,0 +1,112 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-ia32.h" + +#include "TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.h" + +using namespace zz; +using namespace zz::x86; + +static asm_func_t closure_bridge = NULL; + +asm_func_t get_closure_bridge() { + // if already initialized, just return. + if (closure_bridge) + return closure_bridge; + +// Check if enable the inline-assembly closure_bridge_template +#if ENABLE_CLOSURE_BRIDGE_TEMPLATE + + extern void closure_bridge_tempate(); + closure_bridge = closure_bridge_template; + +#else + +// otherwise, use the Assembler build the closure_bridge +#define _ turbo_assembler_. +#define __ turbo_assembler_.GetCodeBuffer()-> + + auto pushfd = (uint8_t *)"\x9c"; + auto popfd = (uint8_t *)"\x9d"; + + TurboAssembler turbo_assembler_(0); + + // general register + _ sub(esp, Immediate(8 * 4, 32)); + _ mov(Address(esp, 4 * 0), eax); + _ mov(Address(esp, 4 * 1), ebx); + _ mov(Address(esp, 4 * 2), ecx); + _ mov(Address(esp, 4 * 3), edx); + _ mov(Address(esp, 4 * 4), ebp); + _ mov(Address(esp, 4 * 5), esp); + _ mov(Address(esp, 4 * 6), edi); + _ mov(Address(esp, 4 * 7), esi); + + // save flags register + __ EmitBuffer(pushfd, 1); + _ pop(eax); + { // save to stack + _ sub(esp, Immediate(2 * 4, 32)); + _ mov(Address(esp, 4), eax); + } + + // save origin sp + _ mov(eax, esp); + _ add(eax, Immediate(8 * 4 + 2 * 4 + 4, 32)); + { // save to stack + _ sub(esp, Immediate(2 * 4, 32)); + _ mov(Address(esp, 4), eax); + } + + // ======= Jump to UnifiedInterface Bridge Handle ======= + + // prepare args + _ sub(esp, Immediate(2 * 4, 32)); + _ mov(eax, Address(esp, 8 * 4 + 2 * 4 + 2 * 4 + 2 * 4)); + _ mov(Address(esp, 4), eax); + _ mov(eax, esp); + _ add(eax, Immediate(2 * 4, 32)); + _ mov(Address(esp, 0), eax); + + // LABEL: call_bridge + _ CallFunction(ExternalReference((void *)common_closure_bridge_handler)); + + // ======= DobbyRegisterContext Restore ======= + + // restore argument reserved stack + _ add(esp, Immediate(2 * 4, 32)); + + // restore sp placeholder stack + _ add(esp, Immediate(2 * 4, 32)); + + _ add(esp, Immediate(4, 32)); + // restore flags register + __ EmitBuffer(popfd, 1); + + // general register + _ pop(eax); + _ pop(ebx); + _ pop(ecx); + _ pop(edx); + _ pop(ebp); + _ add(esp, Immediate(4, 32)); // => pop rsp + _ pop(edi); + _ pop(esi); + + // trick: use the 'carry_data' stack(remain at closure trampoline) placeholder, as the return address + _ ret(); + + _ RelocBind(); + + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + closure_bridge = (asm_func_t)code->addr; + + DEBUG_LOG("[closure bridge] closure bridge at %p", closure_bridge); +#endif + return closure_bridge; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/helper_x86.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/helper_x86.cc new file mode 100644 index 0000000..3dac41c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/helper_x86.cc @@ -0,0 +1,16 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "dobby/dobby_internal.h" + +void set_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address) { + addr_t esp = ctx->esp; + + addr_t entry_placeholder_stack_addr = esp - 4; + *(addr_t *)entry_placeholder_stack_addr = (addr_t)address; +} + +void get_routing_bridge_next_hop(DobbyRegisterContext *ctx, void *address) { +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/Trampoline.h b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/Trampoline.h new file mode 100644 index 0000000..53f3779 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/Trampoline.h @@ -0,0 +1,5 @@ +#pragma once + +#include "MemoryAllocator/AssemblyCodeBuilder.h" + +CodeBufferBase *GenerateNormalTrampolineBuffer(addr_t from, addr_t to); \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm/trampoline_arm.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm/trampoline_arm.cc new file mode 100644 index 0000000..c8c9033 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm/trampoline_arm.cc @@ -0,0 +1,62 @@ +#include "platform_detect_macro.h" + +#if defined(TARGET_ARCH_ARM) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-arm.h" +#include "core/codegen/codegen-arm.h" + +#include "InstructionRelocation/arm/InstructionRelocationARM.h" +#include "MemoryAllocator/NearMemoryAllocator.h" +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +using namespace zz::arm; + +static CodeBufferBase *generate_arm_trampoline(addr32_t from, addr32_t to) { + TurboAssembler turbo_assembler_((void *)from); +#define _ turbo_assembler_. + + CodeGen codegen(&turbo_assembler_); + codegen.LiteralLdrBranch(to); + + return turbo_assembler_.GetCodeBuffer()->Copy(); +} + +CodeBufferBase *generate_thumb_trampoline(addr32_t from, addr32_t to) { + ThumbTurboAssembler thumb_turbo_assembler_((void *)from); +#undef _ +#define _ thumb_turbo_assembler_. + + _ AlignThumbNop(); + _ t2_ldr(pc, MemOperand(pc, 0)); + _ EmitAddress(to); + + return thumb_turbo_assembler_.GetCodeBuffer()->Copy(); +} + +CodeBufferBase *GenerateNormalTrampolineBuffer(addr_t from, addr_t to) { + enum ExecuteState { ARMExecuteState, ThumbExecuteState }; + + // set instruction running state + ExecuteState execute_state_; + execute_state_ = ARMExecuteState; + if ((addr_t)from % 2) { + execute_state_ = ThumbExecuteState; + } + + if (execute_state_ == ARMExecuteState) { + return generate_arm_trampoline(from, to); + } else { + // Check if needed pc align, (relative pc instructions needed 4 align) + from = from - THUMB_ADDRESS_FLAG; + return generate_thumb_trampoline(from, to); + } + return NULL; +} + +CodeBufferBase *GenerateNearTrampolineBuffer(InterceptRouting *routing, addr_t src, addr_t dst) { + return NULL; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm64/trampoline_arm64.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm64/trampoline_arm64.cc new file mode 100644 index 0000000..66eb63c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm64/trampoline_arm64.cc @@ -0,0 +1,41 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-arm64.h" +#include "core/codegen/codegen-arm64.h" + +#include "MemoryAllocator/NearMemoryAllocator.h" +#include "InstructionRelocation/arm64/InstructionRelocationARM64.h" +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +using namespace zz::arm64; + +CodeBufferBase *GenerateNormalTrampolineBuffer(addr_t from, addr_t to) { + TurboAssembler turbo_assembler_((void *)from); +#define _ turbo_assembler_. + + uint64_t distance = llabs((int64_t)(from - to)); + uint64_t adrp_range = ((uint64_t)1 << (2 + 19 + 12 - 1)); + if (distance < adrp_range) { + // adrp, add, br + _ AdrpAdd(TMP_REG_0, from, to); + _ br(TMP_REG_0); + DEBUG_LOG("[trampoline] use [adrp, add, br]"); + } else { + // ldr, br, branch-address + CodeGen codegen(&turbo_assembler_); + codegen.LiteralLdrBranch((uint64_t)to); + DEBUG_LOG("[trampoline] use [ldr, br, #label]"); + } +#undef _ + + // Bind all labels + turbo_assembler_.RelocBind(); + + auto result = turbo_assembler_.GetCodeBuffer()->Copy(); + return result; +} + +#endif diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x64/trampoline_x64.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x64/trampoline_x64.cc new file mode 100644 index 0000000..53afd43 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x64/trampoline_x64.cc @@ -0,0 +1,54 @@ +#include "platform_detect_macro.h" + +#if defined(TARGET_ARCH_X64) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-x64.h" +#include "core/codegen/codegen-x64.h" + +#include "InstructionRelocation/x64/InstructionRelocationX64.h" + +#include "MemoryAllocator/NearMemoryAllocator.h" +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +using namespace zz::x64; + +static addr_t allocate_indirect_stub(addr_t jmp_insn_addr) { + uint32_t jmp_near_range = (uint32_t)2 * 1024 * 1024 * 1024; + auto stub_addr = (addr_t)NearMemoryAllocator::SharedAllocator()->allocateNearDataMemory(sizeof(void *), jmp_insn_addr, + jmp_near_range); + if (stub_addr == 0) { + ERROR_LOG("Not found near forward stub"); + return 0; + } + + DEBUG_LOG("forward stub: %p", stub_addr); + return stub_addr; +} + +CodeBufferBase *GenerateNormalTrampolineBuffer(addr_t from, addr_t to) { + TurboAssembler turbo_assembler_((void *)from); +#define _ turbo_assembler_. + + // allocate forward stub + auto jump_near_next_insn_addr = from + 6; + addr_t forward_stub = allocate_indirect_stub(jump_near_next_insn_addr); + if (forward_stub == 0) + return nullptr; + + *(addr_t *)forward_stub = to; + + CodeGen codegen(&turbo_assembler_); + codegen.JmpNearIndirect((addr_t)forward_stub); + + auto buffer = turbo_assembler_.GetCodeBuffer()->Copy(); + return buffer; +} + +CodeBufferBase *GenerateNearTrampolineBuffer(InterceptRouting *routing, addr_t src, addr_t dst) { + DEBUG_LOG("x64 near branch trampoline enable default"); + return nullptr; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x86/trampoline_x86.cc b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x86/trampoline_x86.cc new file mode 100644 index 0000000..50b4641 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x86/trampoline_x86.cc @@ -0,0 +1,33 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "dobby/dobby_internal.h" + +#include "core/assembler/assembler-ia32.h" +#include "core/codegen/codegen-ia32.h" + +#include "InstructionRelocation/x86/InstructionRelocationX86.h" + +#include "MemoryAllocator/NearMemoryAllocator.h" +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +using namespace zz::x86; + +CodeBufferBase *GenerateNormalTrampolineBuffer(addr_t from, addr_t to) { + TurboAssembler turbo_assembler_((void *)from); +#define _ turbo_assembler_. + + CodeGen codegen(&turbo_assembler_); + codegen.JmpNear((uint32_t)to); + + CodeBufferBase *result = NULL; + result = turbo_assembler_.GetCodeBuffer()->Copy(); + return result; +} + +CodeBufferBase *GenerateNearTrampolineBuffer(InterceptRouting *routing, addr_t src, addr_t dst) { + DEBUG_LOG("x86 near branch trampoline enable default"); + return NULL; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/Cpu.cc b/app/src/main/cpp/Dobby/source/core/arch/Cpu.cc new file mode 100644 index 0000000..0716361 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/Cpu.cc @@ -0,0 +1,5 @@ + +#include "core/arch/Cpu.h" +#include "core/arch/CpuUtils.h" + +#include "xnucxx/LiteMemOpt.h" diff --git a/app/src/main/cpp/Dobby/source/core/arch/Cpu.h b/app/src/main/cpp/Dobby/source/core/arch/Cpu.h new file mode 100644 index 0000000..e0361a7 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/Cpu.h @@ -0,0 +1,7 @@ +#ifndef CORE_ARCH_CPU_H +#define CORE_ARCH_CPU_H + +#include "CpuRegister.h" +#include "CpuFeature.h" + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/arch/CpuFeature.cc b/app/src/main/cpp/Dobby/source/core/arch/CpuFeature.cc new file mode 100644 index 0000000..d29f176 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/CpuFeature.cc @@ -0,0 +1,7 @@ + +#include "core/arch/CpuFeature.h" +#include "logging/logging.h" + +void CpuFeatures::ClearCache(void *start, void *end) { + UNIMPLEMENTED(); +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/CpuFeature.h b/app/src/main/cpp/Dobby/source/core/arch/CpuFeature.h new file mode 100644 index 0000000..adb50ae --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/CpuFeature.h @@ -0,0 +1,19 @@ +#ifndef CORE_ARCH_CPU_FEATURE_H +#define CORE_ARCH_CPU_FEATURE_H + +#include "dobby/common.h" + +class CpuFeatures { +private: + static void FlushICache(void *start, size_t size) { + ClearCache(start, (void *)((addr_t)start + size)); + } + + static void FlushICache(void *start, void *end) { + ClearCache(start, end); + } + + static void ClearCache(void *start, void *end); +}; + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/arch/CpuRegister.cc b/app/src/main/cpp/Dobby/source/core/arch/CpuRegister.cc new file mode 100644 index 0000000..3617e4e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/CpuRegister.cc @@ -0,0 +1,10 @@ + +#include "CpuRegister.h" + +constexpr RegisterBase RegisterBase::from_code(int code) { + return RegisterBase{code}; +} + +constexpr RegisterBase RegisterBase::no_reg() { + return RegisterBase{0}; +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/CpuRegister.h b/app/src/main/cpp/Dobby/source/core/arch/CpuRegister.h new file mode 100644 index 0000000..e12aff4 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/CpuRegister.h @@ -0,0 +1,25 @@ +#ifndef CORE_ARCH_CPU_REGISTER_H +#define CORE_ARCH_CPU_REGISTER_H + +class RegisterBase { +public: + static constexpr RegisterBase from_code(int code); + + static constexpr RegisterBase no_reg(); + + virtual bool Is(const RegisterBase ®) const { + return (reg.reg_code_ == this->reg_code_); + } + + int code() const { + return reg_code_; + }; + +protected: + explicit constexpr RegisterBase(int code) : reg_code_(code) { + } + + int reg_code_; +}; + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/arch/CpuUtils.h b/app/src/main/cpp/Dobby/source/core/arch/CpuUtils.h new file mode 100644 index 0000000..3942363 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/CpuUtils.h @@ -0,0 +1,17 @@ +#ifndef CPU_UTILITY_H +#define CPU_UTILITY_H + +/* Define the default attributes for the functions in this file. */ +#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__)) + +#if defined(__i386__) || defined(__x86_64__) +static __inline__ void __DEFAULT_FN_ATTRS __cpuid(int __info[4], int __level) { + __asm__("cpuid" : "=a"(__info[0]), "=b"(__info[1]), "=c"(__info[2]), "=d"(__info[3]) : "a"(__level)); +} + +static __inline__ void __DEFAULT_FN_ATTRS __cpuidex(int __info[4], int __level, int __ecx) { + __asm__("cpuid" : "=a"(__info[0]), "=b"(__info[1]), "=c"(__info[2]), "=d"(__info[3]) : "a"(__level), "c"(__ecx)); +} +#endif + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/arch/arm/constants-arm.h b/app/src/main/cpp/Dobby/source/core/arch/arm/constants-arm.h new file mode 100644 index 0000000..b326d34 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/arm/constants-arm.h @@ -0,0 +1,70 @@ +#ifndef CORE_ARCH_CONSTANTS_ARM_H +#define CORE_ARCH_CONSTANTS_ARM_H + +enum AddrMode { Offset = 0, PreIndex = 1, PostIndex = 2 }; + +enum Condition { + EQ = 0, // equal + NE = 1, // not equal + CS = 2, // carry set/unsigned higher or same + CC = 3, // carry clear/unsigned lower + MI = 4, // minus/negative + PL = 5, // plus/positive or zero + VS = 6, // overflow + VC = 7, // no overflow + HI = 8, // unsigned higher + LS = 9, // unsigned lower or same + GE = 10, // signed greater than or equal + LT = 11, // signed less than + GT = 12, // signed greater than + LE = 13, // signed less than or equal + AL = 14, // always (unconditional) + +}; + +enum Shift { + LSL = 0, // Logical shift left + LSR = 1, // Logical shift right + ASR = 2, // Arithmetic shift right + ROR = 3, // Rotate right +}; + +enum { + B0 = 1 << 0, + B4 = 1 << 4, + B5 = 1 << 5, + B6 = 1 << 6, + B7 = 1 << 7, + B8 = 1 << 8, + B9 = 1 << 9, + B10 = 1 << 10, + B12 = 1 << 12, + B14 = 1 << 14, + B15 = 1 << 15, + B16 = 1 << 16, + B17 = 1 << 17, + B18 = 1 << 18, + B19 = 1 << 19, + B20 = 1 << 20, + B21 = 1 << 21, + B22 = 1 << 22, + B23 = 1 << 23, + B24 = 1 << 24, + B25 = 1 << 25, + B26 = 1 << 26, + B27 = 1 << 27, + B28 = 1 << 28, +}; + +enum InstructionFields { + // Registers. + kRdShift = 12, + kRtShift = 12, + kRmShift = 10, + kRnShift = 16, + + // Condition + kConditionShift = 28, +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/arm/registers-arm.h b/app/src/main/cpp/Dobby/source/core/arch/arm/registers-arm.h new file mode 100644 index 0000000..88b7c2e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/arm/registers-arm.h @@ -0,0 +1,58 @@ +#ifndef ARCH_ARM_REGISTERS +#define ARCH_ARM_REGISTERS + +#include "core/arch/arm/constants-arm.h" +#include "core/arch/Cpu.h" + +namespace zz { +namespace arm { + +#define GENERAL_REGISTERS(V) \ + V(r0) V(r1) V(r2) V(r3) V(r4) V(r5) V(r6) V(r7) V(r8) V(r9) V(r10) V(r11) V(r12) V(sp) V(lr) V(pc) + +enum RegisterCode { +#define REGISTER_CODE(R) kRegCode_##R, + GENERAL_REGISTERS(REGISTER_CODE) +#undef REGISTER_CODE + kRegAfterLast +}; + +class Register : public RegisterBase { +public: + explicit constexpr Register(int code) : RegisterBase(code) { + } + + static constexpr Register Create(int code) { + return Register(code); + } + + static constexpr Register R(int code) { + return Register(code); + } + + bool Is(const Register ®) const { + return (reg.reg_code_ == this->reg_code_); + } + + bool IsValid() const { + return (reg_code_ != 0); + } + + int code() const { + return reg_code_; + } + +private: +}; + +typedef Register CPURegister; + +#define DECLARE_REGISTER(R) constexpr Register R = Register::Create(kRegCode_##R); +GENERAL_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER + +constexpr Register no_reg = Register::Create(0); + +} // namespace arm +} // namespace zz +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/arm64/constants-arm64.h b/app/src/main/cpp/Dobby/source/core/arch/arm64/constants-arm64.h new file mode 100644 index 0000000..e72a6ed --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/arm64/constants-arm64.h @@ -0,0 +1,387 @@ +#ifndef CORE_ARCH_CONSTANTS_ARM64_H +#define CORE_ARCH_CONSTANTS_ARM64_H + +#include "dobby/common.h" + +enum Shift { NO_SHIFT = -1, LSL = 0x0, LSR = 0x1, ASR = 0x2, ROR = 0x3, MSL = 0x4 }; + +enum Extend { NO_EXTEND = -1, UXTB = 0, UXTH = 1, UXTW = 2, UXTX = 3, SXTB = 4, SXTH = 5, SXTW = 6, SXTX = 7 }; + +enum AddrMode { Offset, PreIndex, PostIndex }; + +enum FlagsUpdate { SetFlags = 1, LeaveFlags = 0 }; + +enum InstructionFields { + + // Registers. + kRdShift = 0, + kRdBits = 5, + kRnShift = 5, + kRnBits = 5, + kRaShift = 10, + kRaBits = 5, + kRmShift = 16, + kRmBits = 5, + kRtShift = 0, + kRtBits = 5, + kRt2Shift = 10, + kRt2Bits = 5, + kRsShift = 16, + kRsBits = 5, + +}; + +#define OP(op) op +#define OP_W(op) op##_w +#define OP_X(op) op##_x +#define OP_B(op) op##_b +#define OP_H(op) op##_h +#define OP_S(op) op##_s +#define OP_D(op) op##_d +#define OP_Q(op) op##_q + +#define OPT(op, attribute) op##_##attribute +#define OPT_W(op, attribute) op##_w_##attribute +#define OPT_X(op, attribute) op##_x_##attribute +#define OPT_B(op, attribute) op##_b_##attribute +#define OPT_H(op, attribute) op##_h_##attribute +#define OPT_S(op, attribute) op##_s_##attribute +#define OPT_D(op, attribute) op##_d_##attribute +#define OPT_Q(op, attribute) op##_q_##attribute + +// ===== + +// Exception. +enum ExceptionOp { + ExceptionFixed = 0xD4000000, + ExceptionFMask = 0xFF000000, + ExceptionMask = 0xFFE0001F, + + HLT = ExceptionFixed | 0x00400000, + BRK = ExceptionFixed | 0x00200000, + SVC = ExceptionFixed | 0x00000001, + HVC = ExceptionFixed | 0x00000002, + SMC = ExceptionFixed | 0x00000003, + DCPS1 = ExceptionFixed | 0x00A00001, + DCPS2 = ExceptionFixed | 0x00A00002, + DCPS3 = ExceptionFixed | 0x00A00003 +}; + +// ===== + +// Unconditional branch. +enum UnconditionalBranchOp { + UnconditionalBranchFixed = 0x14000000, + UnconditionalBranchFixedMask = 0x7C000000, + UnconditionalBranchMask = 0xFC000000, + + B = UnconditionalBranchFixed | 0x00000000, + BL = UnconditionalBranchFixed | 0x80000000 +}; + +// ===== + +// Unconditional branch to register. +enum UnconditionalBranchToRegisterOp { + UnconditionalBranchToRegisterFixed = 0xD6000000, + UnconditionalBranchToRegisterFixedMask = 0xFE000000, + UnconditionalBranchToRegisterMask = 0xFFFFFC1F, + + BR = UnconditionalBranchToRegisterFixed | 0x001F0000, + BLR = UnconditionalBranchToRegisterFixed | 0x003F0000, + RET = UnconditionalBranchToRegisterFixed | 0x005F0000 +}; + +// ===== + +enum LoadRegLiteralOp { + LoadRegLiteralFixed = 0x18000000, + LoadRegLiteralFixedMask = 0x3B000000, + LoadRegLiteralMask = 0xFF000000, + +#define LoadRegLiteralSub(opc, V) LoadRegLiteralFixed | LeftShift(opc, 2, 30) | LeftShift(V, 1, 26) + OPT_W(LDR, literal) = LoadRegLiteralSub(0b00, 0), + OPT_X(LDR, literal) = LoadRegLiteralSub(0b01, 0), + OPT(LDRSW, literal) = LoadRegLiteralSub(0b10, 0), + OPT(PRFM, literal) = LoadRegLiteralSub(0b11, 0), + OPT_S(LDR, literal) = LoadRegLiteralSub(0b00, 1), + OPT_D(LDR, literal) = LoadRegLiteralSub(0b01, 1), + OPT_Q(LDR, literal) = LoadRegLiteralSub(0b10, 1), +}; + +// ===== + +// clang-format off +#define LOAD_STORE_OP_LIST(V) \ + V(OP_W(STRB), 0b00, 0, 0b00), \ + V(OP_W(LDRB), 0b00, 0, 0b01), \ + V(OP_X(LDRSB), 0b00, 0, 0b10), \ + V(OP_W(LDRSB), 0b00, 0, 0b11), \ + V(OP_B(STR), 0b00, 1, 0b00), \ + V(OP_B(LDR), 0b00, 1, 0b01), \ + V(OP_Q(STR), 0b00, 1, 0b10), \ + V(OP_Q(LDR), 0b00, 1, 0b11), \ + V(OP_W(STRH), 0b01, 0, 0b00), \ + V(OP_W(LDRH), 0b01, 0, 0b01), \ + V(OP_X(LDRSH), 0b01, 0, 0b10), \ + V(OP_W(LDRSH), 0b01, 0, 0b11), \ + V(OP_H(STR), 0b01, 1, 0b00), \ + V(OP_H(LDR), 0b01, 1, 0b01), \ + V(OP_W(STR), 0b10, 0, 0b00), \ + V(OP_W(LDR), 0b10, 0, 0b01), \ + V(OP(LDRSW), 0b10, 0, 0b10), \ + V(OP_S(STR), 0b10, 1, 0b00), \ + V(OP_S(LDR), 0b10, 1, 0b01), \ + V(OP_X(STR), 0b11, 0, 0b00), \ + V(OP_X(LDR), 0b11, 0, 0b01), \ + V(OP(PRFM), 0b11, 0, 0b10), \ + V(OP_D(STR), 0b11, 1, 0b00), \ + V(OP_D(LDR), 0b11, 1, 0b01), +// clang-format on + +// Load/store +enum LoadStoreOp { +#define LoadStoreOpSub(size, V, opc) LeftShift(size, 2, 30) | LeftShift(V, 1, 26) | LeftShift(opc, 2, 22) +#define LOAD_STORE(opname, size, V, opc) OP(opname) = LoadStoreOpSub(size, V, opc) + LOAD_STORE_OP_LIST(LOAD_STORE) +#undef LOAD_STORE +}; + +// Load/store register offset. +enum LoadStoreRegisterOffsetOp { + LoadStoreRegisterOffsetFixed = 0x38200800, + LoadStoreRegisterOffsetFixedMask = 0x3B200C00, + LoadStoreRegisterOffsetMask = 0xFFE00C00, + +#define LoadStoreRegisterOffsetOpSub(size, V, opc) \ + LoadStoreRegisterOffsetFixed | LeftShift(size, 2, 30) | LeftShift(V, 1, 26) | LeftShift(opc, 2, 22) +#define LOAD_STORE_REGISTER_OFFSET(opname, size, V, opc) \ + OPT(opname, register) = LoadStoreRegisterOffsetOpSub(size, V, opc) + LOAD_STORE_OP_LIST(LOAD_STORE_REGISTER_OFFSET) +#undef LOAD_STORE_REGISTER_OFFSET +}; + +// Load/store register (unscaled immediate) +enum LoadStoreUnscaledOffsetOp { + LoadStoreUnscaledOffsetFixed = 0x38000000, + LoadStoreUnscaledOffsetFixedMask = 0x3B200C00, + LoadStoreUnscaledOffsetMask = 0xFFE00C00, + +#define LoadStoreUnscaledOffsetOpSub(size, V, opc) \ + LoadStoreUnscaledOffsetFixed | LeftShift(size, 2, 30) | LeftShift(V, 1, 26) | LeftShift(opc, 2, 22) +#define LOAD_STORE_UNSCALED(opname, size, V, opc) OPT(opname, unscaled) = LoadStoreUnscaledOffsetOpSub(size, V, opc) + LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) +#undef LOAD_STORE_UNSCALED +}; + +// Load/store unsigned offset. +enum LoadStoreUnsignedOffset { + LoadStoreUnsignedOffsetFixed = 0x39000000, + LoadStoreUnsignedOffsetFixedMask = 0x3B000000, + LoadStoreUnsignedOffsetMask = 0xFFC00000, + +#define LoadStoreUnsignedOffsetSub(size, V, opc) \ + LoadStoreUnsignedOffsetFixed | LeftShift(size, 2, 30) | LeftShift(V, 1, 26) | LeftShift(opc, 2, 22) +#define LOAD_STORE_UNSIGNED_OFFSET(opname, size, V, opc) \ + OPT(opname, unsigned) = LoadStoreUnsignedOffsetSub(size, V, opc) + LOAD_STORE_OP_LIST(LOAD_STORE_UNSIGNED_OFFSET) +#undef LOAD_STORE_UNSIGNED_OFFSET +}; + +// ===== + +// clang-format off +#define LOAD_STORE_PAIR_OP_LIST(V) \ + V(OP_W(STP), 0b00, 0, 0), \ + V(OP_W(LDP), 0b00, 0, 1), \ + V(OP_S(STP), 0b00, 1, 0), \ + V(OP_S(LDP), 0b00, 1, 1), \ + V(OP(LDPSW), 0b01, 0, 1), \ + V(OP_D(STP), 0b01, 1, 0), \ + V(OP_D(LDP), 0b01, 1, 1), \ + V(OP_X(STP), 0b10, 0, 0), \ + V(OP_X(LDP), 0b10, 0, 1), \ + V(OP_Q(STP), 0b10, 1, 0), \ + V(OP_Q(LDP), 0b10, 1, 1) +// clang-format on + +enum LoadStorePairOp { +#define LoadStorePairOpSub(opc, V, L) LeftShift(opc, 2, 30) | LeftShift(V, 1, 26) | LeftShift(L, 1, 22) +#define LOAD_STORE_PAIR(opname, opc, V, L) OP(opname) = LoadStorePairOpSub(opc, V, L) + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR) +#undef LOAD_STORE_PAIR +}; + +enum LoadStorePairOffsetOp { + LoadStorePairOffsetFixed = 0x29000000, + LoadStorePairOffsetFixedMask = 0x3B800000, + LoadStorePairOffsetMask = 0xFFC00000, + +#define LoadStorePairOffsetOpSub(opc, V, L) \ + LoadStorePairOffsetFixed | LeftShift(opc, 2, 30) | LeftShift(V, 1, 26) | LeftShift(L, 1, 22) +#define LOAD_STORE_PAIR_OFFSET(opname, opc, V, L) OPT(opname, offset) = LoadStorePairOffsetOpSub(opc, V, L) + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_OFFSET) +#undef LOAD_STORE_PAIR_OFFSET +}; + +enum LoadStorePairPostIndexOp { + LoadStorePairPostIndexFixed = 0x28800000, + LoadStorePairPostIndexFixedMask = 0x3B800000, + LoadStorePairPostIndexMask = 0xFFC00000, + +#define LoadStorePairPostOpSub(opc, V, L) \ + LoadStorePairPostIndexFixed | LeftShift(opc, 2, 30) | LeftShift(V, 1, 26) | LeftShift(L, 1, 22) +#define LOAD_STORE_PAIR_POST_INDEX(opname, opc, V, L) OPT(opname, post) = LoadStorePairPostOpSub(opc, V, L) + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_POST_INDEX) +#undef LOAD_STORE_PAIR_POST_INDEX +}; + +enum LoadStorePairPreIndexOp { + LoadStorePairPreIndexFixed = 0x29800000, + LoadStorePairPreIndexFixedMask = 0x3B800000, + LoadStorePairPreIndexMask = 0xFFC00000, + +#define LoadStorePairPreOpSub(opc, V, L) \ + LoadStorePairPreIndexFixed | LeftShift(opc, 2, 30) | LeftShift(V, 1, 26) | LeftShift(L, 1, 22) +#define LOAD_STORE_PAIR_PRE_INDEX(opname, opc, V, L) OPT(opname, pre) = LoadStorePairPreOpSub(opc, V, L) + LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_PRE_INDEX) +#undef LOAD_STORE_PAIR_PRE_INDEX +}; + +// ===== + +// Generic fields. +enum GenericInstrField { SixtyFourBits = 0x80000000, ThirtyTwoBits = 0x00000000, FP32 = 0x00000000, FP64 = 0x00400000 }; + +// Generic utils +// #define sf(rd) (rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits) + +// ===== + +// Move wide immediate. +enum MoveWideImmediateOp { + MoveWideImmediateFixed = 0x12800000, + MoveWideImmediateFixedMask = 0x1F800000, + MoveWideImmediateMask = 0xFF800000, + + OP(MOVN) = 0x00000000, + OP(MOVZ) = 0x40000000, + OP(MOVK) = 0x60000000, + +#define MoveWideImmediateOpSub(sf, opc) MoveWideImmediateFixed | LeftShift(sf, 1, 31) | LeftShift(opc, 2, 29) + OP_W(MOVN) = MoveWideImmediateFixed | MOVN, + OP_X(MOVN) = MoveWideImmediateFixed | MOVN | SixtyFourBits, + OP_W(MOVZ) = MoveWideImmediateFixed | MOVZ, + OP_X(MOVZ) = MoveWideImmediateFixed | MOVZ | SixtyFourBits, + OP_W(MOVK) = MoveWideImmediateFixed | MOVK, + OP_X(MOVK) = MoveWideImmediateFixed | MOVK | SixtyFourBits +}; + +// ===== + +enum AddSubImmediateOp { + AddSubImmediateFixed = 0x11000000, + AddSubImmediateFixedMask = 0x1F000000, + AddSubImmediateMask = 0xFF000000, + +#define AddSubImmediateOpSub(sf, op, S) \ + AddSubImmediateFixed | LeftShift(sf, 1, 31) | LeftShift(op, 1, 30) | LeftShift(S, 1, 29) + OPT_W(ADD, imm) = AddSubImmediateOpSub(0, 0, 0), + OPT_W(ADDS, imm) = AddSubImmediateOpSub(0, 0, 1), + OPT_W(SUB, imm) = AddSubImmediateOpSub(0, 1, 0), + OPT_W(SUBS, imm) = AddSubImmediateOpSub(0, 1, 1), + OPT_X(ADD, imm) = AddSubImmediateOpSub(1, 0, 0), + OPT_X(ADDS, imm) = AddSubImmediateOpSub(1, 0, 1), + OPT_X(SUB, imm) = AddSubImmediateOpSub(1, 1, 0), + OPT_X(SUBS, imm) = AddSubImmediateOpSub(1, 1, 1) +}; + +enum AddSubShiftedOp { + AddSubShiftedFixed = 0x0B000000, + AddSubShiftedFixedMask = 0x1F200000, + AddSubShiftedMask = 0xFF200000, + +#define AddSubShiftedOpSub(sf, op, S) \ + AddSubShiftedFixed | LeftShift(sf, 1, 31) | LeftShift(op, 1, 30) | LeftShift(S, 1, 29) + OPT_W(ADD, shift) = AddSubShiftedOpSub(0, 0, 0), + OPT_W(ADDS, shift) = AddSubShiftedOpSub(0, 0, 1), + OPT_W(SUB, shift) = AddSubShiftedOpSub(0, 1, 0), + OPT_W(SUBS, shift) = AddSubShiftedOpSub(0, 1, 1), + OPT_X(ADD, shift) = AddSubShiftedOpSub(1, 0, 0), + OPT_X(ADDS, shift) = AddSubShiftedOpSub(1, 0, 1), + OPT_X(SUB, shift) = AddSubShiftedOpSub(1, 1, 0), + OPT_X(SUBS, shift) = AddSubShiftedOpSub(1, 1, 1) +}; + +enum AddSubExtendedOp { + AddSubExtendedFixed = 0x0B200000, + AddSubExtendedFixedMask = 0x1F200000, + AddSubExtendedMask = 0xFFE00000, + +#define AddSubExtendedOpSub(sf, op, S) \ + AddSubExtendedFixed | LeftShift(sf, 1, 31) | LeftShift(op, 1, 30) | LeftShift(S, 1, 29) + OPT_W(ADD, extend) = AddSubExtendedOpSub(0, 0, 0), + OPT_W(ADDS, extend) = AddSubExtendedOpSub(0, 0, 1), + OPT_W(SUB, extend) = AddSubExtendedOpSub(0, 1, 0), + OPT_W(SUBS, extend) = AddSubExtendedOpSub(0, 1, 1), + OPT_X(ADD, extend) = AddSubExtendedOpSub(1, 0, 0), + OPT_X(ADDS, extend) = AddSubExtendedOpSub(1, 0, 1), + OPT_X(SUB, extend) = AddSubExtendedOpSub(1, 1, 0), + OPT_X(SUBS, extend) = AddSubExtendedOpSub(1, 1, 1) +}; + +// ===== + +// Logical (immediate and shifted register). +enum LogicalOp { + LogicalOpMask = 0x60200000, + NOT = 0x00200000, + AND = 0x00000000, + BIC = AND | NOT, + ORR = 0x20000000, + ORN = ORR | NOT, + EOR = 0x40000000, + EON = EOR | NOT, + ANDS = 0x60000000, + BICS = ANDS | NOT +}; + +// Logical immediate. +enum LogicalImmediateOp { + LogicalImmediateFixed = 0x12000000, + LogicalImmediateFixedMask = 0x1F800000, + LogicalImmediateMask = 0xFF800000, + +#define W_X_OP(opname, combine_fields) \ + OPT_W(opname, imm) = LogicalImmediateFixed | combine_fields | ThirtyTwoBits, \ + OPT_X(opname, imm) = LogicalImmediateFixed | combine_fields | SixtyFourBits +#define W_X_OP_LIST(V) V(AND, AND), V(ORR, ORR), V(EOR, EOR), V(ANDS, ANDS) +#undef W_X_OP +#undef W_X_OP_LIST +}; + +// Logical shifted register. +enum LogicalShiftedOp { + LogicalShiftedFixed = 0x0A000000, + LogicalShiftedFixedMask = 0x1F000000, + LogicalShiftedMask = 0xFF200000, + +#define W_X_OP(opname, combine_fields) \ + OPT_W(opname, shift) = LogicalShiftedFixed | combine_fields | ThirtyTwoBits, \ + OPT_X(opname, shift) = LogicalShiftedFixed | combine_fields | SixtyFourBits +#define W_X_OP_LIST(V) \ + V(AND, AND), V(BIC, BIC), V(ORR, ORR), V(ORN, ORN), V(EOR, EOR), V(EON, EON), V(ANDS, ANDS), V(BICS, BICS) +#undef W_X_OP +#undef W_X_OP_LIST +}; + +// PC relative addressing. +enum PCRelAddressingOp { + PCRelAddressingFixed = 0x10000000, + PCRelAddressingFixedMask = 0x1F000000, + PCRelAddressingMask = 0x9F000000, + ADR = PCRelAddressingFixed | 0x00000000, + ADRP = PCRelAddressingFixed | 0x80000000 +}; + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/arch/arm64/registers-arm64.h b/app/src/main/cpp/Dobby/source/core/arch/arm64/registers-arm64.h new file mode 100644 index 0000000..84a6d6b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/arm64/registers-arm64.h @@ -0,0 +1,142 @@ +#ifndef ARCH_ARM64_REGISTERS +#define ARCH_ARM64_REGISTERS + +#include "core/arch/arm64/constants-arm64.h" +#include "core/arch/Cpu.h" + +namespace zz { +namespace arm64 { + +class CPURegister : RegisterBase { +public: + enum RegisterType { + kRegister_32, + kRegister_W = kRegister_32, + kRegister_64, + kRegister_X = kRegister_64, + kRegister, + + kVRegister, + kSIMD_FP_Register_8, + kSIMD_FP_Register_B = kSIMD_FP_Register_8, + kSIMD_FP_Register_16, + kSIMD_FP_Register_H = kSIMD_FP_Register_16, + kSIMD_FP_Register_32, + kSIMD_FP_Register_S = kSIMD_FP_Register_32, + kSIMD_FP_Register_64, + kSIMD_FP_Register_D = kSIMD_FP_Register_64, + kSIMD_FP_Register_128, + kSIMD_FP_Register_Q = kSIMD_FP_Register_128, + + kInvalid + }; + + constexpr CPURegister(int code, int size, RegisterType type) : RegisterBase(code), reg_size_(size), reg_type_(type) { + } + + static constexpr CPURegister Create(int code, int size, RegisterType type) { + return CPURegister(code, size, type); + } + + // ===== + + static constexpr CPURegister X(int code) { + return CPURegister(code, 64, kRegister_64); + } + + static constexpr CPURegister W(int code) { + return CPURegister(code, 32, kRegister_32); + } + + static constexpr CPURegister Q(int code) { + return CPURegister(code, 128, kSIMD_FP_Register_128); + } + + static constexpr CPURegister InvalidRegister() { + return CPURegister(0, 0, kInvalid); + } + + // ===== + + bool Is(const CPURegister ®) const { + return (reg.reg_code_ == this->reg_code_); + } + + bool Is64Bits() const { + return reg_size_ == 64; + } + + bool IsRegister() const { + return reg_type_ < kRegister; + } + + bool IsVRegister() const { + return reg_type_ > kVRegister; + } + + // ===== + + RegisterType type() const { + return reg_type_; + } + + int32_t code() const { + return reg_code_; + }; + +private: + RegisterType reg_type_; + int reg_size_; +}; + +typedef CPURegister Register; +typedef CPURegister VRegister; + +// clang-format off +#define GENERAL_REGISTER_CODE_LIST(R) \ + R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ + R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ + R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ + R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) + +#define DEFINE_REGISTER(register_class, name, ...) constexpr register_class name = register_class::Create(__VA_ARGS__) + +#define DEFINE_REGISTERS(N) \ + DEFINE_REGISTER(Register, w##N, N, 32, CPURegister::kRegister_32); \ + DEFINE_REGISTER(Register, x##N, N, 64, CPURegister::kRegister_64); + GENERAL_REGISTER_CODE_LIST(DEFINE_REGISTERS) +#undef DEFINE_REGISTERS + +#define DEFINE_VREGISTERS(N) \ + DEFINE_REGISTER(VRegister, b##N, N, 8, CPURegister::kSIMD_FP_Register_8); \ + DEFINE_REGISTER(VRegister, h##N, N, 16, CPURegister::kSIMD_FP_Register_16); \ + DEFINE_REGISTER(VRegister, s##N, N, 32, CPURegister::kSIMD_FP_Register_32); \ + DEFINE_REGISTER(VRegister, d##N, N, 64, CPURegister::kSIMD_FP_Register_64); \ + DEFINE_REGISTER(VRegister, q##N, N, 128, CPURegister::kSIMD_FP_Register_128); \ +GENERAL_REGISTER_CODE_LIST(DEFINE_VREGISTERS) +#undef DEFINE_VREGISTERS + +#undef DEFINE_REGISTER +// clang-format on + +// ===== + +constexpr Register wzr = w31; +constexpr Register xzr = x31; + +constexpr Register SP = x31; +constexpr Register wSP = w31; +constexpr Register FP = x29; +constexpr Register wFP = w29; +constexpr Register LR = x30; +constexpr Register wLR = w30; + +} // namespace arm64 +} // namespace zz + +#define W(code) CPURegister::W(code) +#define X(code) CPURegister::X(code) +#define Q(code) CPURegister::Q(code) +#define InvalidRegister CPURegister::InvalidRegister() + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/arch/x64/constants-x64.h b/app/src/main/cpp/Dobby/source/core/arch/x64/constants-x64.h new file mode 100644 index 0000000..d72f44f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/x64/constants-x64.h @@ -0,0 +1,21 @@ +#ifndef CORE_ARCH_CONSTANTS_X64_H +#define CORE_ARCH_CONSTANTS_X64_H + +namespace zz { +namespace x64 { + +enum ScaleFactor { + TIMES_1 = 0, + TIMES_2 = 1, + TIMES_4 = 2, + TIMES_8 = 3, + TIMES_16 = 4, + TIMES_HALF_WORD_SIZE = sizeof(void *) / 2 - 1 +}; + +enum RexBits { REX_NONE = 0, REX_B = 1 << 0, REX_X = 1 << 1, REX_R = 1 << 2, REX_W = 1 << 3, REX_PREFIX = 1 << 6 }; + +} // namespace x64 +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/x64/registers-x64.h b/app/src/main/cpp/Dobby/source/core/arch/x64/registers-x64.h new file mode 100644 index 0000000..4c7c612 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/x64/registers-x64.h @@ -0,0 +1,244 @@ +#ifndef ARCH_X64_REGISTERS +#define ARCH_X64_REGISTERS + +#include "core/arch/x64/constants-x64.h" +#include "core/arch/Cpu.h" + +namespace zz { +namespace x64 { + +#define GENERAL_REGISTERS(V) \ + V(rax) \ + V(rcx) \ + V(rdx) \ + V(rbx) \ + V(rsp) \ + V(rbp) \ + V(rsi) \ + V(rdi) \ + V(r8) \ + V(r9) \ + V(r10) \ + V(r11) \ + V(r12) \ + V(r13) \ + V(r14) \ + V(r15) + +#define GENERAL_32_REGISTERS(V) \ + V(eax) \ + V(ecx) \ + V(edx) \ + V(ebx) \ + V(esp) \ + V(ebp) \ + V(esi) \ + V(edi) + +#define GENERAL_16_REGISTERS(V) \ + V(ax) \ + V(cx) \ + V(dx) \ + V(bx) \ + V(sp) \ + V(bp) \ + V(si) \ + V(di) + +#define GENERAL_8H_REGISTERS(V) \ + V(ah) \ + V(ch) \ + V(dh) \ + V(bh) + +#define GENERAL_8L_REGISTERS(V) \ + V(al) \ + V(cl) \ + V(dl) \ + V(bl) + +// clang-format off +enum RegisterCode { +#define REGISTER_CODE(R) kRegCode_##R, + kRegisterCodeStart8L = -1, + GENERAL_8L_REGISTERS(REGISTER_CODE) + kRegisterCodeStart8H = -1, + GENERAL_8H_REGISTERS(REGISTER_CODE) + kRegisterCodeStart16 = -1, + GENERAL_16_REGISTERS(REGISTER_CODE) + kRegisterCodeStart32 = -1, + GENERAL_32_REGISTERS(REGISTER_CODE) + kRegisterCodeStart64 = -1, + GENERAL_REGISTERS(REGISTER_CODE) +#undef REGISTER_CODE + kRegAfterLast +}; +// clang-format on + +class CPURegister : public RegisterBase { +public: + enum RegisterType { kDefault, kInvalid }; + + constexpr CPURegister(int code, int size, RegisterType type) : RegisterBase(code), reg_size_(size), reg_type_(type) { + } + + static constexpr CPURegister Create(int code, int size, RegisterType type) { + return CPURegister(code, size, type); + } + + static constexpr CPURegister from_code(int code) { + return CPURegister(code, 0, kDefault); + } + + static constexpr CPURegister InvalidRegister() { + return CPURegister(0, 0, kInvalid); + } + + bool Is64Bits() const { + return reg_size_ == 64; + } + + RegisterType type() const { + return reg_type_; + } + +public: + bool is_byte_register() const { + return reg_code_ <= 3; + } + + // Return the high bit of the register code as a 0 or 1. Used often + // when constructing the REX prefix byte. + int high_bit() const { + return reg_code_ >> 3; + } + + // Return the 3 low bits of the register code. Used when encoding registers + // in modR/M, SIB, and opcode bytes. + int low_bits() const { + return reg_code_ & 0x7; + } + + int size() { + return reg_size_; + } + +private: + RegisterType reg_type_; + int reg_size_; +}; + +typedef CPURegister Register; + +#define DECLARE_REGISTER(R) constexpr Register R = Register::Create(kRegCode_##R, 64, CPURegister::kDefault); +GENERAL_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER + +#define DECLARE_REGISTER(R) constexpr Register R = Register::Create(kRegCode_##R, 8, CPURegister::kDefault); +GENERAL_8H_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER + +#define DECLARE_REGISTER(R) constexpr Register R = Register::Create(kRegCode_##R, 8, CPURegister::kDefault); +GENERAL_8L_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER + +#define DECLARE_REGISTER(R) constexpr Register R = Register::Create(kRegCode_##R, 16, CPURegister::kDefault); +GENERAL_16_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER + +#define DECLARE_REGISTER(R) constexpr Register R = Register::Create(kRegCode_##R, 32, CPURegister::kDefault); +GENERAL_32_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER + +#ifdef _WIN64 +// Windows calling convention +constexpr Register arg_reg_1 = rcx; +constexpr Register arg_reg_2 = rdx; +constexpr Register arg_reg_3 = r8; +constexpr Register arg_reg_4 = r9; +#else +// AMD64 calling convention +constexpr Register arg_reg_1 = rdi; +constexpr Register arg_reg_2 = rsi; +constexpr Register arg_reg_3 = rdx; +constexpr Register arg_reg_4 = rcx; +#endif // _WIN64 + +#define DOUBLE_REGISTERS(V) \ + V(xmm0) \ + V(xmm1) \ + V(xmm2) \ + V(xmm3) \ + V(xmm4) \ + V(xmm5) \ + V(xmm6) \ + V(xmm7) \ + V(xmm8) \ + V(xmm9) \ + V(xmm10) \ + V(xmm11) \ + V(xmm12) \ + V(xmm13) \ + V(xmm14) \ + V(xmm15) + +#define FLOAT_REGISTERS DOUBLE_REGISTERS +#define SIMD128_REGISTERS DOUBLE_REGISTERS + +constexpr bool kPadArguments = false; +constexpr bool kSimpleFPAliasing = true; +constexpr bool kSimdMaskRegisters = false; + +enum DoubleRegisterCode { +#define REGISTER_CODE(R) kDoubleCode_##R, + DOUBLE_REGISTERS(REGISTER_CODE) +#undef REGISTER_CODE + kDoubleAfterLast +}; + +class XMMRegister : public RegisterBase { +public: + enum RegisterType { kInvalid }; + + constexpr XMMRegister(int code) : RegisterBase(code) { + } + + static constexpr XMMRegister Create(int code) { + return XMMRegister(code); + } + + static constexpr XMMRegister InvalidRegister() { + return XMMRegister(0); + } + +public: + // Return the high bit of the register code as a 0 or 1. Used often + // when constructing the REX prefix byte. + int high_bit() const { + return reg_code_ >> 3; + } + // Return the 3 low bits of the register code. Used when encoding registers + // in modR/M, SIB, and opcode bytes. + int low_bits() const { + return reg_code_ & 0x7; + } + +private: +}; + +typedef XMMRegister FloatRegister; + +typedef XMMRegister DoubleRegister; + +typedef XMMRegister Simd128Register; + +typedef XMMRegister FPURegister; + +#define DECLARE_REGISTER(R) constexpr DoubleRegister R = DoubleRegister::Create(kDoubleCode_##R); +DOUBLE_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER + +} // namespace x64 +} // namespace zz + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/arch/x86/constants-x86.h b/app/src/main/cpp/Dobby/source/core/arch/x86/constants-x86.h new file mode 100644 index 0000000..a243a76 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/x86/constants-x86.h @@ -0,0 +1,19 @@ +#ifndef CORE_ARCH_CONSTANTS_X86_H +#define CORE_ARCH_CONSTANTS_X86_H + +namespace zz { +namespace x86 { + +enum ScaleFactor { + TIMES_1 = 0, + TIMES_2 = 1, + TIMES_4 = 2, + TIMES_8 = 3, + TIMES_16 = 4, + TIMES_HALF_WORD_SIZE = sizeof(void *) / 2 - 1 +}; + +} // namespace x86 +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.cc b/app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.cc new file mode 100644 index 0000000..d700581 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.cc @@ -0,0 +1,10 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) + +#include "cpu-x86.h" + +X86CpuInfo::X86CpuInfo() { + +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.h b/app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.h new file mode 100644 index 0000000..68fd62c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.h @@ -0,0 +1,120 @@ +#ifndef CORE_ARCH_CPU_X86_H +#define CORE_ARCH_CPU_X86_H + +#include "core/arch/Cpu.h" + +class X86CpuInfo { + +public: + X86CpuInfo(); + +public: + // General features + bool has_fpu() const { + return has_fpu_; + } + int icache_line_size() const { + return icache_line_size_; + } + int dcache_line_size() const { + return dcache_line_size_; + } + + static const int UNKNOWN_CACHE_LINE_SIZE = 0; + + // x86 features + bool has_cmov() const { + return has_cmov_; + } + bool has_sahf() const { + return has_sahf_; + } + bool has_mmx() const { + return has_mmx_; + } + bool has_sse() const { + return has_sse_; + } + bool has_sse2() const { + return has_sse2_; + } + bool has_sse3() const { + return has_sse3_; + } + bool has_ssse3() const { + return has_ssse3_; + } + bool has_sse41() const { + return has_sse41_; + } + bool has_sse42() const { + return has_sse42_; + } + bool has_osxsave() const { + return has_osxsave_; + } + bool has_avx() const { + return has_avx_; + } + bool has_fma3() const { + return has_fma3_; + } + bool has_bmi1() const { + return has_bmi1_; + } + bool has_bmi2() const { + return has_bmi2_; + } + bool has_lzcnt() const { + return has_lzcnt_; + } + bool has_popcnt() const { + return has_popcnt_; + } + bool is_atom() const { + return is_atom_; + } + +private: + char vendor_[13]; + + // General features + int icache_line_size_; + int dcache_line_size_; + bool has_fpu_; + + // x86 features + bool has_cmov_; + bool has_sahf_; + bool has_mmx_; + bool has_sse_; + bool has_sse2_; + bool has_sse3_; + bool has_ssse3_; + bool has_sse41_; + bool has_sse42_; + bool has_osxsave_; + bool has_avx_; + bool has_fma3_; + bool has_bmi1_; + bool has_bmi2_; + bool has_lzcnt_; + bool has_popcnt_; + bool is_atom_; +}; + +class X86CpuFeatures : public CpuFeatures { +public: + static bool sse2_supported() { + return X86CpuInfo().has_sse2(); + } + static bool sse4_1_supported() { + return X86CpuInfo().has_sse41(); + } + +private: + static bool sse2_supported_; + static bool sse4_1_supported_; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/arch/x86/registers-x86.h b/app/src/main/cpp/Dobby/source/core/arch/x86/registers-x86.h new file mode 100644 index 0000000..65b06b2 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/x86/registers-x86.h @@ -0,0 +1,124 @@ +#ifndef ARCH_IA32_REGISTERS +#define ARCH_IA32_REGISTERS + +#include "core/arch/x86/constants-x86.h" +#include "core/arch/Cpu.h" + +namespace zz { +namespace x86 { + +#define GENERAL_REGISTERS(V) \ + V(eax) \ + V(ecx) \ + V(edx) \ + V(ebx) \ + V(esp) \ + V(ebp) \ + V(esi) \ + V(edi) + +enum RegisterCode { +#define REGISTER_CODE(R) kRegCode_##R, + GENERAL_REGISTERS(REGISTER_CODE) +#undef REGISTER_CODE + kRegAfterLast +}; + +class CPURegister : public RegisterBase { +public: + enum RegisterType { kDefault, kInvalid }; + + constexpr CPURegister(int code, int size, RegisterType type) : RegisterBase(code), reg_size_(size), reg_type_(type) { + } + + static constexpr CPURegister Create(int code, int size, RegisterType type) { + return CPURegister(code, size, type); + } + + static constexpr CPURegister from_code(int code) { + return CPURegister(code, 0, kDefault); + } + + static constexpr CPURegister InvalidRegister() { + return CPURegister(0, 0, kInvalid); + } + + RegisterType type() const { + return reg_type_; + } + +public: + bool is_byte_register() const { + return reg_code_ <= 3; + } + + int size() { + return reg_size_; + } + +private: + RegisterType reg_type_; + int reg_size_; +}; + +typedef CPURegister Register; + +#define DEFINE_REGISTER(R) constexpr Register R = Register::Create(kRegCode_##R, 32, CPURegister::kDefault); +GENERAL_REGISTERS(DEFINE_REGISTER) +#undef DEFINE_REGISTER + +#define DOUBLE_REGISTERS(V) \ + V(xmm0) \ + V(xmm1) \ + V(xmm2) \ + V(xmm3) \ + V(xmm4) \ + V(xmm5) \ + V(xmm6) \ + V(xmm7) + +#define FLOAT_REGISTERS DOUBLE_REGISTERS +#define SIMD128_REGISTERS DOUBLE_REGISTERS + +constexpr bool kPadArguments = false; +constexpr bool kSimpleFPAliasing = true; +constexpr bool kSimdMaskRegisters = false; + +enum DoubleRegisterCode { +#define REGISTER_CODE(R) kDoubleCode_##R, + DOUBLE_REGISTERS(REGISTER_CODE) +#undef REGISTER_CODE + kDoubleAfterLast +}; + +class XMMRegister : public RegisterBase { +public: + enum RegisterType { kInvalid }; + + constexpr XMMRegister(int code) : RegisterBase(code) { + } + + static constexpr XMMRegister Create(int code) { + return XMMRegister(code); + } + + static constexpr XMMRegister InvalidRegister() { + return XMMRegister(0); + } + +private: +}; + +typedef XMMRegister FloatRegister; +typedef XMMRegister DoubleRegister; +typedef XMMRegister Simd128Register; +typedef XMMRegister FPURegister; + +#define DEFINE_REGISTER(R) constexpr DoubleRegister R = DoubleRegister::Create(kDoubleCode_##R); +DOUBLE_REGISTERS(DEFINE_REGISTER) +#undef DEFINE_REGISTER + +} // namespace x86 +} // namespace zz + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/assembler/AssemblerPseudoLabel.h b/app/src/main/cpp/Dobby/source/core/assembler/AssemblerPseudoLabel.h new file mode 100644 index 0000000..8774774 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/AssemblerPseudoLabel.h @@ -0,0 +1,85 @@ +#pragma once + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +class Label { +public: + Label(addr_t addr) : pos_(addr) { + } + +protected: + addr_t pos_; +}; + +class AssemblerPseudoLabel : public Label { +public: + typedef struct { + int link_type; + size_t pc_offset; + addr_t vmaddr_; + } ref_label_insn_t; + +public: + AssemblerPseudoLabel(addr_t addr) : Label(addr) { + ref_label_insns_.reserve(4); + + bind_to(addr); + } + + bool has_confused_instructions() { + return ref_label_insns_.size(); + } + + void link_confused_instructions(); + + void link_confused_instructions(CodeBufferBase *buffer_); + + void link_to(int link_type, uint32_t pc_offset) { + ref_label_insn_t insn; + insn.link_type = link_type; + insn.pc_offset = pc_offset; + ref_label_insns_.push_back(insn); + } + +public: + addr_t pos() { + return pos_; + }; + + void bind_to(addr_t addr) { + pos_ = addr; + } + +protected: + tinystl::vector ref_label_insns_; +}; + +struct RelocLabel : public AssemblerPseudoLabel { +public: + RelocLabel() : AssemblerPseudoLabel(0) { + memset(data_, 0, sizeof(data_)); + data_size_ = 0; + } + + template static RelocLabel *withData(T value) { + auto label = new RelocLabel(); + label->setData(value); + return label; + } + + template T data() { + return *(T *)data_; + } + + template void setData(T value) { + data_size_ = sizeof(T); + memcpy(data_, &value, data_size_); + } + + template void fixupData(T value) { + *(T *)data_ = value; + } + + uint8_t data_[8]; + int data_size_; +}; \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-arch.h b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arch.h new file mode 100644 index 0000000..23ae9c3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arch.h @@ -0,0 +1,28 @@ +#ifndef CORE_ASSEMBLER_ARCH_H +#define CORE_ASSEMBLER_ARCH_H + +#include "src/assembler.h" + +#if 0 +#if TARGET_ARCH_IA32 +#include "src/ia32/assembler-ia32.h" +#elif TARGET_ARCH_X64 +#include "src/x64/assembler-x64.h" +#elif TARGET_ARCH_ARM64 +#include "src/arm64/assembler-arm64.h" +#elif TARGET_ARCH_ARM +#include "src/arm/assembler-arm.h" +#elif TARGET_ARCH_PPC +#include "src/ppc/assembler-ppc.h" +#elif TARGET_ARCH_MIPS +#include "src/mips/assembler-mips.h" +#elif TARGET_ARCH_MIPS64 +#include "src/mips64/assembler-mips64.h" +#elif TARGET_ARCH_S390 +#include "src/s390/assembler-s390.h" +#else +#error Unknown architecture. +#endif +#endif + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.cc b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.cc new file mode 100644 index 0000000..138db0c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.cc @@ -0,0 +1,41 @@ +#include "platform_detect_macro.h" +#if TARGET_ARCH_ARM + +#include "core/assembler/assembler-arm.h" + +void AssemblerPseudoLabel::link_confused_instructions(CodeBufferBase *buffer) { + CodeBuffer *_buffer = (CodeBuffer *)buffer; + + for (auto &ref_label_insn : ref_label_insns_) { + arm_inst_t inst = _buffer->LoadARMInst(ref_label_insn.pc_offset); + if (ref_label_insn.link_type == kLdrLiteral) { + int64_t pc = ref_label_insn.pc_offset + ARM_PC_OFFSET; + assert(pc % 4 == 0); + int32_t imm12 = pos() - pc; + if (imm12 > 0) { + set_bit(inst, 23, 1); + } else { + set_bit(inst, 23, 0); + imm12 = -imm12; + } + set_bits(inst, 0, 11, imm12); + } + _buffer->RewriteARMInst(ref_label_insn.pc_offset, inst); + } +} + +namespace zz { +namespace arm { + +void Assembler::EmitARMInst(arm_inst_t instr) { + buffer_->EmitARMInst(instr); +} + +void Assembler::EmitAddress(uint32_t value) { + buffer_->Emit32(value); +} + +} // namespace arm +} // namespace zz + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.h b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.h new file mode 100644 index 0000000..46e080a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.h @@ -0,0 +1,354 @@ +#pragma once + +#include "dobby/common.h" + +#include "core/arch/arm/constants-arm.h" +#include "core/arch/arm/registers-arm.h" +#include "core/assembler/assembler.h" + +#include "MemoryAllocator/CodeBuffer/code_buffer_arm.h" + +enum ref_label_type_t { kLdrLiteral }; + +namespace zz { +namespace arm { + +// ARM design had a 3-stage pipeline (fetch-decode-execute) +#define ARM_PC_OFFSET 8 +#define Thumb_PC_OFFSET 4 + +// define instruction length +#define ARM_INST_LEN 4 +#define Thumb1_INST_LEN 2 +#define Thumb2_INST_LEN 4 + +// Thumb instructions address is odd +#define THUMB_ADDRESS_FLAG 1 + +constexpr Register TMP_REG_0 = r12; + +constexpr Register VOLATILE_REGISTER = r12; + +#define Rd(rd) (rd.code() << kRdShift) +#define Rt(rt) (rt.code() << kRtShift) +#define Rn(rn) (rn.code() << kRnShift) +#define Rm(rm) (rm.code() << kRmShift) + +// --- + +class Operand { + friend class OpEncode; + +public: + Operand(int immediate) : imm_(immediate), rm_(no_reg), shift_(LSL), shift_imm_(0), rs_(no_reg) { + } + + Operand(Register rm) : imm_(0), rm_(rm), shift_(LSL), shift_imm_(0), rs_(no_reg) { + } + + Operand(Register rm, Shift shift, uint32_t shift_imm) + : imm_(0), rm_(rm), shift_(shift), shift_imm_(shift_imm), rs_(no_reg) { + } + + Operand(Register rm, Shift shift, Register rs) : imm_(0), rm_(rm), shift_(shift), shift_imm_(0), rs_(rs) { + } + +public: + int GetImmediate() const { + return imm_; + } + +private: + Register rm_; + Register rs_; + + Shift shift_; + int shift_imm_; + + int imm_; + +private: + friend class EncodeUtility; +}; + +// --- + +class MemOperand { + friend class OpEncode; + +public: + MemOperand(Register rn, int32_t offset = 0, AddrMode addrmode = Offset) + : rn_(rn), offset_(offset), rm_(no_reg), shift_(LSL), shift_imm_(0), addrmode_(addrmode) { + } + + MemOperand(Register rn, Register rm, AddrMode addrmode = Offset) + : rn_(rn), offset_(0), rm_(rm), shift_(LSL), shift_imm_(0), addrmode_(addrmode) { + } + + MemOperand(Register rn, Register rm, Shift shift, uint32_t shift_imm, AddrMode addrmode = Offset) + : rn_(rn), offset_(0), rm_(rm), shift_(shift), shift_imm_(shift_imm), addrmode_(addrmode) { + } + + const Register &rn() const { + return rn_; + } + const Register &rm() const { + return rm_; + } + int32_t offset() const { + return offset_; + } + + bool IsImmediateOffset() const { + return (addrmode_ == Offset); + } + bool IsRegisterOffset() const { + return (addrmode_ == Offset); + } + bool IsPreIndex() const { + return addrmode_ == PreIndex; + } + bool IsPostIndex() const { + return addrmode_ == PostIndex; + } + +private: + Register rn_; // base + Register rm_; // register offset + + int32_t offset_; // valid if rm_ == no_reg + + Shift shift_; + uint32_t shift_imm_; // valid if rm_ != no_reg && rs_ == no_reg + + AddrMode addrmode_; // bits P, U, and W +}; + +// --- + +class OpEncode { +public: + static uint32_t MemOperand(const MemOperand operand) { + uint32_t encoding = 0; + if (operand.rm_.IsValid()) { + UNREACHABLE(); + } + + // sign + uint32_t U = 0; + if (operand.offset_ >= 0) { + U = (1 << 23); + } + encoding |= U; + + // offset + encoding |= bits(abs(operand.offset_), 0, 11); + + // addr mode + uint32_t P, W; + if (operand.addrmode_ == Offset) { + P = 1; + W = 0; + } else if (operand.addrmode_ == PostIndex) { + P = 0; + W = 0; + } else if (operand.addrmode_ == PreIndex) { + P = 1; + W = 1; + } + encoding |= ((P << 24) | (W << 21)); + + // rn + encoding |= Rn(operand.rn_); + + return encoding; + } + + static uint32_t Operand(const Operand operand) { + uint32_t encoding = 0; + if (operand.rm_.IsValid()) { + encoding = static_cast(operand.rm_.code()); + } else { + encoding = operand.GetImmediate(); + } + + return encoding; + } +}; + +// --- + +enum ExecuteState { ARMExecuteState, ThumbExecuteState }; + +class Assembler : public AssemblerBase { +private: + ExecuteState execute_state_; + +public: + Assembler(void *address) : AssemblerBase(address) { + execute_state_ = ARMExecuteState; + buffer_ = new CodeBuffer(); + } + + // shared_ptr is better choice + // but we can't use it at kernelspace + Assembler(void *address, CodeBuffer *buffer) : AssemblerBase(address) { + execute_state_ = ARMExecuteState; + buffer_ = buffer; + } + + void ClearCodeBuffer() { + buffer_ = NULL; + } + +public: + void SetExecuteState(ExecuteState state) { + execute_state_ = state; + } + ExecuteState GetExecuteState() { + return execute_state_; + } + + void SetRealizedAddress(void *address) { + DCHECK_EQ(0, reinterpret_cast(address) % 4); + AssemblerBase::SetRealizedAddress(address); + } + + void EmitARMInst(arm_inst_t instr); + + void EmitAddress(uint32_t value); + +public: + void sub(Register rd, Register rn, const Operand &operand) { + uint32_t encoding = B25 | B22; + add_sub(encoding, AL, rd, rn, operand); + } + + void add(Register rd, Register rn, const Operand &operand) { + uint32_t encoding = B25 | B23; + add_sub(encoding, AL, rd, rn, operand); + } + + void add_sub(uint32_t encoding, Condition cond, Register rd, Register rn, const Operand &operand) { + encoding |= (cond << kConditionShift); + + uint32_t imm = operand.GetImmediate(); + encoding |= imm; + + encoding |= Rd(rd); + + encoding |= Rn(rn); + + buffer_->EmitARMInst(encoding); + } + + void ldr(Register rt, const MemOperand &operand) { + uint32_t encoding = B20 | B26; + load_store(encoding, AL, rt, operand); + } + + void str(Register rt, const MemOperand &operand) { + uint32_t encoding = B26; + load_store(encoding, AL, rt, operand); + } + + void load_store(uint32_t encoding, Condition cond, Register rt, const MemOperand &operand) { + encoding |= (cond << kConditionShift); + encoding |= Rt(rt) | OpEncode::MemOperand(operand); + buffer_->EmitARMInst(encoding); + } + + void mov(Register rd, const Operand &operand) { + mov(AL, rd, operand); + } + + void mov(Condition cond, Register rd, const Operand &operand) { + uint32_t encoding = 0x01a00000; + encoding |= (cond << kConditionShift); + encoding |= Rd(rd) | OpEncode::Operand(operand); + buffer_->EmitARMInst(encoding); + } + + // Branch instructions. + void b(int branch_offset) { + b(AL, branch_offset); + } + void b(Condition cond, int branch_offset) { + uint32_t encoding = 0xa000000; + encoding |= (cond << kConditionShift); + uint32_t imm24 = bits(branch_offset >> 2, 0, 23); + encoding |= imm24; + buffer_->EmitARMInst(encoding); + } + + void bl(int branch_offset) { + bl(AL, branch_offset); + } + void bl(Condition cond, int branch_offset) { + uint32_t encoding = 0xb000000; + encoding |= (cond << kConditionShift); + uint32_t imm24 = bits(branch_offset >> 2, 0, 23); + encoding |= imm24; + buffer_->EmitARMInst(encoding); + } + + void blx(int branch_offset) { + UNIMPLEMENTED(); + } + void blx(Register target, Condition cond = AL) { + UNIMPLEMENTED(); + } + void bx(Register target, Condition cond = AL) { + UNIMPLEMENTED(); + } + +}; // namespace arm + +// --- + +class TurboAssembler : public Assembler { +public: + TurboAssembler(void *address) : Assembler(address) { + } + + ~TurboAssembler() { + } + + TurboAssembler(void *address, CodeBuffer *buffer) : Assembler(address, buffer) { + } + + void Ldr(Register rt, AssemblerPseudoLabel *label) { + if (label->pos()) { + int offset = label->pos() - buffer_->GetBufferSize(); + ldr(rt, MemOperand(pc, offset)); + } else { + // record this ldr, and fix later. + label->link_to(kLdrLiteral, buffer_->GetBufferSize()); + ldr(rt, MemOperand(pc, 0)); + } + } + + void CallFunction(ExternalReference function) { + // trick: use bl to replace lr register + bl(0); + b(4); + ldr(pc, MemOperand(pc, -4)); + buffer_->Emit32((uint32_t)(uintptr_t)function.address()); + } + + void Move32Immeidate(Register rd, const Operand &x, Condition cond = AL) { + } + + void RelocLabelFixup(tinystl::unordered_map *relocated_offset_map) { + for (auto *data_label : data_labels_) { + auto val = data_label->data(); + auto iter = relocated_offset_map->find(val); + if (iter != relocated_offset_map->end()) { + data_label->fixupData(iter->second); + } + } + } +}; + +} // namespace arm +} // namespace zz \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.cc b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.cc new file mode 100644 index 0000000..e52fdc6 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.cc @@ -0,0 +1,25 @@ +#include "platform_detect_macro.h" +#if TARGET_ARCH_ARM64 + +#include "core/assembler/assembler-arm64.h" + +void AssemblerPseudoLabel::link_confused_instructions(CodeBufferBase *buffer_) { + auto buffer = (CodeBuffer *)buffer_; + + for (auto &ref_label_insn : ref_label_insns_) { + int64_t fixup_offset = pos() - ref_label_insn.pc_offset; + + arm64_inst_t inst = buffer->LoadInst(ref_label_insn.pc_offset); + arm64_inst_t new_inst = 0; + + if (ref_label_insn.link_type == kLabelImm19) { + new_inst = encode_imm19_offset(inst, fixup_offset); + } + + buffer->RewriteInst(ref_label_insn.pc_offset, new_inst); + } +} + +using namespace zz::arm64; + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.h b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.h new file mode 100644 index 0000000..70c5309 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.h @@ -0,0 +1,560 @@ +#pragma once + +#include "dobby/dobby_internal.h" + +#include "core/arch/arm64/constants-arm64.h" +#include "core/arch/arm64/registers-arm64.h" +#include "core/assembler/assembler.h" + +#include "MemoryAllocator/CodeBuffer/code_buffer_arm64.h" + +#include "InstructionRelocation/arm64/inst_decode_encode_kit.h" + +static inline uint16_t Low16Bits(uint32_t value) { + return static_cast(value & 0xffff); +} + +static inline uint16_t High16Bits(uint32_t value) { + return static_cast(value >> 16); +} + +static inline uint32_t Low32Bits(uint64_t value) { + return static_cast(value); +} + +static inline uint32_t High32Bits(uint64_t value) { + return static_cast(value >> 32); +} + +enum ref_label_type_t { kLabelImm19 }; + +namespace zz { +namespace arm64 { + +constexpr Register TMP_REG_0 = X(ARM64_TMP_REG_NDX_0); + +#define Rd(rd) (rd.code() << kRdShift) +#define Rt(rt) (rt.code() << kRtShift) +#define Rt2(rt) (rt.code() << kRt2Shift) +#define Rn(rn) (rn.code() << kRnShift) +#define Rm(rm) (rm.code() << kRmShift) + +// --- + +class Operand { +public: + inline explicit Operand(int64_t imm) + : immediate_(imm), reg_(InvalidRegister), shift_(NO_SHIFT), extend_(NO_EXTEND), shift_extent_imm_(0) { + } + inline Operand(Register reg, Shift shift = LSL, int32_t shift_imm = 0) + : immediate_(0), reg_(reg), shift_(shift), extend_(NO_EXTEND), shift_extent_imm_(shift_imm) { + } + inline Operand(Register reg, Extend extend, int32_t shift_imm = 0) + : immediate_(0), reg_(reg), shift_(NO_SHIFT), extend_(extend), shift_extent_imm_(shift_imm) { + } + + bool IsImmediate() const { + return reg_.Is(InvalidRegister); + } + bool IsShiftedRegister() const { + return /* reg_.IsValid() && */ (shift_ != NO_SHIFT); + } + bool IsExtendedRegister() const { + return /* reg_.IsValid() && */ (extend_ != NO_EXTEND); + } + + Register reg() const { + DCHECK((IsShiftedRegister() || IsExtendedRegister())); + return reg_; + } + int64_t Immediate() const { + return immediate_; + } + Shift shift() const { + DCHECK(IsShiftedRegister()); + return shift_; + } + Extend extend() const { + DCHECK(IsExtendedRegister()); + return extend_; + } + int32_t shift_extend_imm() const { + return shift_extent_imm_; + } + +private: + int64_t immediate_; + + Register reg_; + + Shift shift_; + Extend extend_; + int32_t shift_extent_imm_; +}; + +// --- + +class MemOperand { +public: + inline explicit MemOperand(Register base, int64_t offset = 0, AddrMode addrmode = Offset) + : base_(base), regoffset_(InvalidRegister), offset_(offset), addrmode_(addrmode), shift_(NO_SHIFT), + extend_(NO_EXTEND), shift_extend_imm_(0) { + } + + inline explicit MemOperand(Register base, Register regoffset, Extend extend, unsigned extend_imm) + : base_(base), regoffset_(regoffset), offset_(0), addrmode_(Offset), shift_(NO_SHIFT), extend_(extend), + shift_extend_imm_(extend_imm) { + } + + inline explicit MemOperand(Register base, Register regoffset, Shift shift = LSL, unsigned shift_imm = 0) + : base_(base), regoffset_(regoffset), offset_(0), addrmode_(Offset), shift_(shift), extend_(NO_EXTEND), + shift_extend_imm_(shift_imm) { + } + + inline explicit MemOperand(Register base, const Operand &offset, AddrMode addrmode = Offset) + : base_(base), regoffset_(InvalidRegister), addrmode_(addrmode) { + if (offset.IsShiftedRegister()) { + regoffset_ = offset.reg(); + shift_ = offset.shift(); + shift_extend_imm_ = offset.shift_extend_imm(); + + extend_ = NO_EXTEND; + offset_ = 0; + } else if (offset.IsExtendedRegister()) { + regoffset_ = offset.reg(); + extend_ = offset.extend(); + shift_extend_imm_ = offset.shift_extend_imm(); + + shift_ = NO_SHIFT; + offset_ = 0; + } + } + + const Register &base() const { + return base_; + } + const Register ®offset() const { + return regoffset_; + } + int64_t offset() const { + return offset_; + } + AddrMode addrmode() const { + return addrmode_; + } + Shift shift() const { + return shift_; + } + Extend extend() const { + return extend_; + } + unsigned shift_extend_imm() const { + return shift_extend_imm_; + } + + bool IsImmediateOffset() const { + return (addrmode_ == Offset); + } + bool IsRegisterOffset() const { + return (addrmode_ == Offset); + } + bool IsPreIndex() const { + return addrmode_ == PreIndex; + } + bool IsPostIndex() const { + return addrmode_ == PostIndex; + } + +private: + Register base_; + Register regoffset_; + + int64_t offset_; + + Shift shift_; + Extend extend_; + uint32_t shift_extend_imm_; + + AddrMode addrmode_; +}; + +// --- + +class OpEncode { +public: + static int32_t sf(const Register ®, int32_t op) { + return (op | sf(reg)); + } + + // register operation size, 32 bits or 64 bits + static int32_t sf(const Register ®) { + if (reg.Is64Bits()) + return LeftShift(1, 1, 31); + return 0; + } + + static int32_t V(const Register ®, int32_t op) { + return (op | V(reg)); + } + + // register type, SIMD_FD register or general register + static int32_t V(const Register ®) { + if (reg.IsVRegister()) + return LeftShift(1, 1, 26); + return 0; + } + + // load or store + static int32_t L(bool load_or_store) { + if (load_or_store) { + return LeftShift(1, 1, 22); + } + return 0; + } + + // shift type + static int32_t shift(Shift shift) { + return LeftShift(shift, 2, 22); + } + + // LogicalImmeidate + static int32_t EncodeLogicalImmediate(const Register &rd, const Register &rn, const Operand &operand) { + int64_t imm = operand.Immediate(); + int32_t N, imms, immr; + immr = bits(imm, 0, 5); + imms = bits(imm, 6, 11); + N = bit(imm, 12); + + return (sf(rd) | LeftShift(immr, 6, 16) | LeftShift(imms, 6, 10) | Rd(rd) | Rn(rn)); + } + + // LogicalShift + static int32_t EncodeLogicalShift(const Register &rd, const Register &rn, const Operand &operand) { + return (sf(rd) | shift(operand.shift()) | Rm(operand.reg()) | LeftShift(operand.shift_extend_imm(), 6, 10) | + Rn(rn) | Rd(rd)); + } + + // LoadStore + static int32_t LoadStorePair(LoadStorePairOp op, CPURegister rt, CPURegister rt2, const MemOperand &addr) { + int32_t scale = 2; + int32_t opc = 0; + int imm7; + opc = bits(op, 30, 31); + if (rt.IsRegister()) { + scale += bit(opc, 1); + } else if (rt.IsVRegister()) { + scale += opc; + } + + imm7 = (int)(addr.offset() >> scale); + return LeftShift(imm7, 7, 15); + } + + // scale + static int32_t scale(int32_t op) { + int scale = 0; + if ((op & LoadStoreUnsignedOffsetFixed) == LoadStoreUnsignedOffsetFixed) { + scale = bits(op, 30, 31); + } + return scale; + } +}; + +// --- + +class Assembler : public AssemblerBase { +public: + Assembler(void *address) : AssemblerBase(address) { + buffer_ = new CodeBuffer(); + } + + ~Assembler() { + if (buffer_) + delete buffer_; + buffer_ = NULL; + } + +public: + void SetRealizedAddress(void *address) { + DCHECK_EQ(0, reinterpret_cast(address) % 4); + AssemblerBase::SetRealizedAddress(address); + } + + void Emit(uint32_t value) { + buffer_->Emit32(value); + } + + void EmitInt64(int64_t value) { + buffer_->Emit64(value); + } + + void bind(Label *label); + + void nop() { + Emit(0xD503201F); + } + + void brk(int code) { + Emit(BRK | LeftShift(code, 16, 5)); + } + + void ret() { + Emit(0xD65F03C0); + } + + void adrp(const Register &rd, int64_t imm) { + DCHECK(rd.Is64Bits()); + DCHECK((abs(imm) >> 12) < (1 << 21)); + + uint32_t immlo = LeftShift(bits(imm >> 12, 0, 1), 2, 29); + uint32_t immhi = LeftShift(bits(imm >> 12, 2, 20), 19, 5); + Emit(ADRP | Rd(rd) | immlo | immhi); + } + + void add(const Register &rd, const Register &rn, int64_t imm) { + if (rd.Is64Bits() && rn.Is64Bits()) + AddSubImmediate(rd, rn, Operand(imm), OPT_X(ADD, imm)); + else + AddSubImmediate(rd, rn, Operand(imm), OPT_W(ADD, imm)); + } + + void adds(const Register &rd, const Register &rn, int64_t imm) { + UNREACHABLE(); + } + void sub(const Register &rd, const Register &rn, int64_t imm) { + if (rd.Is64Bits() && rn.Is64Bits()) + AddSubImmediate(rd, rn, Operand(imm), OPT_X(SUB, imm)); + else + AddSubImmediate(rd, rn, Operand(imm), OPT_W(SUB, imm)); + } + void subs(const Register &rd, const Register &rn, int64_t imm) { + UNREACHABLE(); + } + + void b(int64_t imm) { + int32_t imm26 = bits(imm >> 2, 0, 25); + + Emit(B | imm26); + } + + void b(Label *label) { + int offset = LinkAndGetByteOffsetTo(label); + b(offset); + } + + void br(Register rn) { + Emit(BR | Rn(rn)); + } + + void blr(Register rn) { + Emit(BLR | Rn(rn)); + } + + void ldr(Register rt, int64_t imm) { + LoadRegLiteralOp op; + switch (rt.type()) { + case CPURegister::kRegister_32: + op = OPT_W(LDR, literal); + break; + case CPURegister::kRegister_X: + op = OPT_X(LDR, literal); + break; + case CPURegister::kSIMD_FP_Register_S: + op = OPT_S(LDR, literal); + break; + case CPURegister::kSIMD_FP_Register_D: + op = OPT_D(LDR, literal); + break; + case CPURegister::kSIMD_FP_Register_Q: + op = OPT_Q(LDR, literal); + break; + default: + UNREACHABLE(); + break; + } + EmitLoadRegLiteral(op, rt, imm); + } + + void ldr(const CPURegister &rt, const MemOperand &src) { + LoadStore(OP_X(LDR), rt, src); + } + + void str(const CPURegister &rt, const MemOperand &src) { + LoadStore(OP_X(STR), rt, src); + } + + void ldp(const Register &rt, const Register &rt2, const MemOperand &src) { + if (rt.type() == Register::kSIMD_FP_Register_128) { + LoadStorePair(OP_Q(LDP), rt, rt2, src); + } else if (rt.type() == Register::kRegister_X) { + LoadStorePair(OP_X(LDP), rt, rt2, src); + } else { + UNREACHABLE(); + } + } + + void stp(const Register &rt, const Register &rt2, const MemOperand &dst) { + if (rt.type() == Register::kSIMD_FP_Register_128) { + LoadStorePair(OP_Q(STP), rt, rt2, dst); + } else if (rt.type() == Register::kRegister_X) { + LoadStorePair(OP_X(STP), rt, rt2, dst); + } else { + UNREACHABLE(); + } + } + + void mov(const Register &rd, const Register &rn) { + if ((rd.Is(SP)) || (rn.Is(SP))) { + add(rd, rn, 0); + } else { + if (rd.Is64Bits()) + orr(rd, xzr, Operand(rn)); + else + orr(rd, wzr, Operand(rn)); + } + } + void movk(const Register &rd, uint64_t imm, int shift = -1) { + // Move and keep. + MoveWide(rd, imm, shift, MOVK); + } + void movn(const Register &rd, uint64_t imm, int shift = -1) { + // Move with non-zero. + MoveWide(rd, imm, shift, MOVN); + } + void movz(const Register &rd, uint64_t imm, int shift = -1) { + // Move with zero. + MoveWide(rd, imm, shift, MOVZ); + } + + void orr(const Register &rd, const Register &rn, const Operand &operand) { + Logical(rd, rn, operand, ORR); + } + +private: + // label helpers. + static constexpr int kStartOfLabelLinkChain = 0; + int LinkAndGetByteOffsetTo(Label *label); + + // load helpers. + void EmitLoadRegLiteral(LoadRegLiteralOp op, CPURegister rt, int64_t imm) { + const int32_t encoding = op | LeftShift(imm, 26, 5) | Rt(rt); + Emit(encoding); + } + + void LoadStore(LoadStoreOp op, CPURegister rt, const MemOperand &addr) { + int64_t imm12 = addr.offset(); + if (addr.IsImmediateOffset()) { + // TODO: check Scaled ??? + imm12 = addr.offset() >> OpEncode::scale(LoadStoreUnsignedOffsetFixed | op); + Emit(LoadStoreUnsignedOffsetFixed | op | LeftShift(imm12, 12, 10) | Rn(addr.base()) | Rt(rt)); + } else if (addr.IsRegisterOffset()) { + UNREACHABLE(); + } else { + // pre-index & post-index + UNREACHABLE(); + } + } + + void LoadStorePair(LoadStorePairOp op, CPURegister rt, CPURegister rt2, const MemOperand &addr) { + int32_t combine_fields_op = OpEncode::LoadStorePair(op, rt, rt2, addr) | Rt2(rt2) | Rn(addr.base()) | Rt(rt); + int32_t addrmodeop; + + if (addr.IsImmediateOffset()) { + addrmodeop = LoadStorePairOffsetFixed; + } else { + if (addr.IsPreIndex()) { + addrmodeop = LoadStorePairPreIndexFixed; + } else { + addrmodeop = LoadStorePairPostIndexFixed; + } + } + Emit(op | addrmodeop | combine_fields_op); + } + + void MoveWide(Register rd, uint64_t imm, int shift, MoveWideImmediateOp op) { + if (shift > 0) + shift /= 16; + else + shift = 0; + + int32_t imm16 = LeftShift(imm, 16, 5); + Emit(MoveWideImmediateFixed | op | OpEncode::sf(rd) | LeftShift(shift, 2, 21) | imm16 | Rd(rd)); + } + + void AddSubImmediate(const Register &rd, const Register &rn, const Operand &operand, AddSubImmediateOp op) { + if (operand.IsImmediate()) { + int64_t immediate = operand.Immediate(); + int32_t imm12 = LeftShift(immediate, 12, 10); + Emit(op | Rd(rd) | Rn(rn) | imm12); + } else { + UNREACHABLE(); + } + } + + void Logical(const Register &rd, const Register &rn, const Operand &operand, LogicalOp op) { + if (operand.IsImmediate()) { + LogicalImmediate(rd, rn, operand, op); + } else { + LogicalShift(rd, rn, operand, op); + } + } + void LogicalImmediate(const Register &rd, const Register &rn, const Operand &operand, LogicalOp op) { + int32_t combine_fields_op = OpEncode::EncodeLogicalImmediate(rd, rn, operand); + Emit(op | combine_fields_op); + } + void LogicalShift(const Register &rd, const Register &rn, const Operand &operand, LogicalOp op) { + int32_t combine_fields_op = OpEncode::EncodeLogicalShift(rd, rn, operand); + Emit(op | LogicalShiftedFixed | combine_fields_op); + } +}; + +// --- + +class TurboAssembler : public Assembler { +public: + TurboAssembler(void *address) : Assembler(address) { + } + + ~TurboAssembler() { + } + + void CallFunction(ExternalReference function) { + Mov(TMP_REG_0, (uint64_t)function.address()); + blr(TMP_REG_0); + } + + void Ldr(Register rt, AssemblerPseudoLabel *label) { + if (label->pos()) { + int offset = label->pos() - buffer_->GetBufferSize(); + ldr(rt, offset); + } else { + label->link_to(kLabelImm19, buffer_->GetBufferSize()); + ldr(rt, 0); + } + } + + void Mov(Register rd, uint64_t imm) { + const uint32_t w0 = Low32Bits(imm); + const uint32_t w1 = High32Bits(imm); + const uint16_t h0 = Low16Bits(w0); + const uint16_t h1 = High16Bits(w0); + const uint16_t h2 = Low16Bits(w1); + const uint16_t h3 = High16Bits(w1); + movz(rd, h0, 0); + movk(rd, h1, 16); + movk(rd, h2, 32); + movk(rd, h3, 48); + } + + void AdrpAdd(Register rd, uint64_t from, uint64_t to) { + uint64_t from_PAGE = ALIGN(from, 0x1000); + uint64_t to_PAGE = ALIGN(to, 0x1000); + uint64_t to_PAGEOFF = (uint64_t)to % 0x1000; + + adrp(rd, to_PAGE - from_PAGE); + add(rd, rd, to_PAGEOFF); + } +}; + +} // namespace arm64 +} // namespace zz \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.cc b/app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.cc new file mode 100644 index 0000000..12af8ed --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.cc @@ -0,0 +1,34 @@ +#include "platform_detect_macro.h" +#if TARGET_ARCH_IA32 + +#include "core/assembler/assembler-ia32.h" + +using namespace zz::x86; + +void Assembler::jmp(Immediate imm) { + buffer_->Emit8(0xE9); + buffer_->Emit32((int)imm.value()); +} + +addr32_t TurboAssembler::CurrentIP() { + return pc_offset() + (addr_t)realized_addr_; +} + +void AssemblerPseudoLabel::link_confused_instructions(CodeBufferBase *buffer) { + auto _buffer = (CodeBuffer *)buffer; + + for (auto &ref_label_insn : ref_label_insns_) { + int64_t new_offset = pos() - ref_label_insn.pc_offset; + + if (ref_label_insn.link_type == kDisp32_off_7) { + // why 7 ? + // use `call` and `pop` get the runtime ip register + // but the ip register not the real call next insn + // it need add two insn length == 7 + int disp32_fix_pos = ref_label_insn.pc_offset - sizeof(int32_t); + _buffer->FixBindLabel(disp32_fix_pos, new_offset + 7); + } + } +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.h b/app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.h new file mode 100644 index 0000000..19474a3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.h @@ -0,0 +1,456 @@ +#pragma once + +#include "dobby/common.h" + +#include "core/arch/x86/registers-x86.h" +#include "core/assembler/assembler.h" + +#include "MemoryAllocator/CodeBuffer/code_buffer_x86.h" + +#define IsInt8(imm) (-128 <= imm && imm <= 127) + +enum ref_label_type_t { kDisp32_off_7 }; + +namespace zz { +namespace x86 { + +constexpr Register VOLATILE_REGISTER = eax; + +#define ModRM_Mod(byte) ((byte & 0b11000000) >> 6) +#define ModRM_RegOpcode(byte) ((byte & 0b00111000) >> 3) +#define ModRM_RM(byte) (byte & 0b00000111) + +typedef union _ModRM { + byte_t ModRM; + struct { + byte_t RM : 3; + byte_t RegOpcode : 3; + byte_t Mod : 2; + }; +} ModRM; + +// --- + +class Immediate { +public: + explicit Immediate(int32_t imm) : value_(imm), value_size_(32) { + if ((int32_t)(int8_t)imm == imm) { + value_size_ = 8; + } else if ((int32_t)(int16_t)imm == imm) { + value_size_ = 16; + } else { + value_size_ = 32; + } + } + + explicit Immediate(int32_t imm, int size) : value_(imm), value_size_(size) { + } + + int32_t value() const { + return value_; + } + + int size() const { + return value_size_; + } + +private: + const int32_t value_; + + int value_size_; +}; + +// --- + +class Operand { +public: + // [base] + Operand(Register base); + + // [base + disp/r] + Operand(Register base, int32_t disp); + + // [base + index*scale + disp/r] + Operand(Register base, Register index, ScaleFactor scale, int32_t disp); + + // [index*scale + disp/r] + Operand(Register index, ScaleFactor scale, int32_t disp); + +public: // Getter and Setter + uint8_t modrm() { + return (encoding_at(0)); + } + + uint8_t mod() const { + return (encoding_at(0) >> 6) & 3; + } + + Register rm() const { + return Register::from_code(encoding_at(0) & 7); + } + + ScaleFactor scale() const { + return static_cast((encoding_at(1) >> 6) & 3); + } + + Register index() const { + return Register::from_code((encoding_at(1) >> 3) & 7); + } + + Register base() const { + return Register::from_code(encoding_at(1) & 7); + } + + int8_t disp8() const { + ASSERT(length_ >= 2); + return static_cast(encoding_[length_ - 1]); + } + + int32_t disp32() const { + ASSERT(length_ >= 5); + return static_cast(encoding_[length_ - 4]); + } + +protected: + Operand() : length_(0) { + } + + void SetModRM(int mod, Register rm) { + ASSERT((mod & ~3) == 0); + encoding_[0] = (mod << 6) | rm.code(); + length_ = 1; + } + + void SetSIB(ScaleFactor scale, Register index, Register base) { + ASSERT(length_ == 1); + ASSERT((scale & ~3) == 0); + encoding_[1] = (scale << 6) | (index.code() << 3) | base.code(); + length_ = 2; + } + + void SetDisp8(int8_t disp) { + ASSERT(length_ == 1 || length_ == 2); + encoding_[length_++] = static_cast(disp); + } + + void SetDisp32(int32_t disp) { + ASSERT(length_ == 1 || length_ == 2); + *(int32_t *)&encoding_[length_] = disp; + length_ += sizeof(disp); + } + +private: + // explicit Operand(Register reg) : rex_(REX_NONE) { SetModRM(3, reg); } + + // Get the operand encoding byte at the given index. + uint8_t encoding_at(intptr_t index) const { + ASSERT(index >= 0 && index < length_); + return encoding_[index]; + } + +public: + uint8_t length_; + uint8_t encoding_[6]; +}; + +// --- + +class Address : public Operand { +public: + Address(Register base, int32_t disp) { + int base_ = base.code(); + int ebp_ = ebp.code(); + int esp_ = esp.code(); + if ((disp == 0) && (base_ != ebp_)) { + SetModRM(0, base); + if (base_ == esp_) + SetSIB(TIMES_1, esp, base); + } else if (disp >= -128 && disp <= 127) { + SetModRM(1, base); + if (base_ == esp_) + SetSIB(TIMES_1, esp, base); + SetDisp8(disp); + } else { + SetModRM(2, base); + if (base_ == esp_) + SetSIB(TIMES_1, esp, base); + SetDisp32(disp); + } + } + + // This addressing mode does not exist. + Address(Register base, Register r); + + Address(Register index, ScaleFactor scale, int32_t disp) { + ASSERT(index.code() != rsp.code()); // Illegal addressing mode. + SetModRM(0, esp); + SetSIB(scale, index, ebp); + SetDisp32(disp); + } + + // This addressing mode does not exist. + Address(Register index, ScaleFactor scale, Register r); + + Address(Register base, Register index, ScaleFactor scale, int32_t disp) { + ASSERT(index.code() != rsp.code()); // Illegal addressing mode. + int rbp_ = ebp.code(); + if ((disp == 0) && ((base.code() & 7) != rbp_)) { + SetModRM(0, esp); + SetSIB(scale, index, base); + } else if (disp >= -128 && disp <= 127) { + SetModRM(1, esp); + SetSIB(scale, index, base); + SetDisp8(disp); + } else { + SetModRM(2, esp); + SetSIB(scale, index, base); + SetDisp32(disp); + } + } + + // This addressing mode does not exist. + Address(Register base, Register index, ScaleFactor scale, Register r); + +private: + Address(Register base, int32_t disp, bool fixed) { + ASSERT(fixed); + + SetModRM(2, base); + if (base.code() == esp.code()) { + SetSIB(TIMES_1, esp, base); + } + SetDisp32(disp); + } +}; + +// --- + +class Assembler : public AssemblerBase { +public: + Assembler(void *address) : AssemblerBase(address) { + buffer_ = new CodeBuffer(); + } + ~Assembler() { + if (buffer_) + delete buffer_; + buffer_ = NULL; + } + +public: + void Emit1(byte_t val) { + buffer_->Emit8(val); + } + + void Emit(int32_t value) { + buffer_->Emit32(value); + } + + // --- + + void EmitImmediate(Immediate imm, int imm_size) { + if (imm_size == 8) { + buffer_->Emit8((uint8_t)imm.value()); + } else if (imm_size == 32) { + buffer_->Emit32((uint32_t)imm.value()); + } else { + UNREACHABLE(); + } + } + + // --- + + // ATTENTION: + // ModR/M == 8 registers and 24 addressing mode + + void Emit_OpEn_Register_MemOperand(Register dst, Address &operand) { + EmitModRM_Update_Register(operand.modrm(), dst); + buffer_->EmitBuffer(&operand.encoding_[1], operand.length_ - 1); + } + + void Emit_OpEn_Register_RegOperand(Register dst, Register src) { + EmitModRM_Register_Register(dst, src); + } + + void Emit_OpEn_MemOperand_Immediate(uint8_t extra_opcode, Address &operand, Immediate imm) { + EmitModRM_Update_ExtraOpcode(operand.modrm(), extra_opcode); + buffer_->EmitBuffer(&operand.encoding_[1], operand.length_ - 1); + EmitImmediate(imm, imm.size()); + } + + void Emit_OpEn_RegOperand_Immediate(uint8_t extra_opcode, Register reg, Immediate imm) { + EmitModRM_ExtraOpcode_Register(extra_opcode, reg); + EmitImmediate(imm, imm.size()); + } + + void Emit_OpEn_MemOperand(uint8_t extra_opcode, Address &operand) { + EmitModRM_Update_ExtraOpcode(operand.modrm(), extra_opcode); + buffer_->EmitBuffer(&operand.encoding_[1], operand.length_ - 1); + } + + void Emit_OpEn_RegOperand(uint8_t extra_opcode, Register reg) { + EmitModRM_ExtraOpcode_Register(extra_opcode, reg); + } + + // Encoding: OI + void Emit_OpEn_OpcodeRegister_Immediate(uint8_t opcode, Register dst, Immediate imm) { + EmitOpcode_Register(opcode, dst); + EmitImmediate(imm, imm.size()); + } + + // --- + + inline void EmitModRM(uint8_t Mod, uint8_t RegOpcode, uint8_t RM) { + uint8_t ModRM = 0; + ModRM |= Mod << 6; + ModRM |= RegOpcode << 3; + ModRM |= RM; + Emit1(ModRM); + } + + void EmitModRM_ExtraOpcode_Register(uint8_t extra_opcode, Register reg) { + EmitModRM(0b11, extra_opcode, reg.code()); + } + + void EmitModRM_Register_Register(Register reg1, Register reg2) { + EmitModRM(0b11, reg1.code(), reg2.code()); + } + + // update operand's ModRM + void EmitModRM_Update_Register(uint8_t modRM, Register reg) { + EmitModRM(ModRM_Mod(modRM), reg.code(), ModRM_RM(modRM)); + } + + // update operand's ModRM + void EmitModRM_Update_ExtraOpcode(uint8_t modRM, uint8_t extra_opcode) { + EmitModRM(ModRM_Mod(modRM), extra_opcode, ModRM_RM(modRM)); + } + + // --- + + void EmitOpcode(uint8_t opcode) { + Emit1(opcode); + } + + void EmitOpcode_Register(uint8_t opcode, Register reg) { + EmitOpcode(opcode | reg.code()); + } + + // --- + + void pushfq() { + Emit1(0x9C); + } + + void jmp(Immediate imm); + + void sub(Register dst, Immediate imm) { + DCHECK_EQ(dst.size(), 32); + + EmitOpcode(0x81); + + Emit_OpEn_RegOperand_Immediate(0x5, dst, imm); + } + + void add(Register dst, Immediate imm) { + DCHECK_EQ(dst.size(), 32); + + EmitOpcode(0x81); + + Emit_OpEn_RegOperand_Immediate(0x0, dst, imm); + } + + // MOV RAX, 0x320 + // 48 c7 c0 20 03 00 00 (MI encoding) + // 48 b8 20 03 00 00 00 00 00 00 (OI encoding) + void mov(Register dst, const Immediate imm) { + Emit_OpEn_OpcodeRegister_Immediate(0xb8, dst, imm); + } + + void mov(Address dst, const Immediate imm) { + EmitOpcode(0xc7); + + Emit_OpEn_MemOperand_Immediate(0x0, dst, imm); + } + + void mov(Register dst, Address src) { + EmitOpcode(0x8B); + + Emit_OpEn_Register_MemOperand(dst, src); + } + + void mov(Address dst, Register src) { + EmitOpcode(0x89); + + Emit_OpEn_Register_MemOperand(src, dst); + } + + void mov(Register dst, Register src) { + Emit1(0x8B); + + Emit_OpEn_Register_RegOperand(dst, src); + } + + void call(Address operand) { + EmitOpcode(0xFF); + + Emit_OpEn_MemOperand(0x2, operand); + } + + void call(Immediate imm) { + EmitOpcode(0xe8); + + EmitImmediate(imm, imm.size()); + } + + void call(Register reg) { + EmitOpcode(0xFF); + + Emit_OpEn_RegOperand(0x2, reg); + } + + void pop(Register reg) { + EmitOpcode_Register(0x58, reg); + } + + void push(Register reg) { + EmitOpcode_Register(0x50, reg); + } + + void ret() { + EmitOpcode(0xc3); + } + void nop() { + EmitOpcode(0x90); + } +}; + +class TurboAssembler : public Assembler { +public: + TurboAssembler(void *address) : Assembler(address) { + } + + ~TurboAssembler() { + } + + addr32_t CurrentIP(); + + void CallFunction(ExternalReference function) { + nop(); + MovRipToRegister(VOLATILE_REGISTER); + call(Address(VOLATILE_REGISTER, INT32_MAX)); + { + auto label = RelocLabel::withData(function.address()); + label->link_to(kDisp32_off_7, ip_offset()); + AppendRelocLabel(label); + } + nop(); + } + + void MovRipToRegister(Register dst) { + call(Immediate(0, 32)); + pop(dst); + } +}; + +} // namespace x86 +} // namespace zz \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.cc b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.cc new file mode 100644 index 0000000..8441a11 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.cc @@ -0,0 +1,34 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_X64) + +#include "core/assembler/assembler-x64.h" + +using namespace zz::x64; + +void AssemblerPseudoLabel::link_confused_instructions(CodeBufferBase *buffer) { + CodeBuffer *_buffer = (CodeBuffer *)buffer; + + for (auto &ref_label_insn : ref_label_insns_) { + int64_t new_offset = pos() - ref_label_insn.pc_offset; + + if (ref_label_insn.link_type == kDisp32_off_9) { + // why 9 ? + // use `call` and `pop` get the runtime ip register + // but the ip register not the real call next insn + // it need add two insn length == 9 + int disp32_fix_pos = ref_label_insn.pc_offset - sizeof(int32_t); + _buffer->FixBindLabel(disp32_fix_pos, new_offset + 9); + } + } +} + +void Assembler::jmp(Immediate imm) { + buffer_->Emit8(0xE9); + buffer_->Emit32((int)imm.value()); +} + +addr64_t TurboAssembler::CurrentIP() { + return pc_offset() + (addr_t)realized_addr_; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.h b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.h new file mode 100644 index 0000000..f94d94d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.h @@ -0,0 +1,596 @@ +#pragma once + +#include "dobby/common.h" + +#include "core/arch/x64/registers-x64.h" +#include "core/assembler/assembler.h" + +#include "MemoryAllocator/CodeBuffer/code_buffer_x64.h" + +#define IsInt8(imm) (-128 <= imm && imm <= 127) + +enum ref_label_type_t { kDisp32_off_9 }; + +namespace zz { +namespace x64 { + +constexpr Register VOLATILE_REGISTER = r11; + +#define ModRM_Mod(byte) ((byte & 0b11000000) >> 6) +#define ModRM_RegOpcode(byte) ((byte & 0b00111000) >> 3) +#define ModRM_RM(byte) (byte & 0b00000111) + +typedef union _ModRM { + byte_t ModRM; + struct { + byte_t RM : 3; + byte_t RegOpcode : 3; + byte_t Mod : 2; + }; +} ModRM; + +// --- + +class Immediate { +public: + explicit Immediate(int64_t imm) : value_(imm), value_size_(64) { + if ((int64_t)(int8_t)imm == imm) { + value_size_ = 8; + } else if ((int64_t)(int16_t)imm == imm) { + value_size_ = 8; + } else if ((int64_t)(int32_t)imm == imm) { + value_size_ = 32; + } else { + value_size_ = 64; + } + } + + explicit Immediate(int64_t imm, int size) : value_(imm), value_size_(size) { + } + + int64_t value() const { + return value_; + } + + int size() const { + return value_size_; + } + +private: + const int64_t value_; + + int value_size_; +}; + +// --- + +class Operand { +public: + // [base] + Operand(Register base); + + // [base + disp/r] + Operand(Register base, int32_t disp); + + // [base + index*scale + disp/r] + Operand(Register base, Register index, ScaleFactor scale, int32_t disp); + + // [index*scale + disp/r] + Operand(Register index, ScaleFactor scale, int32_t disp); + +public: // Getter and Setter + uint8_t rex() const { + return rex_; + } + + inline uint8_t rex_b() const { + return (rex_ & REX_B); + } + + inline uint8_t rex_x() const { + return (rex_ & REX_X); + } + + inline uint8_t rex_r() const { + return (rex_ & REX_R); + } + + inline uint8_t rex_w() const { + return (rex_ & REX_W); + } + + uint8_t modrm() { + return (encoding_at(0)); + } + + uint8_t mod() const { + return (encoding_at(0) >> 6) & 3; + } + + Register rm() const { + int rm_rex = rex_b() << 3; + return Register::from_code(rm_rex + (encoding_at(0) & 7)); + } + + ScaleFactor scale() const { + return static_cast((encoding_at(1) >> 6) & 3); + } + + Register index() const { + int index_rex = rex_x() << 2; + return Register::from_code(index_rex + ((encoding_at(1) >> 3) & 7)); + } + + Register base() const { + int base_rex = rex_b() << 3; + return Register::from_code(base_rex + (encoding_at(1) & 7)); + } + + int8_t disp8() const { + ASSERT(length_ >= 2); + return static_cast(encoding_[length_ - 1]); + } + + int32_t disp32() const { + ASSERT(length_ >= 5); + return static_cast(encoding_[length_ - 4]); + } + +protected: + Operand() : length_(0), rex_(REX_NONE) { + } // Needed by subclass Address. + + void SetModRM(int mod, Register rm) { + ASSERT((mod & ~3) == 0); + + if ((rm.code() > 7) && !((rm.Is(r12)) && (mod != 3))) { + rex_ |= REX_B; + } + encoding_[0] = (mod << 6) | (rm.code() & 7); + length_ = 1; + } + + void SetSIB(ScaleFactor scale, Register index, Register base) { + ASSERT(length_ == 1); + ASSERT((scale & ~3) == 0); + + if (base.code() > 7) { + ASSERT((rex_ & REX_B) == 0); // Must not have REX.B already set. + rex_ |= REX_B; + } + if (index.code() > 7) + rex_ |= REX_X; + + encoding_[1] = (scale << 6) | ((index.code() & 7) << 3) | (base.code() & 7); + length_ = 2; + } + + void SetDisp8(int8_t disp) { + ASSERT(length_ == 1 || length_ == 2); + + encoding_[length_++] = static_cast(disp); + } + + void SetDisp32(int32_t disp) { + ASSERT(length_ == 1 || length_ == 2); + + *(int32_t *)&encoding_[length_] = disp; + length_ += sizeof(disp); + } + +private: + // explicit Operand(Register reg) : rex_(REX_NONE) { SetModRM(3, reg); } + + // Get the operand encoding byte at the given index. + uint8_t encoding_at(intptr_t index) const { + ASSERT(index >= 0 && index < length_); + return encoding_[index]; + } + +public: + uint8_t length_; + uint8_t rex_; + uint8_t encoding_[6]; +}; + +// --- + +class Address : public Operand { +public: + Address(Register base, int32_t disp) { + int base_ = base.code(); + int rbp_ = rbp.code(); + int rsp_ = rsp.code(); + if ((disp == 0) && ((base_ & 7) != rbp_)) { + SetModRM(0, base); + if ((base_ & 7) == rsp_) { + SetSIB(TIMES_1, rsp, base); + } + } else if (IsInt8(disp)) { + SetModRM(1, base); + if ((base_ & 7) == rsp_) { + SetSIB(TIMES_1, rsp, base); + } + SetDisp8(disp); + } else { + SetModRM(2, base); + if ((base_ & 7) == rsp_) { + SetSIB(TIMES_1, rsp, base); + } + SetDisp32(disp); + } + } + + // This addressing mode does not exist. + Address(Register base, Register r); + + Address(Register index, ScaleFactor scale, int32_t disp) { + ASSERT(index.code() != rsp.code()); // Illegal addressing mode. + SetModRM(0, rsp); + SetSIB(scale, index, rbp); + SetDisp32(disp); + } + + // This addressing mode does not exist. + Address(Register index, ScaleFactor scale, Register r); + + Address(Register base, Register index, ScaleFactor scale, int32_t disp) { + ASSERT(index.code() != rsp.code()); // Illegal addressing mode. + int rbp_ = rbp.code(); + if ((disp == 0) && ((base.code() & 7) != rbp_)) { + SetModRM(0, rsp); + SetSIB(scale, index, base); + } else if (IsInt8(disp)) { + SetModRM(1, rsp); + SetSIB(scale, index, base); + SetDisp8(disp); + } else { + SetModRM(2, rsp); + SetSIB(scale, index, base); + SetDisp32(disp); + } + } + + // This addressing mode does not exist. + Address(Register base, Register index, ScaleFactor scale, Register r); + +private: + Address(Register base, int32_t disp, bool fixed) { + ASSERT(fixed); + + SetModRM(2, base); + if ((base.code() & 7) == rsp.code()) { + SetSIB(TIMES_1, rsp, base); + } + SetDisp32(disp); + } +}; + +// --- + +class Assembler : public AssemblerBase { +public: + Assembler(void *address) : AssemblerBase(address) { + buffer_ = new CodeBuffer(); + } + ~Assembler() { + if (buffer_) + delete buffer_; + buffer_ = NULL; + } + +public: + void Emit1(byte_t val) { + buffer_->Emit8(val); + } + + void Emit(int32_t value) { + buffer_->Emit32(value); + } + + void EmitInt64(int64_t value) { + buffer_->Emit64(value); + } + + // --- + + // refer android_art + uint8_t EmitOptionalRex(bool force, bool w, bool r, bool x, bool b) { + // REX.WRXB + // W - 64-bit operand + // R - MODRM.reg + // X - SIB.index + // B - MODRM.rm/SIB.base + + uint8_t rex = force ? 0x40 : 0; + if (w) { + rex |= 0x48; // REX.W000 + } + if (r) { + rex |= 0x44; // REX.0R00 + } + if (x) { + rex |= 0x42; // REX.00X0 + } + if (b) { + rex |= 0x41; // REX.000B + } + if (rex != 0) { + return rex; + } + return 0; + } + + void Emit_64REX(uint8_t extra) { + uint8_t rex = EmitOptionalRex(false, true, false, false, false); + rex |= extra; + if (rex) + Emit1(rex); + } + + void EmitREX_ExtraRegister(Register reg) { + uint8_t rex = EmitOptionalRex(false, reg.size() == 64, reg.code() > 7, false, reg.code() > 7); + if (rex) + Emit1(rex); + } + + void EmitREX_Register(Register reg) { + uint8_t rex = EmitOptionalRex(false, reg.size() == 64, reg.code() > 7, false, false); + if (rex) + Emit1(rex); + } + + void EmitREX_Register_Operand(Register reg, Operand &operand) { + if (reg.size() != 64) + UNIMPLEMENTED(); + uint8_t rex = operand.rex(); + rex |= EmitOptionalRex(true, reg.size() == 64, reg.code() > 7, false, false); + if (rex != 0) { + Emit1(rex); + } + } + + void EmitREX_Operand(Operand &operand) { + uint8_t rex = operand.rex(); + rex |= REX_PREFIX; + if (rex != 0) { + Emit1(rex); + } + } + + // --- + + void EmitImmediate(Immediate imm, int imm_size) { + if (imm_size == 8) { + buffer_->Emit8((uint8_t)imm.value()); + } else if (imm_size == 32) { + buffer_->Emit32((uint32_t)imm.value()); + } else if (imm_size == 64) { + buffer_->Emit64((uint64_t)imm.value()); + } else { + UNREACHABLE(); + } + } + + // --- + + // ATTENTION: + // ModR/M == 8 registers and 24 addressing mode + + void Emit_OpEn_Register_MemOperand(Register dst, Address &operand) { + EmitModRM_Update_Register(operand.modrm(), dst); + buffer_->EmitBuffer(&operand.encoding_[1], operand.length_ - 1); + } + + void Emit_OpEn_Register_RegOperand(Register dst, Register src) { + EmitModRM_Register_Register(dst, src); + } + + void Emit_OpEn_MemOperand_Immediate(uint8_t extra_opcode, Address &operand, Immediate imm) { + EmitModRM_Update_ExtraOpcode(operand.modrm(), extra_opcode); + buffer_->EmitBuffer(&operand.encoding_[1], operand.length_ - 1); + EmitImmediate(imm, imm.size()); + } + + void Emit_OpEn_RegOperand_Immediate(uint8_t extra_opcode, Register reg, Immediate imm) { + EmitModRM_ExtraOpcode_Register(extra_opcode, reg); + EmitImmediate(imm, imm.size()); + } + + void Emit_OpEn_MemOperand(uint8_t extra_opcode, Address &operand) { + EmitModRM_Update_ExtraOpcode(operand.modrm(), extra_opcode); + buffer_->EmitBuffer(&operand.encoding_[1], operand.length_ - 1); + } + + void Emit_OpEn_RegOperand(uint8_t extra_opcode, Register reg) { + EmitModRM_ExtraOpcode_Register(extra_opcode, reg); + } + + // Encoding: OI + void Emit_OpEn_OpcodeRegister_Immediate(uint8_t opcode, Register dst, Immediate imm) { + EmitOpcode_Register(opcode, dst); + EmitImmediate(imm, imm.size()); + } + + // --- + + inline void EmitModRM(uint8_t Mod, uint8_t RegOpcode, uint8_t RM) { + uint8_t ModRM = 0; + ModRM |= Mod << 6; + ModRM |= RegOpcode << 3; + ModRM |= RM; + Emit1(ModRM); + } + + void EmitModRM_ExtraOpcode_Register(uint8_t extra_opcode, Register reg) { + EmitModRM(0b11, extra_opcode, reg.code()); + } + + void EmitModRM_Register_Register(Register reg1, Register reg2) { + EmitModRM(0b11, reg1.code(), reg2.code()); + } + + // update operand's ModRM + void EmitModRM_Update_Register(uint8_t modRM, Register reg) { + EmitModRM(ModRM_Mod(modRM), reg.low_bits(), ModRM_RM(modRM)); + } + + // update operand's ModRM + void EmitModRM_Update_ExtraOpcode(uint8_t modRM, uint8_t extra_opcode) { + EmitModRM(ModRM_Mod(modRM), extra_opcode, ModRM_RM(modRM)); + } + + // --- + + void EmitOpcode(uint8_t opcode) { + Emit1(opcode); + } + + void EmitOpcode_Register(uint8_t opcode, Register reg) { + EmitOpcode(opcode | reg.low_bits()); + } + + // --- + + void pushfq() { + Emit1(0x9C); + } + + void jmp(Immediate imm); + + void sub(Register dst, Immediate imm) { + EmitREX_Register(dst); + + EmitOpcode(0x81); + + Emit_OpEn_RegOperand_Immediate(0x5, dst, imm); + } + + void add(Register dst, Immediate imm) { + EmitREX_Register(dst); + + EmitOpcode(0x81); + + Emit_OpEn_RegOperand_Immediate(0x0, dst, imm); + } + + // MOV RAX, 0x320 + // 48 c7 c0 20 03 00 00 (MI encoding) + // 48 b8 20 03 00 00 00 00 00 00 (OI encoding) + void mov(Register dst, const Immediate imm) { + EmitREX_Register(dst); + + Emit_OpEn_OpcodeRegister_Immediate(0xb8, dst, imm); + } + + void mov(Address dst, const Immediate imm) { + EmitREX_Operand(dst); + + EmitOpcode(0xc7); + + Emit_OpEn_MemOperand_Immediate(0x0, dst, imm); + } + + void mov(Register dst, Address src) { + EmitREX_Register(dst); + + EmitOpcode(0x8B); + + Emit_OpEn_Register_MemOperand(dst, src); + } + + void mov(Address dst, Register src) { + EmitREX_Register_Operand(src, dst); + + EmitOpcode(0x89); + + Emit_OpEn_Register_MemOperand(src, dst); + } + + void mov(Register dst, Register src) { + EmitREX_Register(dst); + + Emit1(0x8B); + + Emit_OpEn_Register_RegOperand(dst, src); + } + + void call(Address operand) { + EmitREX_Operand(operand); + + EmitOpcode(0xFF); + + Emit_OpEn_MemOperand(0x2, operand); + } + + void call(Immediate imm) { + EmitOpcode(0xe8); + + EmitImmediate(imm, imm.size()); + } + + void call(Register reg) { + EmitREX_Register(reg); + + EmitOpcode(0xFF); + + Emit_OpEn_RegOperand(0x2, reg); + } + + void pop(Register reg) { + EmitREX_ExtraRegister(reg); + + EmitOpcode_Register(0x58, reg); + } + + void push(Register reg) { + EmitREX_ExtraRegister(reg); + + EmitOpcode_Register(0x50, reg); + } + + void ret() { + EmitOpcode(0xc3); + } + void nop() { + EmitOpcode(0x90); + } +}; + +// --- + +class TurboAssembler : public Assembler { +public: + TurboAssembler(void *address) : Assembler(address) { + } + + ~TurboAssembler() { + } + + addr64_t CurrentIP(); + + void CallFunction(ExternalReference function) { +#if 0 + mov(r11, Immediate((int64_t)function.address(), 64)); + call(r11); +#endif + + nop(); + MovRipToRegister(VOLATILE_REGISTER); + call(Address(VOLATILE_REGISTER, INT32_MAX)); + { + auto label = RelocLabel::withData((uint64_t)function.address()); + label->link_to(kDisp32_off_9, ip_offset()); + this->AppendRelocLabel(label); + } + nop(); + } + + void MovRipToRegister(Register dst) { + call(Immediate(0, 32)); + pop(dst); + } +}; + +} // namespace x64 +} // namespace zz diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.cc b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.cc new file mode 100644 index 0000000..e81e063 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.cc @@ -0,0 +1,17 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_IA32) + +#include "core/assembler/assembler-x86-shared.h" + +using namespace zz::x86shared; + +void Assembler::jmp(Immediate imm) { + buffer_->Emit8(0xE9); + buffer_->Emit32((int)imm.value()); +} + +uint64_t TurboAssembler::CurrentIP() { + return pc_offset() + (addr_t)realized_addr_; +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.h b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.h new file mode 100644 index 0000000..2dcd87c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.h @@ -0,0 +1,707 @@ +#pragma once + +#include "dobby/common.h" + +#include "core/arch/x64/registers-x64.h" +#include "core/assembler/assembler.h" + +#include "MemoryAllocator/CodeBuffer/code_buffer_x64.h" + +#include "xnucxx/LiteMutableArray.h" +#include "xnucxx/LiteIterator.h" + +#define IsInt8(imm) (-128 <= imm && imm <= 127) + +namespace zz { +namespace x86shared { + +using namespace x64; + +constexpr Register VOLATILE_REGISTER = r11; + +// ================================================================ +// AssemblerPseudoLabel + +class AssemblerPseudoLabel : public Label { +public: + enum PseudoLabelType { kDisp32_off_9 }; + + typedef struct _PseudoLabelInstruction { + int position_; + PseudoLabelType type_; + } PseudoLabelInstruction; + +public: + AssemblerPseudoLabel(void) { + instructions_.initWithCapacity(8); + } + ~AssemblerPseudoLabel(void) { + for (size_t i = 0; i < instructions_.getCount(); i++) { + PseudoLabelInstruction *item = (PseudoLabelInstruction *)instructions_.getObject(i); + delete item; + } + + instructions_.release(); + } + + bool has_confused_instructions() { + return instructions_.getCount() > 0; + } + + void link_confused_instructions(CodeBuffer *buffer = nullptr) { + if (!buffer) + UNREACHABLE(); + CodeBuffer *_buffer = buffer; + + for (size_t i = 0; i < instructions_.getCount(); i++) { + PseudoLabelInstruction *instruction = (PseudoLabelInstruction *)instructions_.getObject(i); + + int32_t offset = pos() - instruction->position_; + + switch (instruction->type_) { + case kDisp32_off_9: { + int disp32_fix_pos = instruction->position_ - sizeof(int32_t); + _buffer->FixBindLabel(disp32_fix_pos, offset + 9); + } break; + default: + UNREACHABLE(); + break; + } + } + }; + + void link_to(int pos, PseudoLabelType type) { + PseudoLabelInstruction *instruction = new PseudoLabelInstruction; + instruction->position_ = pos; + instruction->type_ = type; + instructions_.pushObject((LiteObject *)instruction); + } + +private: + LiteMutableArray instructions_; +}; + +class RelocLabel : public AssemblerPseudoLabel { +public: + explicit RelocLabel(uint64_t data) : data_size_(0) { + data_ = data; + } + + uint64_t data() { + return data_; + } + +private: + uint64_t data_; + + int data_size_; +}; + +#define ModRM_Mod(byte) ((byte & 0b11000000) >> 6) +#define ModRM_RegOpcode(byte) ((byte & 0b00111000) >> 3) +#define ModRM_RM(byte) (byte & 0b00000111) + +typedef union _ModRM { + byte_t ModRM; + struct { + byte_t RM : 3; + byte_t RegOpcode : 3; + byte_t Mod : 2; + }; +} ModRM; + +// ================================================================ +// Immediate + +class Immediate { +public: + explicit Immediate(int64_t imm) : value_(imm), value_size_(64) { + if ((int64_t)(int8_t)imm == imm) { + value_size_ = 8; + } else if ((int64_t)(int16_t)imm == imm) { + value_size_ = 8; + } else if ((int64_t)(int32_t)imm == imm) { + value_size_ = 32; + } else { + value_size_ = 64; + } + } + + explicit Immediate(int64_t imm, int size) : value_(imm), value_size_(size) { + } + + int64_t value() const { + return value_; + } + + int size() const { + return value_size_; + } + +private: + const int64_t value_; + + int value_size_; +}; + +// ================================================================ +// Operand + +class Operand { +public: + // [base] + Operand(Register base); + + // [base + disp/r] + Operand(Register base, int32_t disp); + + // [base + index*scale + disp/r] + Operand(Register base, Register index, ScaleFactor scale, int32_t disp); + + // [index*scale + disp/r] + Operand(Register index, ScaleFactor scale, int32_t disp); + +public: // Getter and Setter + uint8_t rex() const { + return rex_; + } + + inline uint8_t rex_b() const { + return (rex_ & REX_B); + } + + inline uint8_t rex_x() const { + return (rex_ & REX_X); + } + + inline uint8_t rex_r() const { + return (rex_ & REX_R); + } + + inline uint8_t rex_w() const { + return (rex_ & REX_W); + } + + uint8_t modrm() { + return (encoding_at(0)); + } + + uint8_t mod() const { + return (encoding_at(0) >> 6) & 3; + } + + Register rm() const { + int rm_rex = rex_b() << 3; + return Register::from_code(rm_rex + (encoding_at(0) & 7)); + } + + ScaleFactor scale() const { + return static_cast((encoding_at(1) >> 6) & 3); + } + + Register index() const { + int index_rex = rex_x() << 2; + return Register::from_code(index_rex + ((encoding_at(1) >> 3) & 7)); + } + + Register base() const { + int base_rex = rex_b() << 3; + return Register::from_code(base_rex + (encoding_at(1) & 7)); + } + + int8_t disp8() const { + ASSERT(length_ >= 2); + return static_cast(encoding_[length_ - 1]); + } + + int32_t disp32() const { + ASSERT(length_ >= 5); + return static_cast(encoding_[length_ - 4]); + } + +protected: + Operand() : length_(0), rex_(REX_NONE) { + } // Needed by subclass Address. + + void SetModRM(int mod, Register rm) { + ASSERT((mod & ~3) == 0); + + if ((rm.code() > 7) && !((rm.Is(r12)) && (mod != 3))) { + rex_ |= REX_B; + } + encoding_[0] = (mod << 6) | (rm.code() & 7); + length_ = 1; + } + + void SetSIB(ScaleFactor scale, Register index, Register base) { + ASSERT(length_ == 1); + ASSERT((scale & ~3) == 0); + + if (base.code() > 7) { + ASSERT((rex_ & REX_B) == 0); // Must not have REX.B already set. + rex_ |= REX_B; + } + if (index.code() > 7) + rex_ |= REX_X; + encoding_[1] = (scale << 6) | ((index.code() & 7) << 3) | (base.code() & 7); + length_ = 2; + } + + void SetDisp8(int8_t disp) { + ASSERT(length_ == 1 || length_ == 2); + + encoding_[length_++] = static_cast(disp); + } + + void SetDisp32(int32_t disp) { + ASSERT(length_ == 1 || length_ == 2); + + *(int32_t *)&encoding_[length_] = disp; + length_ += sizeof(disp); + } + +private: + // explicit Operand(Register reg) : rex_(REX_NONE) { SetModRM(3, reg); } + + // Get the operand encoding byte at the given index. + uint8_t encoding_at(intptr_t index) const { + ASSERT(index >= 0 && index < length_); + return encoding_[index]; + } + +public: + uint8_t length_; + uint8_t rex_; + uint8_t encoding_[6]; +}; + +// ================================================================ +// Address + +class Address : public Operand { +public: + Address(Register base, int32_t disp) { + int base_ = base.code(); + int rbp_ = rbp.code(); + int rsp_ = rsp.code(); + if ((disp == 0) && ((base_ & 7) != rbp_)) { + SetModRM(0, base); + if ((base_ & 7) == rsp_) { + SetSIB(TIMES_1, rsp, base); + } + } else if (IsInt8(disp)) { + SetModRM(1, base); + if ((base_ & 7) == rsp_) { + SetSIB(TIMES_1, rsp, base); + } + SetDisp8(disp); + } else { + SetModRM(2, base); + if ((base_ & 7) == rsp_) { + SetSIB(TIMES_1, rsp, base); + } + SetDisp32(disp); + } + } + + // This addressing mode does not exist. + Address(Register base, Register r); + + Address(Register index, ScaleFactor scale, int32_t disp) { + ASSERT(index.code() != rsp.code()); // Illegal addressing mode. + SetModRM(0, rsp); + SetSIB(scale, index, rbp); + SetDisp32(disp); + } + + // This addressing mode does not exist. + Address(Register index, ScaleFactor scale, Register r); + + Address(Register base, Register index, ScaleFactor scale, int32_t disp) { + ASSERT(index.code() != rsp.code()); // Illegal addressing mode. + int rbp_ = rbp.code(); + if ((disp == 0) && ((base.code() & 7) != rbp_)) { + SetModRM(0, rsp); + SetSIB(scale, index, base); + } else if (IsInt8(disp)) { + SetModRM(1, rsp); + SetSIB(scale, index, base); + SetDisp8(disp); + } else { + SetModRM(2, rsp); + SetSIB(scale, index, base); + SetDisp32(disp); + } + } + + // This addressing mode does not exist. + Address(Register base, Register index, ScaleFactor scale, Register r); + +private: + Address(Register base, int32_t disp, bool fixed) { + ASSERT(fixed); + SetModRM(2, base); + if ((base.code() & 7) == rsp.code()) { + SetSIB(TIMES_1, rsp, base); + } + SetDisp32(disp); + } +}; + +// ================================================================ +// Assembler + +class Assembler : public AssemblerBase { +public: + Assembler(void *address, int mode) : AssemblerBase(address) : mode_(mode) { + buffer_ = new CodeBuffer(); + } + ~Assembler() { + if (buffer_) + delete buffer_; + buffer_ = NULL + } + +public: + void Emit1(byte_t val) { + buffer_->Emit8(val); + } + + void Emit(int32_t value) { + buffer_->Emit32(value); + } + + void EmitInt64(int64_t value) { + buffer_->Emit64(value); + } + + void EmitAddr(uint64_t addr) { + if (mode == 64) { + EmitInt64(int64_t)addr); + } else { + EmitI((int32_t)addr); + } + } + + // ================================================================ + // REX + + // refer android_art + uint8_t EmitOptionalRex(bool force, bool w, bool r, bool x, bool b) { + // REX.WRXB + // W - 64-bit operand + // R - MODRM.reg + // X - SIB.index + // B - MODRM.rm/SIB.base + + uint8_t rex = force ? 0x40 : 0; + if (w) { + rex |= 0x48; // REX.W000 + } + if (r) { + rex |= 0x44; // REX.0R00 + } + if (x) { + rex |= 0x42; // REX.00X0 + } + if (b) { + rex |= 0x41; // REX.000B + } + if (rex != 0) { + return rex; + } + return 0; + } + + void Emit_64REX(uint8_t extra) { + uint8_t rex = EmitOptionalRex(false, true, false, false, false); + rex |= extra; + if (rex) + Emit1(rex); + } + + void EmitREX_ExtraRegister(Register reg) { + uint8_t rex = EmitOptionalRex(false, reg.size() == 64, reg.code() > 7, false, reg.code() > 7); + if (rex) + Emit1(rex); + } + + void EmitREX_Register(Register reg) { + uint8_t rex = EmitOptionalRex(false, reg.size() == 64, reg.code() > 7, false, false); + if (rex) + Emit1(rex); + } + + void EmitREX_Register_Operand(Register reg, Operand &operand) { + if (reg.size() != 64) + UNIMPLEMENTED(); + uint8_t rex = operand.rex(); + rex |= EmitOptionalRex(true, reg.size() == 64, reg.code() > 7, false, false); + if (rex != 0) { + Emit1(rex); + } + } + + void EmitREX_Operand(Operand &operand) { + uint8_t rex = operand.rex(); + rex |= REX_PREFIX; + if (rex != 0) { + Emit1(rex); + } + } + + // ================================================================ + // Immediate + + void EmitImmediate(Immediate imm, int imm_size) { + if (imm_size == 8) { + buffer_->Emit8((uint8_t)imm.value()); + } else if (imm_size == 32) { + buffer_->Emit32((uint32_t)imm.value()); + } else if (imm_size == 64) { + buffer_->Emit64((uint64_t)imm.value()); + } else { + UNREACHABLE(); + } + } + + // ================================================================ + // Operand Encoding + + // ATTENTION: + // ModR/M == 8 registers and 24 addressing mode + + // RM or MR + void Emit_OpEn_Register_MemOperand(Register dst, Address &operand) { + EmitModRM_Update_Register(operand.modrm(), dst); + buffer_->EmitBuffer(&operand.encoding_[1], operand.length_ - 1); + } + void Emit_OpEn_Register_RegOperand(Register dst, Register src) { + EmitModRM_Register_Register(dst, src); + } + + void Emit_OpEn_MemOperand_Immediate(uint8_t extra_opcode, Address &operand, Immediate imm) { + } + void Emit_OpEn_RegOperand_Immediate(uint8_t extra_opcode, Register reg, Immediate imm) { + EmitModRM_ExtraOpcode_Register(extra_opcode, reg); + EmitImmediate(imm, imm.size()); + } + + void Emit_OpEn_MemOperand(uint8_t extra_opcode, Address &operand) { + EmitModRM_Update_ExtraOpcode(operand.modrm(), extra_opcode); + buffer_->EmitBuffer(&operand.encoding_[1], operand.length_ - 1); + } + void Emit_OpEn_RegOperand(uint8_t extra_opcode, Register reg) { + EmitModRM_ExtraOpcode_Register(extra_opcode, reg); + } + + // Encoding: OI + void Emit_OpEn_OpcodeRegister_Immediate(uint8_t opcode, Register dst, Immediate imm) { + EmitOpcode_Register(opcode, dst); + EmitImmediate(imm, imm.size()); + } + + // ================================================================ + // ModRM + + inline void EmitModRM(uint8_t Mod, uint8_t RegOpcode, uint8_t RM) { + uint8_t ModRM = 0; + ModRM |= Mod << 6; + ModRM |= RegOpcode << 3; + ModRM |= RM; + Emit1(ModRM); + } + + void EmitModRM_ExtraOpcode_Register(uint8_t extra_opcode, Register reg) { + EmitModRM(0b11, extra_opcode, reg.code()); + } + + void EmitModRM_Register_Register(Register reg1, Register reg2) { + EmitModRM(0b11, reg1.code(), reg2.code()); + } + + // update operand's ModRM + void EmitModRM_Update_Register(uint8_t modRM, Register reg) { + EmitModRM(ModRM_Mod(modRM), reg.low_bits(), ModRM_RM(modRM)); + } + + // update operand's ModRM + void EmitModRM_Update_ExtraOpcode(uint8_t modRM, uint8_t extra_opcode) { + EmitModRM(ModRM_Mod(modRM), extra_opcode, ModRM_RM(modRM)); + } + + // ================================================================ + // Opcode + void EmitOpcode(uint8_t opcode) { + Emit1(opcode); + } + + void EmitOpcode_Register(uint8_t opcode, Register reg) { + EmitOpcode(opcode | reg.low_bits()); + } + + // ================================================================ + // Instruction + + void pushfq() { + Emit1(0x9C); + } + + void jmp(Immediate imm); + + void sub(Register dst, Immediate imm) { + EmitREX_Register(dst); + EmitOpcode(0x81); + Emit_OpEn_RegOperand_Immediate(0x5, dst, imm); + } + + void add(Register dst, Immediate imm) { + EmitREX_Register(dst); + EmitOpcode(0x81); + Emit_OpEn_RegOperand_Immediate(0x0, dst, imm); + } + + // MOV RAX, 0x320 + // 48 c7 c0 20 03 00 00 (MI encoding) + // 48 b8 20 03 00 00 00 00 00 00 (OI encoding) + void mov(Register dst, const Immediate imm) { + EmitREX_Register(dst); + + // OI encoding + Emit_OpEn_OpcodeRegister_Immediate(0xb8, dst, imm); + } + + void mov(Register dst, Address src) { + EmitREX_Register(dst); + + EmitOpcode(0x8B); + + Emit_OpEn_Register_MemOperand(dst, src); + } + + void mov(Address dst, Register src) { + EmitREX_Register_Operand(src, dst); + EmitOpcode(0x89); + Emit_OpEn_Register_MemOperand(src, dst); + } + + void mov(Register dst, Register src) { + EmitREX_Register(dst); + + Emit1(0x8B); + + Emit_OpEn_Register_RegOperand(dst, src); + } + + void call(Address operand) { + EmitREX_Operand(operand); + + EmitOpcode(0xFF); + + Emit_OpEn_MemOperand(0x2, operand); + } + + void call(Immediate imm) { + EmitOpcode(0xe8); + EmitImmediate(imm, imm.size()); + } + + void call(Register reg) { + EmitREX_Register(reg); + EmitOpcode(0xFF); + Emit_OpEn_RegOperand(0x2, reg); + } + + void pop(Register reg) { + EmitREX_ExtraRegister(reg); + EmitOpcode_Register(0x58, reg); + } + + void push(Register reg) { + EmitREX_ExtraRegister(reg); + EmitOpcode_Register(0x50, reg); + } + + void ret() { + EmitOpcode(0xc3); + } + void nop() { + EmitOpcode(0x90); + } + +private: + int mode_; +}; + +// ================================================================ +// TurboAssembler + +class TurboAssembler : public Assembler { +public: + TurboAssembler(void *address, int mode) : Assembler(address, mode) { + data_labels_ = NULL; + } + + addr64_t CurrentIP(); + + void CallFunction(ExternalReference function) { +#if 0 + mov(r11, Immediate((int64_t)function.address(), 64)); + call(r11); +#endif + + nop(); + MovRipToRegister(VOLATILE_REGISTER); + call(Address(VOLATILE_REGISTER, INT32_MAX)); + { + RelocLabel *addrLabel = new RelocLabel((uint64_t)function.address()); + addrLabel->link_to(ip_offset(), AssemblerPseudoLabel::kDisp32_off_9); + this->AppendRelocLabel(addrLabel); + } + nop(); + } + + void MovRipToRegister(Register dst) { + call(Immediate(0, 32)); + pop(dst); + } + + // ================================================================ + // RelocLabel + + void PseudoBind(AssemblerPseudoLabel *label) { + const addr_t bound_pc = buffer_->GetBufferSize(); + label->bind_to(bound_pc); + // If some instructions have been wrote, before the label bound, we need link these `confused` instructions + if (label->has_confused_instructions()) { + label->link_confused_instructions(reinterpret_cast(this->GetCodeBuffer())); + } + } + + void RelocBind() { + if (data_labels_ == NULL) + return; + for (size_t i = 0; i < data_labels_->getCount(); i++) { + RelocLabel *label = (RelocLabel *)data_labels_->getObject(i); + PseudoBind(label); + EmitAddr(label->data()); + } + } + + void AppendRelocLabel(RelocLabel *label) { + if (data_labels_ == NULL) { + data_labels_ = new LiteMutableArray(8); + } + data_labels_->pushObject((LiteObject *)label); + } + + LiteMutableArray *GetLabels() { + return data_labels_; + } + +private: + LiteMutableArray *data_labels_; +}; + +} // namespace x86shared +} // namespace zz \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler.cc b/app/src/main/cpp/Dobby/source/core/assembler/assembler.cc new file mode 100644 index 0000000..30192bf --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler.cc @@ -0,0 +1,64 @@ +#include "core/assembler/assembler.h" +#include "logging/logging.h" + +namespace zz { + +const void *ExternalReference::address() { + return address_; +} + +AssemblerBase::AssemblerBase(void *address) { + realized_addr_ = address; + buffer_ = nullptr; +} + +AssemblerBase::~AssemblerBase() { + buffer_ = nullptr; +} + +size_t AssemblerBase::ip_offset() const { + return reinterpret_cast(buffer_)->GetBufferSize(); +} + +size_t AssemblerBase::pc_offset() const { + return reinterpret_cast(buffer_)->GetBufferSize(); +} + +CodeBuffer *AssemblerBase::GetCodeBuffer() { + return buffer_; +} + +void AssemblerBase::PseudoBind(AssemblerPseudoLabel *label) { + auto pc_offset = reinterpret_cast(buffer_)->GetBufferSize(); + label->bind_to(pc_offset); + if (label->has_confused_instructions()) { + label->link_confused_instructions(reinterpret_cast(buffer_)); + } +} + +void AssemblerBase::RelocBind() { + for (auto *data_label : data_labels_) { + PseudoBind(data_label); + reinterpret_cast(buffer_)->EmitBuffer(data_label->data_, data_label->data_size_); + } +} + +void AssemblerBase::AppendRelocLabel(RelocLabel *label) { + data_labels_.push_back(label); +} + +void AssemblerBase::SetRealizedAddress(void *address) { + realized_addr_ = address; +} + +void *AssemblerBase::GetRealizedAddress() { + return realized_addr_; +} + +void AssemblerBase::FlushICache(addr_t start, int size) { +} + +void AssemblerBase::FlushICache(addr_t start, addr_t end) { +} + +} // namespace zz diff --git a/app/src/main/cpp/Dobby/source/core/assembler/assembler.h b/app/src/main/cpp/Dobby/source/core/assembler/assembler.h new file mode 100644 index 0000000..e3e22df --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler.h @@ -0,0 +1,74 @@ +#pragma once + +#include "MemoryAllocator/CodeBuffer/CodeBufferBase.h" + +#include "AssemblerPseudoLabel.h" + +class CodeBuffer; + +namespace zz { + +class ExternalReference { +public: + explicit ExternalReference(void *address) : address_(address) { +#if defined(__APPLE__) && __arm64e__ + address_ = pac_strip((void *)address_); +#endif + } + + const void *address(); + +private: + const void *address_; +}; + +class AssemblerBase { +public: + explicit AssemblerBase(void *address); + + ~AssemblerBase(); + + size_t ip_offset() const; + + size_t pc_offset() const; + + CodeBuffer *GetCodeBuffer(); + + void PseudoBind(AssemblerPseudoLabel *label); + + void RelocBind(); + + void AppendRelocLabel(RelocLabel *label); + +protected: + tinystl::vector data_labels_; + +public: + virtual void *GetRealizedAddress(); + + virtual void SetRealizedAddress(void *address); + + static void FlushICache(addr_t start, int size); + + static void FlushICache(addr_t start, addr_t end); + +protected: + CodeBuffer *buffer_; + + void *realized_addr_; +}; + +} // namespace zz + +#if 0 +#include "globals.h" +#if TARGET_ARCH_ARM +#include "core/assembler/assembler-arm.h" +#elif TARGET_ARCH_ARM64 +#include "core/assembler/assembler-arm64.h" +#elif TARGET_ARCH_X64 +#include "core/assembler/assembler-x64.h" +#else +#error "unsupported architecture" +#endif +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.cc b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.cc new file mode 100644 index 0000000..926d901 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.cc @@ -0,0 +1,19 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM) + +#include "core/codegen/codegen-arm.h" + +namespace zz { +namespace arm { + +void CodeGen::LiteralLdrBranch(uint32_t address) { + TurboAssembler *turbo_assembler_ = reinterpret_cast(this->assembler_); +#define _ turbo_assembler_-> + _ ldr(pc, MemOperand(pc, -4)); + turbo_assembler_->GetCodeBuffer()->Emit32((addr_t)address); +} + +} // namespace arm +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.h b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.h new file mode 100644 index 0000000..9f87202 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.h @@ -0,0 +1,22 @@ +#ifndef CORE_CODEGEN_ARM_H +#define CORE_CODEGEN_ARM_H + +#include "core/codegen/codegen.h" +#include "core/assembler/assembler.h" +#include "core/assembler/assembler-arm.h" + +namespace zz { +namespace arm { + +class CodeGen : public CodeGenBase { +public: + CodeGen(TurboAssembler *turbo_assembler) : CodeGenBase(turbo_assembler) { + } + + void LiteralLdrBranch(uint32_t address); +}; + +} // namespace arm +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.cc b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.cc new file mode 100644 index 0000000..8e82718 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.cc @@ -0,0 +1,26 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "dobby/dobby_internal.h" +#include "core/codegen/codegen-arm64.h" + +namespace zz { +namespace arm64 { + +void CodeGen::LiteralLdrBranch(uint64_t address) { + auto turbo_assembler_ = reinterpret_cast(this->assembler_); +#define _ turbo_assembler_-> + + auto label = RelocLabel::withData(address); + turbo_assembler_->AppendRelocLabel(label); + + _ Ldr(TMP_REG_0, label); + _ br(TMP_REG_0); + +#undef _ +} + +} // namespace arm64 +} // namespace zz + +#endif diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.h b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.h new file mode 100644 index 0000000..99dafb3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.h @@ -0,0 +1,21 @@ +#ifndef CORE_CODEGEN_ARM64_H +#define CORE_CODEGEN_ARM64_H + +#include "core/codegen/codegen.h" +#include "core/assembler/assembler.h" +#include "core/assembler/assembler-arm64.h" + +namespace zz { +namespace arm64 { + +class CodeGen : public CodeGenBase { +public: + CodeGen(TurboAssembler *turbo_assembler) : CodeGenBase(turbo_assembler) { + } + void LiteralLdrBranch(uint64_t address); +}; + +} // namespace arm64 +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.cc b/app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.cc new file mode 100644 index 0000000..821137f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.cc @@ -0,0 +1,23 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "core/codegen/codegen-ia32.h" + +namespace zz { +namespace x86 { + +void CodeGen::JmpNear(uint32_t address) { + TurboAssembler *turbo_assembler_ = reinterpret_cast(this->assembler_); +#define _ turbo_assembler_-> +#define __ turbo_assembler_->GetCodeBuffer()-> + uint32_t currIP = turbo_assembler_->CurrentIP() + 5; + int32_t offset = (int32_t)(address - currIP); + + __ Emit8(0xe9); + __ Emit32(offset); +} + +} // namespace x86 +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.h b/app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.h new file mode 100644 index 0000000..5ad42bc --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.h @@ -0,0 +1,22 @@ +#ifndef CORE_CODEGEN_X86_H +#define CORE_CODEGEN_X86_H + +#include "core/codegen/codegen.h" +#include "core/assembler/assembler.h" +#include "core/assembler/assembler-ia32.h" + +namespace zz { +namespace x86 { + +class CodeGen : public CodeGenBase { +public: + CodeGen(TurboAssembler *turbo_assembler) : CodeGenBase(turbo_assembler) { + } + + void JmpNear(uint32_t address); +}; + +} // namespace x86 +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.cc b/app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.cc new file mode 100644 index 0000000..ad6d660 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.cc @@ -0,0 +1,25 @@ +#include "platform_detect_macro.h" +#if defined(TARGET_ARCH_X64) + +#include "core/codegen/codegen-x64.h" + +namespace zz { +namespace x64 { + +void CodeGen::JmpNearIndirect(addr_t forward_stub_addr) { + TurboAssembler *turbo_assembler_ = reinterpret_cast(this->assembler_); +#define _ turbo_assembler_-> +#define __ turbo_assembler_->GetCodeBuffer()-> + uint64_t currIP = turbo_assembler_->CurrentIP() + 6; + int32_t offset = (int32_t)(forward_stub_addr - currIP); + + // jmp *(rip + disp32) + __ Emit8(0xFF); + __ Emit8(0x25); // ModR/M: 00 100 101 + __ Emit32(offset); +} + +} // namespace x64 +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.h b/app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.h new file mode 100644 index 0000000..5fbbc1f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.h @@ -0,0 +1,22 @@ +#ifndef CORE_CODEGEN_X64_H +#define CORE_CODEGEN_X64_H + +#include "core/codegen/codegen.h" +#include "core/assembler/assembler.h" +#include "core/assembler/assembler-x64.h" + +namespace zz { +namespace x64 { + +class CodeGen : public CodeGenBase { +public: + CodeGen(TurboAssembler *turbo_assembler) : CodeGenBase(turbo_assembler) { + } + + void JmpNearIndirect(addr_t forward_stub_addr); +}; + +} // namespace x64 +} // namespace zz + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/codegen/codegen.h b/app/src/main/cpp/Dobby/source/core/codegen/codegen.h new file mode 100644 index 0000000..e75d193 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen.h @@ -0,0 +1,17 @@ +#ifndef CORE_CODEGEN_H +#define CORE_CODEGEN_H + +#include "core/assembler/assembler.h" + +using namespace zz; + +class CodeGenBase { +public: + CodeGenBase(AssemblerBase *assembler) : assembler_(assembler) { + } + +protected: + AssemblerBase *assembler_; +}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/core/emulator/dummy.cc b/app/src/main/cpp/Dobby/source/core/emulator/dummy.cc new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/cpp/Dobby/source/dobby.cpp b/app/src/main/cpp/Dobby/source/dobby.cpp new file mode 100644 index 0000000..18b847b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby.cpp @@ -0,0 +1,31 @@ +#include "dobby/dobby_internal.h" +#include "Interceptor.h" + +__attribute__((constructor)) static void ctor() { + DEBUG_LOG("================================"); + DEBUG_LOG("Dobby"); + DEBUG_LOG("dobby in debug log mode, disable with cmake flag \"-DDOBBY_DEBUG=OFF\""); + DEBUG_LOG("================================"); +} + +PUBLIC const char *DobbyGetVersion() { + return ""; +} + +PUBLIC int DobbyDestroy(void *address) { +#if defined(TARGET_ARCH_ARM) + if ((addr_t)address % 2) { + address = (void *)((addr_t)address - 1); + } +#endif + auto entry = Interceptor::SharedInstance()->find((addr_t)address); + if (entry) { + uint8_t *buffer = entry->origin_insns; + uint32_t buffer_size = entry->origin_insn_size; + DobbyCodePatch(address, buffer, buffer_size); + Interceptor::SharedInstance()->remove((addr_t)address); + return 0; + } + + return -1; +} diff --git a/app/src/main/cpp/Dobby/source/dobby/common.h b/app/src/main/cpp/Dobby/source/dobby/common.h new file mode 100644 index 0000000..94d897b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/common.h @@ -0,0 +1,11 @@ +#pragma once + +#include "dobby.h" +#include "dobby/types.h" +#include "dobby/platform_features.h" +#include "dobby/platform_detect_macro.h" +#include "dobby/utility_macro.h" +#include "dobby/pac_kit.h" + +#include "logging/logging.h" +#include "logging/check_logging.h" \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/dobby/dobby_internal.h b/app/src/main/cpp/Dobby/source/dobby/dobby_internal.h new file mode 100644 index 0000000..121d650 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/dobby_internal.h @@ -0,0 +1,13 @@ +#pragma once + +#include "dobby/common.h" + +#include "PlatformUnifiedInterface/platform.h" + +#include "PlatformUnifiedInterface/MemoryAllocator.h" +#include "PlatformUnifiedInterface/ExecMemory/CodePatchTool.h" +#include "PlatformUnifiedInterface/ExecMemory/ClearCacheTool.h" + +#include "MemoryAllocator/AssemblyCodeBuilder.h" + +#include "InterceptRouting/InterceptRouting.h" \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/dobby/kernel_mode_header.h b/app/src/main/cpp/Dobby/source/dobby/kernel_mode_header.h new file mode 100644 index 0000000..f962e3e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/kernel_mode_header.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/app/src/main/cpp/Dobby/source/dobby/pac_kit.h b/app/src/main/cpp/Dobby/source/dobby/pac_kit.h new file mode 100644 index 0000000..12bf097 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/pac_kit.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#ifndef PAC_KIT +#define PAC_KIT +#if defined(__arm64e__) && __has_feature(ptrauth_calls) +#include +#endif + +static inline void *pac_strip(void *addr) { + if (addr == NULL) { + return NULL; + } +#if __has_feature(ptrauth_calls) + addr = ptrauth_strip(addr, ptrauth_key_asia); +#endif + return addr; +} + +static inline void *pac_sign(void *addr) { + if (addr == NULL) { + return NULL; + } +#if __has_feature(ptrauth_calls) + addr = ptrauth_sign_unauthenticated((void *)addr, ptrauth_key_asia, 0); +#endif + return addr; +} + +static inline void *pac_strip_and_sign(void *addr) { + return pac_sign(pac_strip(addr)); +} + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/dobby/platform_detect_macro.h b/app/src/main/cpp/Dobby/source/dobby/platform_detect_macro.h new file mode 100644 index 0000000..300c759 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/platform_detect_macro.h @@ -0,0 +1,15 @@ +#pragma once + +#if !defined(DISABLE_ARCH_DETECT) +#if defined(__arm__) +#define TARGET_ARCH_ARM 1 +#elif defined(__arm64__) || defined(__aarch64__) +#define TARGET_ARCH_ARM64 1 +#elif defined(_M_IX86) || defined(__i386__) +#define TARGET_ARCH_IA32 1 +#elif defined(_M_X64) || defined(__x86_64__) +#define TARGET_ARCH_X64 1 +#else +#error Target architecture was not detected as supported by Dobby +#endif +#endif diff --git a/app/src/main/cpp/Dobby/source/dobby/platform_features.h b/app/src/main/cpp/Dobby/source/dobby/platform_features.h new file mode 100644 index 0000000..fb9076a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/platform_features.h @@ -0,0 +1,50 @@ +#pragma once + +#if defined(__APPLE__) && __arm64e__ +#if __has_feature(ptrauth_calls) +#include +#endif +#endif + +#if defined(BUILDING_KERNEL) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__linux__) || defined(__APPLE__) +#include +#include +#endif +#endif + +#if defined(BUILDING_KERNEL) +#include "kernel_mode_header.h" +#endif + +#if defined(BUILDING_KERNEL) +#define abs(a) ((a) < 0 ? -(a) : (a)) +#define llabs(a) (((long long)a) < 0 ? -((long long)a) : ((long long)a)) +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#ifdef __cplusplus +#define abs(a) ((a) < 0 ? -(a) : (a)) +#endif +#else +#endif + +#ifdef __cplusplus +#include "TINYSTL/vector.h" +#include "TINYSTL/unordered_map.h" +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/dobby/types.h b/app/src/main/cpp/Dobby/source/dobby/types.h new file mode 100644 index 0000000..f67546e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/types.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +typedef unsigned char byte_t; +typedef unsigned int uint; + +#ifndef NULL +#define NULL 0 +#endif diff --git a/app/src/main/cpp/Dobby/source/dobby/utility_macro.h b/app/src/main/cpp/Dobby/source/dobby/utility_macro.h new file mode 100644 index 0000000..a6c567c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/utility_macro.h @@ -0,0 +1,62 @@ +#pragma once + +// offset of struct member +#define OFFSETOF(TYPE, ELEMENT) ((size_t) & (((TYPE *)0)->ELEMENT)) + +// assert +#define ASSERT(X) + +// left/right shift +#define LeftShift(a, b, c) ((a & ((1 << b) - 1)) << c) +#define RightShift(a, b, c) ((a >> c) & ((1 << b) - 1)) + +// align +#ifndef ALIGN +#define ALIGN ALIGN_FLOOR +#endif +#define ALIGN_FLOOR(address, range) ((uintptr_t)address & ~((uintptr_t)range - 1)) +#define ALIGN_CEIL(address, range) (((uintptr_t)address + (uintptr_t)range - 1) & ~((uintptr_t)range - 1)) + +// borrow from gdb, refer: binutils-gdb/gdb/arch/arm.h +#define submask(x) ((1L << ((x) + 1)) - 1) +#define bits(obj, st, fn) (((obj) >> (st)) & submask((fn) - (st))) +#define bit(obj, st) (((obj) >> (st)) & 1) +#define sbits(obj, st, fn) ((long)(bits(obj, st, fn) | ((long)bit(obj, fn) * ~submask(fn - st)))) + +// make it easy +#define set_bit(obj, st, bit) obj = (((~(1 << st)) & obj) | (bit << st)) +#define set_bits(obj, st, fn, bits) obj = (((~(submask(fn - st) << st)) & obj) | (bits << st)) + +// definition to expand macro then apply to pragma message +// #pragma message(VAR_NAME_VALUE(HOST_OS_IOS)) +#define VALUE_TO_STRING(x) #x +#define VALUE(x) VALUE_TO_STRING(x) +#define VAR_NAME_VALUE(var) #var "=" VALUE(var) + +// format print +#ifdef __LP64__ +#define __PRI_64_prefix "l" +#define __PRI_PTR_prefix "l" +#else +#define __PRI_64_prefix "ll" +#define __PRI_PTR_prefix +#endif +#define PRIxPTR __PRI_PTR_prefix "x" /* uintptr_t */ + +// deprecated declared +#if defined(__GNUC__) || defined(__clang__) +#define DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define DEPRECATED __declspec(deprecated) +#else +#pragma message("WARNING: You need to implement DEPRECATED for this compiler") +#define DEPRECATED +#endif + +// export method +#if defined(_WIN32) +#define PUBLIC +#else +#define PUBLIC __attribute__((visibility("default"))) +#define INTERNAL __attribute__((visibility("internal"))) +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/tests/CMakeLists.txt b/app/src/main/cpp/Dobby/tests/CMakeLists.txt new file mode 100644 index 0000000..8cb2454 --- /dev/null +++ b/app/src/main/cpp/Dobby/tests/CMakeLists.txt @@ -0,0 +1,120 @@ + +find_package(PkgConfig REQUIRED) +pkg_check_modules(CAPSTONE REQUIRED capstone) +message(STATUS "Capstone libraries: " ${CAPSTONE_LIBRARY_DIRS}) +message(STATUS "Capstone includes: " ${CAPSTONE_INCLUDE_DIRS}) + +pkg_check_modules(UNICORN REQUIRED unicorn) +message(STATUS "unicorn libraries: " ${UNICORN_LIBRARY_DIRS}) +message(STATUS "unicorn includes: " ${UNICORN_INCLUDE_DIRS}) + +get_property(DOBBY_SOURCE_FILE_LIST + TARGET dobby + PROPERTY SOURCES) + +set(DOBBY_SOURCES) +foreach (path ${DOBBY_SOURCE_FILE_LIST}) + if (NOT path MATCHES "^/") + list(APPEND DOBBY_SOURCES ${DOBBY_DIR}/${path}) + else () + list(APPEND DOBBY_SOURCES ${path}) + endif () +endforeach () + + +add_executable(test_insn_relo_arm64 + test_insn_relo_arm64.cpp + UniconEmulator.cpp + ${DOBBY_SOURCES} + ) + +target_compile_definitions(test_insn_relo_arm64 PUBLIC + LOGGING_DEBUG=1 + DISABLE_ARCH_DETECT=1 + TARGET_ARCH_ARM64=1 + TEST_WITH_UNICORN=1 + ) + +target_include_directories(test_insn_relo_arm64 PUBLIC + ${CAPSTONE_INCLUDE_DIRS} + ${UNICORN_INCLUDE_DIRS} + ) + +target_link_directories(test_insn_relo_arm64 PUBLIC + ${CAPSTONE_LIBRARY_DIRS} + ${UNICORN_LIBRARY_DIRS} + ) + +target_link_libraries(test_insn_relo_arm64 PUBLIC + ${CAPSTONE_LIBRARIES} + ${UNICORN_LIBRARIES} + ) + +# --- + +add_executable(test_insn_relo_arm + test_insn_relo_arm.cpp + UniconEmulator.cpp + ${DOBBY_SOURCES} + ) + +target_compile_definitions(test_insn_relo_arm PUBLIC + LOGGING_DEBUG=1 + DISABLE_ARCH_DETECT=1 + TARGET_ARCH_ARM=1 + TEST_WITH_UNICORN=1 + ) + +target_include_directories(test_insn_relo_arm PUBLIC + ${CAPSTONE_INCLUDE_DIRS} + ${UNICORN_INCLUDE_DIRS} + ) + +target_link_directories(test_insn_relo_arm PUBLIC + ${CAPSTONE_LIBRARY_DIRS} + ${UNICORN_LIBRARY_DIRS} + ) + +target_link_libraries(test_insn_relo_arm PUBLIC + ${CAPSTONE_LIBRARIES} + ${UNICORN_LIBRARIES} + ) + +# --- + +add_executable(test_insn_relo_x64 + test_insn_relo_x64.cpp + UniconEmulator.cpp + ${DOBBY_SOURCES} + ) + +target_compile_definitions(test_insn_relo_x64 PUBLIC + LOGGING_DEBUG=1 + DISABLE_ARCH_DETECT=1 + TARGET_ARCH_X64=1 + TEST_WITH_UNICORN=1 + # TARGET_ARCH_IA32=1 + ) + +target_include_directories(test_insn_relo_x64 PUBLIC + ${CAPSTONE_INCLUDE_DIRS} + ${UNICORN_INCLUDE_DIRS} + ) + +target_link_directories(test_insn_relo_x64 PUBLIC + ${CAPSTONE_LIBRARY_DIRS} + ${UNICORN_LIBRARY_DIRS} + ) + +target_link_libraries(test_insn_relo_x64 PUBLIC + ${CAPSTONE_LIBRARIES} + ${UNICORN_LIBRARIES} + ) + +# --- + +add_executable(test_native + test_native.cpp) + +target_link_libraries(test_native + dobby) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/tests/UniconEmulator.cpp b/app/src/main/cpp/Dobby/tests/UniconEmulator.cpp new file mode 100644 index 0000000..a0848ca --- /dev/null +++ b/app/src/main/cpp/Dobby/tests/UniconEmulator.cpp @@ -0,0 +1,219 @@ +#include "UniconEmulator.h" +#include "PlatformUnifiedInterface/MemoryAllocator.h" +#include "InstructionRelocation/InstructionRelocation.h" + +// align +#ifndef ALIGN +#define ALIGN ALIGN_FLOOR +#endif +#define ALIGN_FLOOR(address, range) ((uintptr_t)address & ~((uintptr_t)range - 1)) +#define ALIGN_CEIL(address, range) (((uintptr_t)address + (uintptr_t)range - 1) & ~((uintptr_t)range - 1)) + +std::string g_arch = ""; + +void set_global_arch(std::string arch) { + g_arch = arch; +} + +std::unordered_map CapstoneDisassembler::instances_; + +CapstoneDisassembler *CapstoneDisassembler::Get(const std::string &arch) { + if (instances_.count(arch) == 0) { + cs_err err = CS_ERR_OK; + csh csh_; + if (arch == "arm") { + err = cs_open(CS_ARCH_ARM, CS_MODE_ARM, &csh_); + } else if (arch == "thumb") { + err = cs_open(CS_ARCH_ARM, CS_MODE_THUMB, &csh_); + } else if (arch == "arm64") { + err = cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &csh_); + } else if (arch == "x86_64") { + err = cs_open(CS_ARCH_X86, CS_MODE_64, &csh_); + } else if (arch == "x86") { + err = cs_open(CS_ARCH_X86, CS_MODE_32, &csh_); + } + + auto instance = new CapstoneDisassembler(arch, csh_); + instances_[arch] = instance; + } + return instances_[arch]; +} + +CapstoneDisassembler::CapstoneDisassembler(const std::string &arch, csh csh_) : arch_(arch), csh_(csh_) { +} + +CapstoneDisassembler::~CapstoneDisassembler() { + cs_close((csh *)&csh_); +} + +void CapstoneDisassembler::disassemble(uintptr_t addr, char *buffer, size_t buffer_size) { + cs_insn *insns; + + size_t count = cs_disasm(csh_, (uint8_t *)buffer, buffer_size, addr, 0, &insns); + for (size_t i = 0; i < count; ++i) { + auto &insn = insns[i]; + if (arch_ == "thumb") { + printf("%s %p: %s %s // thumb-%d\n", "-", insn.address, insn.mnemonic, insn.op_str, insn.size / 2); + } else { + printf("%s %p: %s %s\n", "-", insn.address, insn.mnemonic, insn.op_str); + } + } + cs_free(insns, count); +} + +static void hook_trace_insn(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { + auto emu = (UniconEmulator *)user_data; + + uc_err err; + char insn_bytes[16]; + err = uc_mem_read(uc, address, insn_bytes, size); + assert(err == UC_ERR_OK); + + if (address >= emu->end_) { + emu->stop(); + return; + } + + if ((emu->arch_ == "arm" || emu->arch_ == "thumb") && emu->isThumb()) { + CapstoneDisassembler::Get("thumb")->disassemble(address, (char *)insn_bytes, size); + } else { + CapstoneDisassembler::Get(g_arch)->disassemble(address, (char *)insn_bytes, size); + } +} + +static void hook_unmapped(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data) { + printf(">>> Unmapped memory access at %p, data size = %p, data value = %p\n", address, size, value); + auto emu = (UniconEmulator *)user_data; + emu->setUnmappedAddr(address); + emu->stop(); +} + +void dump_regions(uc_engine *uc) { + uc_mem_region *regions; + uint32_t region_count; + uc_mem_regions(uc, ®ions, ®ion_count); + for (int i = 0; i < region_count; ++i) { + auto ®ion = regions[i]; + printf("region: %p - %p\n", region.begin, region.end); + } +} + +UniconEmulator::UniconEmulator(const std::string &arch) { + uc_err err = UC_ERR_OK; + if (arch == "arm" || arch == "thumb") { + err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc_); + } else if (arch == "arm64") { + err = uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc_); + } else if (arch == "x86_64") { + err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc_); + } else if (arch == "x86") { + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc_); + } + assert(err == UC_ERR_OK); + + arch_ = arch; + + uc_hook hook_trace_insn_handle; + uc_hook_add(uc_, &hook_trace_insn_handle, UC_HOOK_CODE, (void *)hook_trace_insn, this, 1, 0); + + uc_hook hook_unmapped_handle; + uc_hook_add(uc_, &hook_unmapped_handle, UC_HOOK_MEM_UNMAPPED, (void *)hook_unmapped, this, 1, 0); +} + +void UniconEmulator::mapMemory(uintptr_t addr, char *buffer, size_t buffer_size) { + uc_err err = UC_ERR_OK; + uintptr_t map_addr = ALIGN_FLOOR(addr, 0x1000); + size_t map_size = ALIGN_CEIL(buffer_size, 0x1000); + err = uc_mem_map(uc_, map_addr, map_size, UC_PROT_ALL); + assert(err == UC_ERR_OK); + err = uc_mem_write(uc_, addr, buffer, buffer_size); + assert(err == UC_ERR_OK); +} + +void *UniconEmulator::readRegister(int regId) { + void *value = nullptr; + uc_reg_read(uc_, regId, &value); + return value; +} + +void UniconEmulator::writeRegister(int regNdx, void *value) { + uc_reg_write(uc_, regNdx, (void *)&value); +} + +void UniconEmulator::start(uintptr_t addr, uintptr_t end) { + uc_err err; + if (g_arch == "thumb") { + addr |= 1; + } + err = uc_emu_start(uc_, addr, end, 0, 0); + if (err == UC_ERR_FETCH_UNMAPPED || err == UC_ERR_READ_UNMAPPED || err == UC_ERR_WRITE_UNMAPPED) + err = UC_ERR_OK; + assert(err == UC_ERR_OK); +} + +void UniconEmulator::emulate(uintptr_t addr, uintptr_t end, char *buffer, size_t buffer_size) { + uc_err err; + mapMemory(addr, buffer, buffer_size); + writeRegister(UC_ARM_REG_PC, (void *)addr); + + if (end == 0) + end = addr + buffer_size; + + start_ = addr; + end_ = end; + + start(addr, end); +} + +void check_insn_relo(char *buffer, size_t buffer_size, bool check_fault_addr, int check_reg_id, + void (^callback)(UniconEmulator *orig, UniconEmulator *relo), uintptr_t relo_stop_size) { + auto *orig_ue = new UniconEmulator(g_arch); + auto *relo_ue = new UniconEmulator(g_arch); + + addr_t orig_addr = 0x100014000; + addr_t relocate_addr = 0x100024000; + + if (g_arch == "arm" || g_arch == "thumb") { + orig_addr = 0x10014000; + relocate_addr = 0x10024000; + } + + // auto dism = CapstoneDisassembler::Get("arm64"); + // dism->disassemble((uintptr_t)orig_addr, buffer, buffer_size); + // printf("\n"); + + auto origin = new CodeMemBlock(orig_addr, buffer_size); + auto relocated = new CodeMemBlock(relocate_addr, 0x1000); + if (g_arch == "thumb") { + origin->reset(origin->addr + 1, origin->size); + } + + GenRelocateCode(buffer, origin, relocated, false); + + if (g_arch == "thumb") { + orig_ue->writeRegister(UC_ARM_REG_CPSR, (void *)0x20); + relo_ue->writeRegister(UC_ARM_REG_CPSR, (void *)0x20); + } + orig_ue->emulate(orig_addr, 0, buffer, buffer_size); + if (g_arch == "thumb") { + relocated->addr -= 1; + } + if (relo_stop_size == 0) { + relo_stop_size = relocated->size; + } + relo_ue->emulate(relocate_addr, relocate_addr + relo_stop_size, (char *)relocated->addr, relocated->size); + + // dism->disassemble((uintptr_t)relocate_addr, (char *)relocated->addr, relocated->size); + // printf("\n"); + + if (check_fault_addr) { + assert(orig_ue->getFaultAddr() == relo_ue->getFaultAddr()); + } else if (check_reg_id != -1) { + assert(orig_ue->readRegister(check_reg_id) == relo_ue->readRegister(check_reg_id)); + } else if (callback) { + callback(orig_ue, relo_ue); + } + + delete orig_ue; + delete relo_ue; +} diff --git a/app/src/main/cpp/Dobby/tests/UniconEmulator.h b/app/src/main/cpp/Dobby/tests/UniconEmulator.h new file mode 100644 index 0000000..414618b --- /dev/null +++ b/app/src/main/cpp/Dobby/tests/UniconEmulator.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +#include +#include + +class CapstoneDisassembler { +public: + void disassemble(uintptr_t addr, char *buffer, size_t buffer_size); + + static CapstoneDisassembler *Get(const std::string &arch); + +private: + CapstoneDisassembler(const std::string &arch, csh csh_); + + ~CapstoneDisassembler(); + +private: + std::string arch_; + csh csh_; + + static std::unordered_map instances_; +}; + +class UniconEmulator { +public: + UniconEmulator(const std::string &arch); + + void mapMemory(uintptr_t addr, char *buffer, size_t buffer_size); + + void *readRegister(int regId); + + void writeRegister(int regId, void *value); + + void start(uintptr_t addr, uintptr_t end); + + void stop() { + uc_emu_stop(uc_); + } + + void emulate(uintptr_t addr, uintptr_t end, char *buffer, size_t buffer_size); + + void setUnmappedAddr(uintptr_t addr) { + unmapped_addr_ = addr; + } + + intptr_t getFaultAddr() { + return unmapped_addr_; + } + + bool isThumb() { + void *reg_value = readRegister(UC_ARM_REG_CPSR); + return (intptr_t)reg_value & 0x20; + } + + void reset(); + +public: + std::string arch_; + uintptr_t start_, end_; + +private: + uc_err err_; + uc_engine *uc_; + uintptr_t unmapped_addr_; +}; + +void set_global_arch(std::string arch); + +void check_insn_relo(char *buffer, size_t buffer_size, bool check_fault_addr, int check_reg_id, + void (^callback)(UniconEmulator *orig, UniconEmulator *relo), uintptr_t relo_stop_size = 0); \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/tests/test_insn_decoder_x86.cpp b/app/src/main/cpp/Dobby/tests/test_insn_decoder_x86.cpp new file mode 100644 index 0000000..20e0fb9 --- /dev/null +++ b/app/src/main/cpp/Dobby/tests/test_insn_decoder_x86.cpp @@ -0,0 +1,267 @@ +#include "InstructionRelocation/InstructionRelocation.h" +#include "InstructionRelocation/arm64/InstructionRelocationARM64.h" + +#include +#include + +#include + +class CapstoneDisassembler { +public: + void disassemble(uintptr_t addr, char *buffer, size_t buffer_szie); + + static CapstoneDisassembler *Get(const std::string &arch); + +private: + CapstoneDisassembler(const std::string &arch, csh csh_); + + ~CapstoneDisassembler(); + +private: + csh csh_; + + static CapstoneDisassembler *instance_; +}; + +CapstoneDisassembler *CapstoneDisassembler::instance_ = nullptr; + +CapstoneDisassembler *CapstoneDisassembler::Get(const std::string &arch) { + if (instance_ == nullptr) { + cs_err err = CS_ERR_OK; + csh csh_; + if (arch == "arm") { + err = cs_open(CS_ARCH_ARM, CS_MODE_ARM, &csh_); + } else if (arch == "arm64") { + err = cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &csh_); + } else if (arch == "x86_64") { + err = cs_open(CS_ARCH_X86, CS_MODE_64, &csh_); + } else if (arch == "x86") { + err = cs_open(CS_ARCH_X86, CS_MODE_32, &csh_); + } + instance_ = new CapstoneDisassembler(arch, csh_); + } + return instance_; +} + +CapstoneDisassembler::CapstoneDisassembler(const std::string &arch, csh csh_) : csh_(csh_) { +} + +CapstoneDisassembler::~CapstoneDisassembler() { + cs_close((csh *)&csh_); +} + +void CapstoneDisassembler::disassemble(uintptr_t addr, char *buffer, size_t buffer_size) { + cs_insn *insns; + + size_t count = cs_disasm(csh_, (uint8_t *)buffer, buffer_size, addr, 0, &insns); + for (size_t i = 0; i < count; ++i) { + auto &insn = insns[i]; + printf("%s %p: %s %s\n", "-", insn.address, insn.mnemonic, insn.op_str); + } + cs_free(insns, count); +} + +class UniconEmulator { +public: + UniconEmulator(const std::string &arch); + + void mapMemory(uintptr_t addr, char *buffer, size_t buffer_size); + + void *readRegister(int regId); + + void writeRegister(int regId, void *value); + + void start(uintptr_t addr, uintptr_t end); + + void stop() { + uc_emu_stop(uc_); + } + + void emulate(uintptr_t addr, char *buffer, size_t buffer_size); + + void setUnmappedAddr(uintptr_t addr) { + unmapped_addr_ = addr; + } + + intptr_t getFaultAddr() { + return unmapped_addr_; + } + + void reset(); + +private: +private: + uc_err err_; + uc_engine *uc_; + uintptr_t unmapped_addr_; +}; + +static void hook_trace_insn(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { + auto emu = (UniconEmulator *)user_data; + uc_err err; + char insn_bytes[16]; + err = uc_mem_read(uc, address, insn_bytes, size); + assert(err == UC_ERR_OK); + CapstoneDisassembler::Get("arm64")->disassemble(address, (char *)insn_bytes, size); +} + +static void hook_unmapped(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data) { + printf(">>> Unmapped memory access at %p, data size = %p, data value = %p\n", address, size, value); + auto emu = (UniconEmulator *)user_data; + emu->setUnmappedAddr(address); + emu->stop(); +} + +void dump_regions(uc_engine *uc) { + uc_mem_region *regions; + uint32_t region_count; + uc_mem_regions(uc, ®ions, ®ion_count); + for (int i = 0; i < region_count; ++i) { + auto ®ion = regions[i]; + printf("region: %p - %p\n", region.begin, region.end); + } +} + +UniconEmulator::UniconEmulator(const std::string &arch) { + uc_err err = UC_ERR_OK; + if (arch == "arm") { + err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc_); + } else if (arch == "arm64") { + err = uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc_); + } else if (arch == "x86_64") { + err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc_); + } else if (arch == "x86") { + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc_); + } + assert(err == UC_ERR_OK); + + uc_hook hook_trace_insn_handle; + uc_hook_add(uc_, &hook_trace_insn_handle, UC_HOOK_CODE, (void *)hook_trace_insn, this, 1, 0); + + uc_hook hook_unmapped_handle; + uc_hook_add(uc_, &hook_unmapped_handle, UC_HOOK_MEM_UNMAPPED, (void *)hook_unmapped, this, 1, 0); +} + +void UniconEmulator::mapMemory(uintptr_t addr, char *buffer, size_t buffer_size) { + uc_err err = UC_ERR_OK; + uintptr_t map_addr = ALIGN_FLOOR(addr, 0x1000); + size_t map_size = ALIGN_CEIL(buffer_size, 0x1000); + err = uc_mem_map(uc_, map_addr, map_size, UC_PROT_ALL); + assert(err == UC_ERR_OK); + err = uc_mem_write(uc_, addr, buffer, buffer_size); + assert(err == UC_ERR_OK); +} + +void *UniconEmulator::readRegister(int regId) { + void *value = nullptr; + uc_reg_read(uc_, regId, &value); + return value; +} + +void UniconEmulator::writeRegister(int regNdx, void *value) { + uc_reg_write(uc_, regNdx, (void *)&value); +} + +void UniconEmulator::start(uintptr_t addr, uintptr_t end) { + uc_err err; + err = uc_emu_start(uc_, addr, end, 0, 0); + if (err == UC_ERR_FETCH_UNMAPPED || err == UC_ERR_READ_UNMAPPED || err == UC_ERR_WRITE_UNMAPPED) + err = UC_ERR_OK; + assert(err == UC_ERR_OK); +} + +void UniconEmulator::emulate(uintptr_t addr, char *buffer, size_t buffer_size) { + uc_err err; + mapMemory(addr, buffer, buffer_size); + writeRegister(UC_ARM_REG_PC, (void *)addr); + start(addr, addr + buffer_size); +} + +void check_insn_relo(char *buffer, size_t buffer_size, bool check_fault_addr, int check_reg_id, + void (^callback)(UniconEmulator *orig, UniconEmulator *relo)) { + auto *orig_ue = new UniconEmulator("arm64"); + auto *relo_ue = new UniconEmulator("arm64"); + + addr_t orig_addr = 0x100004000; + addr_t relocate_addr = 0x200004000; + + // auto dism = CapstoneDisassembler::Get("arm64"); + // dism->disassemble((uintptr_t)orig_addr, buffer, buffer_size); + // printf("\n"); + + auto origin = new CodeMemBlock(orig_addr, buffer_size); + auto relocated = new CodeMemBlock(); + + GenRelocateCode(buffer, origin, relocated, false); + + orig_ue->emulate(orig_addr, buffer, buffer_size); + relo_ue->emulate(relocate_addr, (char *)relocated->addr, relocated->size); + + // dism->disassemble((uintptr_t)relocate_addr, (char *)relocated->addr, relocated->size); + // printf("\n"); + + if (check_fault_addr) { + assert(orig_ue->getFaultAddr() == relo_ue->getFaultAddr()); + } else if (check_reg_id != -1) { + assert(orig_ue->readRegister(check_reg_id) == relo_ue->readRegister(check_reg_id)); + } else if (callback) { + callback(orig_ue, relo_ue); + } + + delete orig_ue; + delete relo_ue; +} + +int main() { + // b #-0x4000 + check_insn_relo("\x00\xf0\xff\x17", 4, true, -1, nullptr); + // b #0x4000 + check_insn_relo("\x00\x10\x00\x14", 4, true, -1, nullptr); + + // bl #-0x4000 + check_insn_relo("\x00\xf0\xff\x97", 4, true, -1, nullptr); + // bl #0x4000 + check_insn_relo("\x00\x10\x00\x94", 4, true, -1, nullptr); + + // mov x0, #0 + // cbz x0, #-0x4000 + check_insn_relo("\x00\x00\x80\xd2\x00\x00\xfe\xb4", 8, true, -1, nullptr); + // mov x0, #0 + // cbz x0, #0x4000 + check_insn_relo("\x00\x00\x80\xd2\x00\x00\x02\xb4", 8, true, -1, nullptr); + + // ldr x0, #-0x4000 + check_insn_relo("\x00\x00\xfe\x58", 4, true, -1, nullptr); + // ldr x0, #0x4000 + check_insn_relo("\x00\x00\x02\x58", 4, true, -1, nullptr); + + // adr x0, #-0x4000 + check_insn_relo("\x00\x00\xfe\x10", 4, false, UC_ARM64_REG_X0, nullptr); + // adr x0, #0x4000 + check_insn_relo("\x00\x00\x02\x10", 4, false, UC_ARM64_REG_X0, nullptr); + + // adrp x0, #-0x4000 + check_insn_relo("\xe0\xff\xff\x90", 4, false, UC_ARM64_REG_X0, nullptr); + // adrp x0, #0x4000 + check_insn_relo("\x20\x00\x00\x90", 4, false, UC_ARM64_REG_X0, nullptr); + + // mov x0, #0 + // cmp x0, #0 + // b.eq #-0x4000 + check_insn_relo("\x00\x00\x80\xd2\x1f\x00\x00\xf1\x00\x00\xfe\x54", 12, true, -1, nullptr); + // mov x0, #0 + // cmp x0, #0 + // b.eq #0x4000 + check_insn_relo("\x00\x00\x80\xd2\x1f\x00\x00\xf1\x00\x00\x02\x54", 12, true, -1, nullptr); + + // mov x0, #0xb + // tbz w0, 2, #-0x4000 + check_insn_relo("\x60\x01\x80\xd2\x00\x00\x16\x36", 8, true, -1, nullptr); + // mov x0, #0xb + + // mov x0, #0xb + // tbz w0, 2, #0x4000 + check_insn_relo("\x60\x01\x80\xd2\x00\x00\x12\x36", 8, true, -1, nullptr); + + return 0; +} diff --git a/app/src/main/cpp/Dobby/tests/test_insn_relo_arm.cpp b/app/src/main/cpp/Dobby/tests/test_insn_relo_arm.cpp new file mode 100644 index 0000000..43dd16c --- /dev/null +++ b/app/src/main/cpp/Dobby/tests/test_insn_relo_arm.cpp @@ -0,0 +1,116 @@ +#include "InstructionRelocation/InstructionRelocation.h" + +#include "UniconEmulator.h" + +void check_insn_relo_arm(char *buffer, size_t buffer_size, bool check_fault_addr, int check_reg_id, + void (^callback)(UniconEmulator *orig, UniconEmulator *relo)) { + __attribute__((aligned(4))) char code[64] = {0}; + memcpy(code, buffer, buffer_size); + check_insn_relo(code, buffer_size, check_fault_addr, check_reg_id, callback); +} + +void check_insn_relo_thumb(char *buffer, size_t buffer_size, bool check_fault_addr, int check_reg_id, + void (^callback)(UniconEmulator *orig, UniconEmulator *relo), uintptr_t relo_stop_size = 0) { + __attribute__((aligned(4))) char code[64] = {0}; + memcpy(code, buffer, buffer_size); + check_insn_relo(code, buffer_size, check_fault_addr, check_reg_id, callback, relo_stop_size); +} + +int main() { + log_set_level(0); + set_global_arch("arm"); + + // ldr r0, [pc, #-0x20] + __attribute__((aligned(4))) char *code_0 = "\x20\x00\x1f\xe5"; + check_insn_relo(code_0, 4, true, -1, nullptr); + + // ldr r0, [pc, #0x20] + __attribute__((aligned(4))) char *code_1 = "\x20\x00\x9f\xe5"; + check_insn_relo(code_1, 4, false, -1, ^(UniconEmulator *orig, UniconEmulator *relo) { + assert(relo->getFaultAddr() == 0x10014028); + }); + + // add r0, pc, #-0x4000 + check_insn_relo_arm("\x01\x09\x4f\xe2", 4, false, UC_ARM_REG_R0, nullptr); + // add r0, pc, #0x4000 + check_insn_relo_arm("\x01\x09\x8f\xe2", 4, false, UC_ARM_REG_R0, nullptr); + + // b #-0x4000 + check_insn_relo_arm("\xfe\xef\xff\xea", 4, true, -1, nullptr); + // b #0x4000 + check_insn_relo_arm("\xfe\x0f\x00\xea", 4, true, -1, nullptr); + + // bl #-0x4000 + check_insn_relo_arm("\xfe\xef\xff\xeb", 4, true, -1, nullptr); + // blx #0x4000 + check_insn_relo_arm("\xfe\x0f\x00\xfa", 4, true, -1, nullptr); + + set_global_arch("thumb"); + + // cmp r0, pc + check_insn_relo_thumb("\x78\x45", 2, false, -1, ^(UniconEmulator *orig, UniconEmulator *relo) { + assert(relo->readRegister(UC_ARM_REG_R12) == (void *)0x10014004); + }); + + // adr r0, #0x20 + check_insn_relo_thumb("\x08\xa0", 2, false, UC_ARM_REG_R0, nullptr, 8); + + // bx pc + check_insn_relo_thumb("\x78\x47", 2, false, UC_ARM_REG_PC, nullptr); + // blx pc + check_insn_relo_thumb("\xf8\x47", 2, false, UC_ARM_REG_PC, nullptr); + + // ldr r0, [pc, #8] + check_insn_relo_thumb("\x02\x48", 2, false, -1, ^(UniconEmulator *orig, UniconEmulator *relo) { + assert(relo->getFaultAddr() == 0x1001400c); + }); + + // b #-8 + check_insn_relo_thumb("\xfa\xe7", 2, true, -1, nullptr); + // b #8 + check_insn_relo_thumb("\x02\xe0", 2, false, -1, ^(UniconEmulator *orig, UniconEmulator *relo) { + assert(relo->getFaultAddr() == 0x10014008); + }); + + // mov r0, 0 + // cbz r0, #8 + check_insn_relo_thumb("\x4f\xf0\x00\x00" + "\x10\xb1", + 6, false, -1, ^(UniconEmulator *orig, UniconEmulator *relo) { + assert(relo->getFaultAddr() == 0x1001400c); + }); + + set_global_arch("thumb"); + + // cmp r0, r0 + // beq.w #-0x4000 + check_insn_relo_thumb("\x80\x42" + "\x3c\xf4\x00\xa8", + 6, true, -1, nullptr); + // cmp r0, r0 + // beq.w #0x4000 + check_insn_relo_thumb("\x80\x42" + "\x04\xf0\x00\x80", + 6, true, -1, nullptr); + + // bl #-0x4000 + check_insn_relo_thumb("\xfb\xf7\xfe\xff", 4, true, -1, nullptr); + // blx #0x4000 + check_insn_relo_thumb("\x03\xf0\xfe\xef", 4, true, -1, nullptr); + + // adr r0, #-0x512 + check_insn_relo_thumb("\xaf\xf2\x12\x50", 4, false, UC_ARM_REG_R0, nullptr); + // adr r0, #0x512 + check_insn_relo_thumb("\x0f\xf2\x12\x50", 4, false, UC_ARM_REG_R0, nullptr); + + // ldr r0, [pc, #-0x512] + check_insn_relo_thumb("\x5f\xf8\x12\x05", 4, true, -1, nullptr, 0xc); + // ldr r0, [pc, #0x512] + check_insn_relo_thumb( + "\xdf\xf8\x12\x05", 4, false, -1, + ^(UniconEmulator *orig, UniconEmulator *relo) { + assert(relo->getFaultAddr() == 0x10014000 + 0x512 + 4); + }, + 0xc); + return 0; +} diff --git a/app/src/main/cpp/Dobby/tests/test_insn_relo_arm64.cpp b/app/src/main/cpp/Dobby/tests/test_insn_relo_arm64.cpp new file mode 100644 index 0000000..d76aee6 --- /dev/null +++ b/app/src/main/cpp/Dobby/tests/test_insn_relo_arm64.cpp @@ -0,0 +1,97 @@ +/* + +test_b: +b #-0x4000 +b #0x4000 + +test_bl: +bl #-0x4000 +bl #0x4000 + +test_cbz: +cbz x0, #-0x4000 +cbz x0, #0x4000 + +test_ldr_liberal: +ldr x0, #-0x4000 +ldr x0, #0x4000 + +test_adr: +adr x0, #-0x4000 +adr x0, #0x4000 + +test_adrp: +adrp x0, #-0x4000 +adrp x0, #0x4000 + +test_b_cond: +b.eq #-0x4000 +b.eq #0x4000 + +test_tbz: +tbz x0, #0, #-0x4000 +tbz x0, #0, #0x4000 + +*/ + +// clang -arch arm64 code_arm64.asm -o code_arm64.o + +#include "InstructionRelocation/InstructionRelocation.h" + +#include "UniconEmulator.h" + +int main() { + set_global_arch("arm64"); + + // b #-0x4000 + check_insn_relo("\x00\xf0\xff\x17", 4, true, -1, nullptr); + // b #0x4000 + check_insn_relo("\x00\x10\x00\x14", 4, true, -1, nullptr); + + // bl #-0x4000 + check_insn_relo("\x00\xf0\xff\x97", 4, true, -1, nullptr); + // bl #0x4000 + check_insn_relo("\x00\x10\x00\x94", 4, true, -1, nullptr); + + // mov x0, #0 + // cbz x0, #-0x4000 + check_insn_relo("\x00\x00\x80\xd2\x00\x00\xfe\xb4", 8, true, -1, nullptr); + // mov x0, #0 + // cbz x0, #0x4000 + check_insn_relo("\x00\x00\x80\xd2\x00\x00\x02\xb4", 8, true, -1, nullptr); + + // ldr x0, #-0x4000 + check_insn_relo("\x00\x00\xfe\x58", 4, true, -1, nullptr); + // ldr x0, #0x4000 + check_insn_relo("\x00\x00\x02\x58", 4, true, -1, nullptr); + + // adr x0, #-0x4000 + check_insn_relo("\x00\x00\xfe\x10", 4, false, UC_ARM64_REG_X0, nullptr); + // adr x0, #0x4000 + check_insn_relo("\x00\x00\x02\x10", 4, false, UC_ARM64_REG_X0, nullptr); + + // adrp x0, #-0x4000 + check_insn_relo("\xe0\xff\xff\x90", 4, false, UC_ARM64_REG_X0, nullptr); + // adrp x0, #0x4000 + check_insn_relo("\x20\x00\x00\x90", 4, false, UC_ARM64_REG_X0, nullptr); + + // mov x0, #0 + // cmp x0, #0 + // b.eq #-0x4000 + check_insn_relo("\x00\x00\x80\xd2\x1f\x00\x00\xf1\x00\x00\xfe\x54", 12, true, -1, nullptr); + // mov x0, #0 + // cmp x0, #0 + // b.eq #0x4000 + check_insn_relo("\x00\x00\x80\xd2\x1f\x00\x00\xf1\x00\x00\x02\x54", 12, true, -1, nullptr); + + // mov x0, #0xb + // tbz w0, 2, #-0x4000 + check_insn_relo("\x60\x01\x80\xd2\x00\x00\x16\x36", 8, true, -1, nullptr); + // mov x0, #0xb + + // mov x0, #0xb + // tbz w0, 2, #0x4000 + check_insn_relo("\x60\x01\x80\xd2\x00\x00\x12\x36", 8, true, -1, nullptr); + + return 0; +} diff --git a/app/src/main/cpp/Dobby/tests/test_insn_relo_x64.cpp b/app/src/main/cpp/Dobby/tests/test_insn_relo_x64.cpp new file mode 100644 index 0000000..f9db59d --- /dev/null +++ b/app/src/main/cpp/Dobby/tests/test_insn_relo_x64.cpp @@ -0,0 +1,38 @@ +#include "InstructionRelocation/InstructionRelocation.h" + +#include "UniconEmulator.h" + +int main() { + log_set_level(0); + set_global_arch("x86_64"); + + + // cmp eax, eax + // jz -0x20 + check_insn_relo("\x39\xc0\x74\xdc", 4, false, UC_X86_REG_IP, nullptr); + // cmp eax, eax + // jz 0x20 + check_insn_relo("\x39\xc0\x74\x1c", 4, false, UC_X86_REG_IP, nullptr); + + // jmp -0x20 + check_insn_relo("\xeb\xde", 2, false, UC_X86_REG_IP, nullptr); + // jmp 0x20 + check_insn_relo("\xeb\x1e", 2, false, UC_X86_REG_IP, nullptr); + + + // jmp -0x4000 + check_insn_relo("\xe9\xfb\xbf\xff\xff", 4, false, UC_X86_REG_IP, nullptr); + // jmp 0x4000 + check_insn_relo("\xe9\xfb\x3f\x00\x00", 4, false, UC_X86_REG_IP, nullptr); + + // lea rax, [rip] + check_insn_relo("\x48\x8d\x05\x00\x00\x00\x00", 7, false, UC_X86_REG_RAX, nullptr); + + // lea rax, [rip + 0x4000] + check_insn_relo("\x48\x8d\x05\x00\x40\x00\x00", 7, false, UC_X86_REG_RAX, nullptr); + + // mov rax, [rip + 0x4000] + check_insn_relo("\x48\x8b\x05\x00\x40\x00\x00", 7, true, -1, nullptr); + + return 0; +} diff --git a/app/src/main/cpp/Dobby/tests/test_native.cpp b/app/src/main/cpp/Dobby/tests/test_native.cpp new file mode 100644 index 0000000..21f84a9 --- /dev/null +++ b/app/src/main/cpp/Dobby/tests/test_native.cpp @@ -0,0 +1,30 @@ +#include "dobby.h" + +#include +#include + +#define LOG(fmt, ...) printf("[test_native] " fmt, ##__VA_ARGS__) + +void test_execve() { + char *argv[] = {NULL}; + char *envp[] = {NULL}; + + LOG("test execve"); + + DobbyInstrument(DobbySymbolResolver(0, "_execve"), [](void *, DobbyRegisterContext *ctx) { + LOG("execve: %s", (char *)ctx->general.regs.rdi); + return; + }); + + execve("ls", argv, envp); + + return; +} + +int main(int argc, char *argv[]) { + log_set_level(0); + + test_execve(); + + return 0; +} diff --git a/app/src/main/cpp/cJSON/cJSON.c b/app/src/main/cpp/cJSON/cJSON.c deleted file mode 100644 index 4e4979e..0000000 --- a/app/src/main/cpp/cJSON/cJSON.c +++ /dev/null @@ -1,3129 +0,0 @@ -/* - Copyright (c) 2009-2017 Dave Gamble and cJSON contributors - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/* cJSON */ -/* JSON parser in C. */ - -/* disable warnings about old C89 functions in MSVC */ -#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) -#define _CRT_SECURE_NO_DEPRECATE -#endif - -#ifdef __GNUC__ -#pragma GCC visibility push(default) -#endif -#if defined(_MSC_VER) -#pragma warning (push) -/* disable warning about single line comments in system headers */ -#pragma warning (disable : 4001) -#endif - -#include -#include -#include -#include -#include -#include -#include - -#ifdef ENABLE_LOCALES -#include -#endif - -#if defined(_MSC_VER) -#pragma warning (pop) -#endif -#ifdef __GNUC__ -#pragma GCC visibility pop -#endif - -#include "cJSON.h" - -/* define our own boolean type */ -#ifdef true -#undef true -#endif -#define true ((cJSON_bool)1) - -#ifdef false -#undef false -#endif -#define false ((cJSON_bool)0) - -/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ -#ifndef isinf -#define isinf(d) (isnan((d - d)) && !isnan(d)) -#endif -#ifndef isnan -#define isnan(d) (d != d) -#endif - -#ifndef NAN -#ifdef _WIN32 -#define NAN sqrt(-1.0) -#else -#define NAN 0.0/0.0 -#endif -#endif - -typedef struct { - const unsigned char *json; - size_t position; -} error; -static error global_error = { NULL, 0 }; - -CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) -{ - return (const char*) (global_error.json + global_error.position); -} - -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) -{ - if (!cJSON_IsString(item)) - { - return NULL; - } - - return item->valuestring; -} - -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) -{ - if (!cJSON_IsNumber(item)) - { - return (double) NAN; - } - - return item->valuedouble; -} - -/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 17) - #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. -#endif - -CJSON_PUBLIC(const char*) cJSON_Version(void) -{ - static char version[15]; - sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); - - return version; -} - -/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ -static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) -{ - if ((string1 == NULL) || (string2 == NULL)) - { - return 1; - } - - if (string1 == string2) - { - return 0; - } - - for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) - { - if (*string1 == '\0') - { - return 0; - } - } - - return tolower(*string1) - tolower(*string2); -} - -typedef struct internal_hooks -{ - void *(CJSON_CDECL *allocate)(size_t size); - void (CJSON_CDECL *deallocate)(void *pointer); - void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); -} internal_hooks; - -#if defined(_MSC_VER) -/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ -static void * CJSON_CDECL internal_malloc(size_t size) -{ - return malloc(size); -} -static void CJSON_CDECL internal_free(void *pointer) -{ - free(pointer); -} -static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) -{ - return realloc(pointer, size); -} -#else -#define internal_malloc malloc -#define internal_free free -#define internal_realloc realloc -#endif - -/* strlen of character literals resolved at compile time */ -#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) - -static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; - -static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) -{ - size_t length = 0; - unsigned char *copy = NULL; - - if (string == NULL) - { - return NULL; - } - - length = strlen((const char*)string) + sizeof(""); - copy = (unsigned char*)hooks->allocate(length); - if (copy == NULL) - { - return NULL; - } - memcpy(copy, string, length); - - return copy; -} - -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (hooks == NULL) - { - /* Reset hooks */ - global_hooks.allocate = malloc; - global_hooks.deallocate = free; - global_hooks.reallocate = realloc; - return; - } - - global_hooks.allocate = malloc; - if (hooks->malloc_fn != NULL) - { - global_hooks.allocate = hooks->malloc_fn; - } - - global_hooks.deallocate = free; - if (hooks->free_fn != NULL) - { - global_hooks.deallocate = hooks->free_fn; - } - - /* use realloc only if both free and malloc are used */ - global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) - { - global_hooks.reallocate = realloc; - } -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item(const internal_hooks * const hooks) -{ - cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); - if (node) - { - memset(node, '\0', sizeof(cJSON)); - } - - return node; -} - -/* Delete a cJSON structure. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) -{ - cJSON *next = NULL; - while (item != NULL) - { - next = item->next; - if (!(item->type & cJSON_IsReference) && (item->child != NULL)) - { - cJSON_Delete(item->child); - } - if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) - { - global_hooks.deallocate(item->valuestring); - } - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) - { - global_hooks.deallocate(item->string); - } - global_hooks.deallocate(item); - item = next; - } -} - -/* get the decimal point character of the current locale */ -static unsigned char get_decimal_point(void) -{ -#ifdef ENABLE_LOCALES - struct lconv *lconv = localeconv(); - return (unsigned char) lconv->decimal_point[0]; -#else - return '.'; -#endif -} - -typedef struct -{ - const unsigned char *content; - size_t length; - size_t offset; - size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ - internal_hooks hooks; -} parse_buffer; - -/* check if the given size is left to read in a given parse buffer (starting with 1) */ -#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) -/* check if the buffer can be accessed at the given index (starting with 0) */ -#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) -#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) -/* get a pointer to the buffer at the position */ -#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) - -/* Parse the input text to generate a number, and populate the result into item. */ -static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) -{ - double number = 0; - unsigned char *after_end = NULL; - unsigned char number_c_string[64]; - unsigned char decimal_point = get_decimal_point(); - size_t i = 0; - - if ((input_buffer == NULL) || (input_buffer->content == NULL)) - { - return false; - } - - /* copy the number into a temporary buffer and replace '.' with the decimal point - * of the current locale (for strtod) - * This also takes care of '\0' not necessarily being available for marking the end of the input */ - for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) - { - switch (buffer_at_offset(input_buffer)[i]) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': - case 'e': - case 'E': - number_c_string[i] = buffer_at_offset(input_buffer)[i]; - break; - - case '.': - number_c_string[i] = decimal_point; - break; - - default: - goto loop_end; - } - } -loop_end: - number_c_string[i] = '\0'; - - number = strtod((const char*)number_c_string, (char**)&after_end); - if (number_c_string == after_end) - { - return false; /* parse_error */ - } - - item->valuedouble = number; - - /* use saturation in case of overflow */ - if (number >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (number <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)number; - } - - item->type = cJSON_Number; - - input_buffer->offset += (size_t)(after_end - number_c_string); - return true; -} - -/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) -{ - if (number >= INT_MAX) - { - object->valueint = INT_MAX; - } - else if (number <= (double)INT_MIN) - { - object->valueint = INT_MIN; - } - else - { - object->valueint = (int)number; - } - - return object->valuedouble = number; -} - -CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) -{ - char *copy = NULL; - /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ - if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) - { - return NULL; - } - /* return NULL if the object is corrupted */ - if (object->valuestring == NULL) - { - return NULL; - } - if (strlen(valuestring) <= strlen(object->valuestring)) - { - strcpy(object->valuestring, valuestring); - return object->valuestring; - } - copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); - if (copy == NULL) - { - return NULL; - } - if (object->valuestring != NULL) - { - cJSON_free(object->valuestring); - } - object->valuestring = copy; - - return copy; -} - -typedef struct -{ - unsigned char *buffer; - size_t length; - size_t offset; - size_t depth; /* current nesting depth (for formatted printing) */ - cJSON_bool noalloc; - cJSON_bool format; /* is this print a formatted print */ - internal_hooks hooks; -} printbuffer; - -/* realloc printbuffer if necessary to have at least "needed" bytes more */ -static unsigned char* ensure(printbuffer * const p, size_t needed) -{ - unsigned char *newbuffer = NULL; - size_t newsize = 0; - - if ((p == NULL) || (p->buffer == NULL)) - { - return NULL; - } - - if ((p->length > 0) && (p->offset >= p->length)) - { - /* make sure that offset is valid */ - return NULL; - } - - if (needed > INT_MAX) - { - /* sizes bigger than INT_MAX are currently not supported */ - return NULL; - } - - needed += p->offset + 1; - if (needed <= p->length) - { - return p->buffer + p->offset; - } - - if (p->noalloc) { - return NULL; - } - - /* calculate new buffer size */ - if (needed > (INT_MAX / 2)) - { - /* overflow of int, use INT_MAX if possible */ - if (needed <= INT_MAX) - { - newsize = INT_MAX; - } - else - { - return NULL; - } - } - else - { - newsize = needed * 2; - } - - if (p->hooks.reallocate != NULL) - { - /* reallocate with realloc if available */ - newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); - if (newbuffer == NULL) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - } - else - { - /* otherwise reallocate manually */ - newbuffer = (unsigned char*)p->hooks.allocate(newsize); - if (!newbuffer) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - - memcpy(newbuffer, p->buffer, p->offset + 1); - p->hooks.deallocate(p->buffer); - } - p->length = newsize; - p->buffer = newbuffer; - - return newbuffer + p->offset; -} - -/* calculate the new length of the string in a printbuffer and update the offset */ -static void update_offset(printbuffer * const buffer) -{ - const unsigned char *buffer_pointer = NULL; - if ((buffer == NULL) || (buffer->buffer == NULL)) - { - return; - } - buffer_pointer = buffer->buffer + buffer->offset; - - buffer->offset += strlen((const char*)buffer_pointer); -} - -/* securely comparison of floating-point variables */ -static cJSON_bool compare_double(double a, double b) -{ - double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); - return (fabs(a - b) <= maxVal * DBL_EPSILON); -} - -/* Render the number nicely from the given item into a string. */ -static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - double d = item->valuedouble; - int length = 0; - size_t i = 0; - unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ - unsigned char decimal_point = get_decimal_point(); - double test = 0.0; - - if (output_buffer == NULL) - { - return false; - } - - /* This checks for NaN and Infinity */ - if (isnan(d) || isinf(d)) - { - length = sprintf((char*)number_buffer, "null"); - } - else if(d == (double)item->valueint) - { - length = sprintf((char*)number_buffer, "%d", item->valueint); - } - else - { - /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = sprintf((char*)number_buffer, "%1.15g", d); - - /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) - { - /* If not, print with 17 decimal places of precision */ - length = sprintf((char*)number_buffer, "%1.17g", d); - } - } - - /* sprintf failed or buffer overrun occurred */ - if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) - { - return false; - } - - /* reserve appropriate space in the output */ - output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); - if (output_pointer == NULL) - { - return false; - } - - /* copy the printed number to the output and replace locale - * dependent decimal point with '.' */ - for (i = 0; i < ((size_t)length); i++) - { - if (number_buffer[i] == decimal_point) - { - output_pointer[i] = '.'; - continue; - } - - output_pointer[i] = number_buffer[i]; - } - output_pointer[i] = '\0'; - - output_buffer->offset += (size_t)length; - - return true; -} - -/* parse 4 digit hexadecimal number */ -static unsigned parse_hex4(const unsigned char * const input) -{ - unsigned int h = 0; - size_t i = 0; - - for (i = 0; i < 4; i++) - { - /* parse digit */ - if ((input[i] >= '0') && (input[i] <= '9')) - { - h += (unsigned int) input[i] - '0'; - } - else if ((input[i] >= 'A') && (input[i] <= 'F')) - { - h += (unsigned int) 10 + input[i] - 'A'; - } - else if ((input[i] >= 'a') && (input[i] <= 'f')) - { - h += (unsigned int) 10 + input[i] - 'a'; - } - else /* invalid */ - { - return 0; - } - - if (i < 3) - { - /* shift left to make place for the next nibble */ - h = h << 4; - } - } - - return h; -} - -/* converts a UTF-16 literal to UTF-8 - * A literal can be one or two sequences of the form \uXXXX */ -static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) -{ - long unsigned int codepoint = 0; - unsigned int first_code = 0; - const unsigned char *first_sequence = input_pointer; - unsigned char utf8_length = 0; - unsigned char utf8_position = 0; - unsigned char sequence_length = 0; - unsigned char first_byte_mark = 0; - - if ((input_end - first_sequence) < 6) - { - /* input ends unexpectedly */ - goto fail; - } - - /* get the first utf16 sequence */ - first_code = parse_hex4(first_sequence + 2); - - /* check that the code is valid */ - if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) - { - goto fail; - } - - /* UTF16 surrogate pair */ - if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) - { - const unsigned char *second_sequence = first_sequence + 6; - unsigned int second_code = 0; - sequence_length = 12; /* \uXXXX\uXXXX */ - - if ((input_end - second_sequence) < 6) - { - /* input ends unexpectedly */ - goto fail; - } - - if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) - { - /* missing second half of the surrogate pair */ - goto fail; - } - - /* get the second utf16 sequence */ - second_code = parse_hex4(second_sequence + 2); - /* check that the code is valid */ - if ((second_code < 0xDC00) || (second_code > 0xDFFF)) - { - /* invalid second half of the surrogate pair */ - goto fail; - } - - - /* calculate the unicode codepoint from the surrogate pair */ - codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); - } - else - { - sequence_length = 6; /* \uXXXX */ - codepoint = first_code; - } - - /* encode as UTF-8 - * takes at maximum 4 bytes to encode: - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - if (codepoint < 0x80) - { - /* normal ascii, encoding 0xxxxxxx */ - utf8_length = 1; - } - else if (codepoint < 0x800) - { - /* two bytes, encoding 110xxxxx 10xxxxxx */ - utf8_length = 2; - first_byte_mark = 0xC0; /* 11000000 */ - } - else if (codepoint < 0x10000) - { - /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ - utf8_length = 3; - first_byte_mark = 0xE0; /* 11100000 */ - } - else if (codepoint <= 0x10FFFF) - { - /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ - utf8_length = 4; - first_byte_mark = 0xF0; /* 11110000 */ - } - else - { - /* invalid unicode codepoint */ - goto fail; - } - - /* encode as utf8 */ - for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) - { - /* 10xxxxxx */ - (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); - codepoint >>= 6; - } - /* encode first byte */ - if (utf8_length > 1) - { - (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); - } - else - { - (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); - } - - *output_pointer += utf8_length; - - return sequence_length; - -fail: - return 0; -} - -/* Parse the input text into an unescaped cinput, and populate item. */ -static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) -{ - const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; - const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; - unsigned char *output_pointer = NULL; - unsigned char *output = NULL; - - /* not a string */ - if (buffer_at_offset(input_buffer)[0] != '\"') - { - goto fail; - } - - { - /* calculate approximate size of the output (overestimate) */ - size_t allocation_length = 0; - size_t skipped_bytes = 0; - while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) - { - /* is escape sequence */ - if (input_end[0] == '\\') - { - if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) - { - /* prevent buffer overflow when last input character is a backslash */ - goto fail; - } - skipped_bytes++; - input_end++; - } - input_end++; - } - if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) - { - goto fail; /* string ended unexpectedly */ - } - - /* This is at most how much we need for the output */ - allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; - output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); - if (output == NULL) - { - goto fail; /* allocation failure */ - } - } - - output_pointer = output; - /* loop through the string literal */ - while (input_pointer < input_end) - { - if (*input_pointer != '\\') - { - *output_pointer++ = *input_pointer++; - } - /* escape sequence */ - else - { - unsigned char sequence_length = 2; - if ((input_end - input_pointer) < 1) - { - goto fail; - } - - switch (input_pointer[1]) - { - case 'b': - *output_pointer++ = '\b'; - break; - case 'f': - *output_pointer++ = '\f'; - break; - case 'n': - *output_pointer++ = '\n'; - break; - case 'r': - *output_pointer++ = '\r'; - break; - case 't': - *output_pointer++ = '\t'; - break; - case '\"': - case '\\': - case '/': - *output_pointer++ = input_pointer[1]; - break; - - /* UTF-16 literal */ - case 'u': - sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); - if (sequence_length == 0) - { - /* failed to convert UTF16-literal to UTF-8 */ - goto fail; - } - break; - - default: - goto fail; - } - input_pointer += sequence_length; - } - } - - /* zero terminate the output */ - *output_pointer = '\0'; - - item->type = cJSON_String; - item->valuestring = (char*)output; - - input_buffer->offset = (size_t) (input_end - input_buffer->content); - input_buffer->offset++; - - return true; - -fail: - if (output != NULL) - { - input_buffer->hooks.deallocate(output); - } - - if (input_pointer != NULL) - { - input_buffer->offset = (size_t)(input_pointer - input_buffer->content); - } - - return false; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) -{ - const unsigned char *input_pointer = NULL; - unsigned char *output = NULL; - unsigned char *output_pointer = NULL; - size_t output_length = 0; - /* numbers of additional characters needed for escaping */ - size_t escape_characters = 0; - - if (output_buffer == NULL) - { - return false; - } - - /* empty string */ - if (input == NULL) - { - output = ensure(output_buffer, sizeof("\"\"")); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "\"\""); - - return true; - } - - /* set "flag" to 1 if something needs to be escaped */ - for (input_pointer = input; *input_pointer; input_pointer++) - { - switch (*input_pointer) - { - case '\"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - /* one character escape sequence */ - escape_characters++; - break; - default: - if (*input_pointer < 32) - { - /* UTF-16 escape sequence uXXXX */ - escape_characters += 5; - } - break; - } - } - output_length = (size_t)(input_pointer - input) + escape_characters; - - output = ensure(output_buffer, output_length + sizeof("\"\"")); - if (output == NULL) - { - return false; - } - - /* no characters have to be escaped */ - if (escape_characters == 0) - { - output[0] = '\"'; - memcpy(output + 1, input, output_length); - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; - - return true; - } - - output[0] = '\"'; - output_pointer = output + 1; - /* copy the string */ - for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) - { - if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) - { - /* normal character, copy */ - *output_pointer = *input_pointer; - } - else - { - /* character needs to be escaped */ - *output_pointer++ = '\\'; - switch (*input_pointer) - { - case '\\': - *output_pointer = '\\'; - break; - case '\"': - *output_pointer = '\"'; - break; - case '\b': - *output_pointer = 'b'; - break; - case '\f': - *output_pointer = 'f'; - break; - case '\n': - *output_pointer = 'n'; - break; - case '\r': - *output_pointer = 'r'; - break; - case '\t': - *output_pointer = 't'; - break; - default: - /* escape and print as unicode codepoint */ - sprintf((char*)output_pointer, "u%04x", *input_pointer); - output_pointer += 4; - break; - } - } - } - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; - - return true; -} - -/* Invoke print_string_ptr (which is useful) on an item. */ -static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) -{ - return print_string_ptr((unsigned char*)item->valuestring, p); -} - -/* Predeclare these prototypes. */ -static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); -static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); -static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); - -/* Utility to jump whitespace and cr/lf */ -static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) -{ - if ((buffer == NULL) || (buffer->content == NULL)) - { - return NULL; - } - - if (cannot_access_at_index(buffer, 0)) - { - return buffer; - } - - while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) - { - buffer->offset++; - } - - if (buffer->offset == buffer->length) - { - buffer->offset--; - } - - return buffer; -} - -/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ -static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) -{ - if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) - { - return NULL; - } - - if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) - { - buffer->offset += 3; - } - - return buffer; -} - -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) -{ - size_t buffer_length; - - if (NULL == value) - { - return NULL; - } - - /* Adding null character size due to require_null_terminated. */ - buffer_length = strlen(value) + sizeof(""); - - return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); -} - -/* Parse an object - create a new root, and populate. */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) -{ - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; - cJSON *item = NULL; - - /* reset error position */ - global_error.json = NULL; - global_error.position = 0; - - if (value == NULL || 0 == buffer_length) - { - goto fail; - } - - buffer.content = (const unsigned char*)value; - buffer.length = buffer_length; - buffer.offset = 0; - buffer.hooks = global_hooks; - - item = cJSON_New_Item(&global_hooks); - if (item == NULL) /* memory fail */ - { - goto fail; - } - - if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) - { - /* parse failure. ep is set. */ - goto fail; - } - - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) - { - buffer_skip_whitespace(&buffer); - if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') - { - goto fail; - } - } - if (return_parse_end) - { - *return_parse_end = (const char*)buffer_at_offset(&buffer); - } - - return item; - -fail: - if (item != NULL) - { - cJSON_Delete(item); - } - - if (value != NULL) - { - error local_error; - local_error.json = (const unsigned char*)value; - local_error.position = 0; - - if (buffer.offset < buffer.length) - { - local_error.position = buffer.offset; - } - else if (buffer.length > 0) - { - local_error.position = buffer.length - 1; - } - - if (return_parse_end != NULL) - { - *return_parse_end = (const char*)local_error.json + local_error.position; - } - - global_error = local_error; - } - - return NULL; -} - -/* Default options for cJSON_Parse */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) -{ - return cJSON_ParseWithOpts(value, 0, 0); -} - -CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) -{ - return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); -} - -#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) - -static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) -{ - static const size_t default_buffer_size = 256; - printbuffer buffer[1]; - unsigned char *printed = NULL; - - memset(buffer, 0, sizeof(buffer)); - - /* create buffer */ - buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); - buffer->length = default_buffer_size; - buffer->format = format; - buffer->hooks = *hooks; - if (buffer->buffer == NULL) - { - goto fail; - } - - /* print the value */ - if (!print_value(item, buffer)) - { - goto fail; - } - update_offset(buffer); - - /* check if reallocate is available */ - if (hooks->reallocate != NULL) - { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); - if (printed == NULL) { - goto fail; - } - buffer->buffer = NULL; - } - else /* otherwise copy the JSON over to a new buffer */ - { - printed = (unsigned char*) hooks->allocate(buffer->offset + 1); - if (printed == NULL) - { - goto fail; - } - memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); - printed[buffer->offset] = '\0'; /* just to be sure */ - - /* free the buffer */ - hooks->deallocate(buffer->buffer); - } - - return printed; - -fail: - if (buffer->buffer != NULL) - { - hooks->deallocate(buffer->buffer); - } - - if (printed != NULL) - { - hooks->deallocate(printed); - } - - return NULL; -} - -/* Render a cJSON item/entity/structure to text. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) -{ - return (char*)print(item, true, &global_hooks); -} - -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) -{ - return (char*)print(item, false, &global_hooks); -} - -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) -{ - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - - if (prebuffer < 0) - { - return NULL; - } - - p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); - if (!p.buffer) - { - return NULL; - } - - p.length = (size_t)prebuffer; - p.offset = 0; - p.noalloc = false; - p.format = fmt; - p.hooks = global_hooks; - - if (!print_value(item, &p)) - { - global_hooks.deallocate(p.buffer); - return NULL; - } - - return (char*)p.buffer; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) -{ - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - - if ((length < 0) || (buffer == NULL)) - { - return false; - } - - p.buffer = (unsigned char*)buffer; - p.length = (size_t)length; - p.offset = 0; - p.noalloc = true; - p.format = format; - p.hooks = global_hooks; - - return print_value(item, &p); -} - -/* Parser core - when encountering text, process appropriately. */ -static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) -{ - if ((input_buffer == NULL) || (input_buffer->content == NULL)) - { - return false; /* no input */ - } - - /* parse the different types of values */ - /* null */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) - { - item->type = cJSON_NULL; - input_buffer->offset += 4; - return true; - } - /* false */ - if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) - { - item->type = cJSON_False; - input_buffer->offset += 5; - return true; - } - /* true */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) - { - item->type = cJSON_True; - item->valueint = 1; - input_buffer->offset += 4; - return true; - } - /* string */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) - { - return parse_string(item, input_buffer); - } - /* number */ - if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) - { - return parse_number(item, input_buffer); - } - /* array */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) - { - return parse_array(item, input_buffer); - } - /* object */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) - { - return parse_object(item, input_buffer); - } - - return false; -} - -/* Render a value to text. */ -static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output = NULL; - - if ((item == NULL) || (output_buffer == NULL)) - { - return false; - } - - switch ((item->type) & 0xFF) - { - case cJSON_NULL: - output = ensure(output_buffer, 5); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "null"); - return true; - - case cJSON_False: - output = ensure(output_buffer, 6); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "false"); - return true; - - case cJSON_True: - output = ensure(output_buffer, 5); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "true"); - return true; - - case cJSON_Number: - return print_number(item, output_buffer); - - case cJSON_Raw: - { - size_t raw_length = 0; - if (item->valuestring == NULL) - { - return false; - } - - raw_length = strlen(item->valuestring) + sizeof(""); - output = ensure(output_buffer, raw_length); - if (output == NULL) - { - return false; - } - memcpy(output, item->valuestring, raw_length); - return true; - } - - case cJSON_String: - return print_string(item, output_buffer); - - case cJSON_Array: - return print_array(item, output_buffer); - - case cJSON_Object: - return print_object(item, output_buffer); - - default: - return false; - } -} - -/* Build an array from input text. */ -static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) -{ - cJSON *head = NULL; /* head of the linked list */ - cJSON *current_item = NULL; - - if (input_buffer->depth >= CJSON_NESTING_LIMIT) - { - return false; /* to deeply nested */ - } - input_buffer->depth++; - - if (buffer_at_offset(input_buffer)[0] != '[') - { - /* not an array */ - goto fail; - } - - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) - { - /* empty array */ - goto success; - } - - /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) - { - input_buffer->offset--; - goto fail; - } - - /* step back to character in front of the first element */ - input_buffer->offset--; - /* loop through the comma separated array elements */ - do - { - /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) - { - goto fail; /* allocation failure */ - } - - /* attach next item to list */ - if (head == NULL) - { - /* start the linked list */ - current_item = head = new_item; - } - else - { - /* add to the end and advance */ - current_item->next = new_item; - new_item->prev = current_item; - current_item = new_item; - } - - /* parse next value */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) - { - goto fail; /* failed to parse value */ - } - buffer_skip_whitespace(input_buffer); - } - while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - - if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') - { - goto fail; /* expected end of array */ - } - -success: - input_buffer->depth--; - - if (head != NULL) { - head->prev = current_item; - } - - item->type = cJSON_Array; - item->child = head; - - input_buffer->offset++; - - return true; - -fail: - if (head != NULL) - { - cJSON_Delete(head); - } - - return false; -} - -/* Render an array to text */ -static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - size_t length = 0; - cJSON *current_element = item->child; - - if (output_buffer == NULL) - { - return false; - } - - /* Compose the output array. */ - /* opening square bracket */ - output_pointer = ensure(output_buffer, 1); - if (output_pointer == NULL) - { - return false; - } - - *output_pointer = '['; - output_buffer->offset++; - output_buffer->depth++; - - while (current_element != NULL) - { - if (!print_value(current_element, output_buffer)) - { - return false; - } - update_offset(output_buffer); - if (current_element->next) - { - length = (size_t) (output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ','; - if(output_buffer->format) - { - *output_pointer++ = ' '; - } - *output_pointer = '\0'; - output_buffer->offset += length; - } - current_element = current_element->next; - } - - output_pointer = ensure(output_buffer, 2); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ']'; - *output_pointer = '\0'; - output_buffer->depth--; - - return true; -} - -/* Build an object from the text. */ -static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) -{ - cJSON *head = NULL; /* linked list head */ - cJSON *current_item = NULL; - - if (input_buffer->depth >= CJSON_NESTING_LIMIT) - { - return false; /* to deeply nested */ - } - input_buffer->depth++; - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) - { - goto fail; /* not an object */ - } - - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) - { - goto success; /* empty object */ - } - - /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) - { - input_buffer->offset--; - goto fail; - } - - /* step back to character in front of the first element */ - input_buffer->offset--; - /* loop through the comma separated array elements */ - do - { - /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) - { - goto fail; /* allocation failure */ - } - - /* attach next item to list */ - if (head == NULL) - { - /* start the linked list */ - current_item = head = new_item; - } - else - { - /* add to the end and advance */ - current_item->next = new_item; - new_item->prev = current_item; - current_item = new_item; - } - - /* parse the name of the child */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_string(current_item, input_buffer)) - { - goto fail; /* failed to parse name */ - } - buffer_skip_whitespace(input_buffer); - - /* swap valuestring and string, because we parsed the name */ - current_item->string = current_item->valuestring; - current_item->valuestring = NULL; - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) - { - goto fail; /* invalid object */ - } - - /* parse the value */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) - { - goto fail; /* failed to parse value */ - } - buffer_skip_whitespace(input_buffer); - } - while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) - { - goto fail; /* expected end of object */ - } - -success: - input_buffer->depth--; - - if (head != NULL) { - head->prev = current_item; - } - - item->type = cJSON_Object; - item->child = head; - - input_buffer->offset++; - return true; - -fail: - if (head != NULL) - { - cJSON_Delete(head); - } - - return false; -} - -/* Render an object to text. */ -static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - size_t length = 0; - cJSON *current_item = item->child; - - if (output_buffer == NULL) - { - return false; - } - - /* Compose the output: */ - length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - - *output_pointer++ = '{'; - output_buffer->depth++; - if (output_buffer->format) - { - *output_pointer++ = '\n'; - } - output_buffer->offset += length; - - while (current_item) - { - if (output_buffer->format) - { - size_t i; - output_pointer = ensure(output_buffer, output_buffer->depth); - if (output_pointer == NULL) - { - return false; - } - for (i = 0; i < output_buffer->depth; i++) - { - *output_pointer++ = '\t'; - } - output_buffer->offset += output_buffer->depth; - } - - /* print key */ - if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) - { - return false; - } - update_offset(output_buffer); - - length = (size_t) (output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ':'; - if (output_buffer->format) - { - *output_pointer++ = '\t'; - } - output_buffer->offset += length; - - /* print value */ - if (!print_value(current_item, output_buffer)) - { - return false; - } - update_offset(output_buffer); - - /* print comma if not last */ - length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - if (current_item->next) - { - *output_pointer++ = ','; - } - - if (output_buffer->format) - { - *output_pointer++ = '\n'; - } - *output_pointer = '\0'; - output_buffer->offset += length; - - current_item = current_item->next; - } - - output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); - if (output_pointer == NULL) - { - return false; - } - if (output_buffer->format) - { - size_t i; - for (i = 0; i < (output_buffer->depth - 1); i++) - { - *output_pointer++ = '\t'; - } - } - *output_pointer++ = '}'; - *output_pointer = '\0'; - output_buffer->depth--; - - return true; -} - -/* Get Array size/item / object item. */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) -{ - cJSON *child = NULL; - size_t size = 0; - - if (array == NULL) - { - return 0; - } - - child = array->child; - - while(child != NULL) - { - size++; - child = child->next; - } - - /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ - - return (int)size; -} - -static cJSON* get_array_item(const cJSON *array, size_t index) -{ - cJSON *current_child = NULL; - - if (array == NULL) - { - return NULL; - } - - current_child = array->child; - while ((current_child != NULL) && (index > 0)) - { - index--; - current_child = current_child->next; - } - - return current_child; -} - -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) -{ - if (index < 0) - { - return NULL; - } - - return get_array_item(array, (size_t)index); -} - -static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) -{ - cJSON *current_element = NULL; - - if ((object == NULL) || (name == NULL)) - { - return NULL; - } - - current_element = object->child; - if (case_sensitive) - { - while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) - { - current_element = current_element->next; - } - } - else - { - while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) - { - current_element = current_element->next; - } - } - - if ((current_element == NULL) || (current_element->string == NULL)) { - return NULL; - } - - return current_element; -} - -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) -{ - return get_object_item(object, string, false); -} - -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) -{ - return get_object_item(object, string, true); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) -{ - return cJSON_GetObjectItem(object, string) ? 1 : 0; -} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev, cJSON *item) -{ - prev->next = item; - item->prev = prev; -} - -/* Utility for handling references. */ -static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) -{ - cJSON *reference = NULL; - if (item == NULL) - { - return NULL; - } - - reference = cJSON_New_Item(hooks); - if (reference == NULL) - { - return NULL; - } - - memcpy(reference, item, sizeof(cJSON)); - reference->string = NULL; - reference->type |= cJSON_IsReference; - reference->next = reference->prev = NULL; - return reference; -} - -static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) -{ - cJSON *child = NULL; - - if ((item == NULL) || (array == NULL) || (array == item)) - { - return false; - } - - child = array->child; - /* - * To find the last item in array quickly, we use prev in array - */ - if (child == NULL) - { - /* list is empty, start new one */ - array->child = item; - item->prev = item; - item->next = NULL; - } - else - { - /* append to the end */ - if (child->prev) - { - suffix_object(child->prev, item); - array->child->prev = item; - } - } - - return true; -} - -/* Add item to array/object. */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) -{ - return add_item_to_array(array, item); -} - -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) - #pragma GCC diagnostic push -#endif -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif -/* helper function to cast away const */ -static void* cast_away_const(const void* string) -{ - return (void*)string; -} -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) - #pragma GCC diagnostic pop -#endif - - -static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) -{ - char *new_key = NULL; - int new_type = cJSON_Invalid; - - if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) - { - return false; - } - - if (constant_key) - { - new_key = (char*)cast_away_const(string); - new_type = item->type | cJSON_StringIsConst; - } - else - { - new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); - if (new_key == NULL) - { - return false; - } - - new_type = item->type & ~cJSON_StringIsConst; - } - - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) - { - hooks->deallocate(item->string); - } - - item->string = new_key; - item->type = new_type; - - return add_item_to_array(object, item); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) -{ - return add_item_to_object(object, string, item, &global_hooks, false); -} - -/* Add an item to an object with constant string as key */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) -{ - return add_item_to_object(object, string, item, &global_hooks, true); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) -{ - if (array == NULL) - { - return false; - } - - return add_item_to_array(array, create_reference(item, &global_hooks)); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) -{ - if ((object == NULL) || (string == NULL)) - { - return false; - } - - return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); -} - -CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) -{ - cJSON *null = cJSON_CreateNull(); - if (add_item_to_object(object, name, null, &global_hooks, false)) - { - return null; - } - - cJSON_Delete(null); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) -{ - cJSON *true_item = cJSON_CreateTrue(); - if (add_item_to_object(object, name, true_item, &global_hooks, false)) - { - return true_item; - } - - cJSON_Delete(true_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) -{ - cJSON *false_item = cJSON_CreateFalse(); - if (add_item_to_object(object, name, false_item, &global_hooks, false)) - { - return false_item; - } - - cJSON_Delete(false_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) -{ - cJSON *bool_item = cJSON_CreateBool(boolean); - if (add_item_to_object(object, name, bool_item, &global_hooks, false)) - { - return bool_item; - } - - cJSON_Delete(bool_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) -{ - cJSON *number_item = cJSON_CreateNumber(number); - if (add_item_to_object(object, name, number_item, &global_hooks, false)) - { - return number_item; - } - - cJSON_Delete(number_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) -{ - cJSON *string_item = cJSON_CreateString(string); - if (add_item_to_object(object, name, string_item, &global_hooks, false)) - { - return string_item; - } - - cJSON_Delete(string_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) -{ - cJSON *raw_item = cJSON_CreateRaw(raw); - if (add_item_to_object(object, name, raw_item, &global_hooks, false)) - { - return raw_item; - } - - cJSON_Delete(raw_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) -{ - cJSON *object_item = cJSON_CreateObject(); - if (add_item_to_object(object, name, object_item, &global_hooks, false)) - { - return object_item; - } - - cJSON_Delete(object_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) -{ - cJSON *array = cJSON_CreateArray(); - if (add_item_to_object(object, name, array, &global_hooks, false)) - { - return array; - } - - cJSON_Delete(array); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) -{ - if ((parent == NULL) || (item == NULL)) - { - return NULL; - } - - if (item != parent->child) - { - /* not the first element */ - item->prev->next = item->next; - } - if (item->next != NULL) - { - /* not the last element */ - item->next->prev = item->prev; - } - - if (item == parent->child) - { - /* first element */ - parent->child = item->next; - } - else if (item->next == NULL) - { - /* last element */ - parent->child->prev = item->prev; - } - - /* make sure the detached item doesn't point anywhere anymore */ - item->prev = NULL; - item->next = NULL; - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) -{ - if (which < 0) - { - return NULL; - } - - return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) -{ - cJSON_Delete(cJSON_DetachItemFromArray(array, which)); -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) -{ - cJSON *to_detach = cJSON_GetObjectItem(object, string); - - return cJSON_DetachItemViaPointer(object, to_detach); -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) -{ - cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); - - return cJSON_DetachItemViaPointer(object, to_detach); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); -} - -/* Replace array/object items with new ones. */ -CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) -{ - cJSON *after_inserted = NULL; - - if (which < 0 || newitem == NULL) - { - return false; - } - - after_inserted = get_array_item(array, (size_t)which); - if (after_inserted == NULL) - { - return add_item_to_array(array, newitem); - } - - if (after_inserted != array->child && after_inserted->prev == NULL) { - /* return false if after_inserted is a corrupted array item */ - return false; - } - - newitem->next = after_inserted; - newitem->prev = after_inserted->prev; - after_inserted->prev = newitem; - if (after_inserted == array->child) - { - array->child = newitem; - } - else - { - newitem->prev->next = newitem; - } - return true; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) -{ - if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) - { - return false; - } - - if (replacement == item) - { - return true; - } - - replacement->next = item->next; - replacement->prev = item->prev; - - if (replacement->next != NULL) - { - replacement->next->prev = replacement; - } - if (parent->child == item) - { - if (parent->child->prev == parent->child) - { - replacement->prev = replacement; - } - parent->child = replacement; - } - else - { /* - * To find the last item in array quickly, we use prev in array. - * We can't modify the last item's next pointer where this item was the parent's child - */ - if (replacement->prev != NULL) - { - replacement->prev->next = replacement; - } - if (replacement->next == NULL) - { - parent->child->prev = replacement; - } - } - - item->next = NULL; - item->prev = NULL; - cJSON_Delete(item); - - return true; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) -{ - if (which < 0) - { - return false; - } - - return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); -} - -static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) -{ - if ((replacement == NULL) || (string == NULL)) - { - return false; - } - - /* replace the name in the replacement */ - if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) - { - cJSON_free(replacement->string); - } - replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - if (replacement->string == NULL) - { - return false; - } - - replacement->type &= ~cJSON_StringIsConst; - - return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) -{ - return replace_item_in_object(object, string, newitem, false); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) -{ - return replace_item_in_object(object, string, newitem, true); -} - -/* Create basic types: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_NULL; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_True; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_False; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = boolean ? cJSON_True : cJSON_False; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Number; - item->valuedouble = num; - - /* use saturation in case of overflow */ - if (num >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (num <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)num; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_String; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - if(!item->valuestring) - { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) - { - item->type = cJSON_String | cJSON_IsReference; - item->valuestring = (char*)cast_away_const(string); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_Object | cJSON_IsReference; - item->child = (cJSON*)cast_away_const(child); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_Array | cJSON_IsReference; - item->child = (cJSON*)cast_away_const(child); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Raw; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); - if(!item->valuestring) - { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type=cJSON_Array; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) - { - item->type = cJSON_Object; - } - - return item; -} - -/* Create Arrays: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber(numbers[i]); - if (!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber((double)numbers[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber(numbers[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (strings == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for (i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateString(strings[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p,n); - } - p = n; - } - - if (a && a->child) { - a->child->prev = n; - } - - return a; -} - -/* Duplication */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) -{ - cJSON *newitem = NULL; - cJSON *child = NULL; - cJSON *next = NULL; - cJSON *newchild = NULL; - - /* Bail on bad ptr */ - if (!item) - { - goto fail; - } - /* Create new item */ - newitem = cJSON_New_Item(&global_hooks); - if (!newitem) - { - goto fail; - } - /* Copy over all vars */ - newitem->type = item->type & (~cJSON_IsReference); - newitem->valueint = item->valueint; - newitem->valuedouble = item->valuedouble; - if (item->valuestring) - { - newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); - if (!newitem->valuestring) - { - goto fail; - } - } - if (item->string) - { - newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); - if (!newitem->string) - { - goto fail; - } - } - /* If non-recursive, then we're done! */ - if (!recurse) - { - return newitem; - } - /* Walk the ->next chain for the child. */ - child = item->child; - while (child != NULL) - { - newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) - { - goto fail; - } - if (next != NULL) - { - /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - next->next = newchild; - newchild->prev = next; - next = newchild; - } - else - { - /* Set newitem->child and move to it */ - newitem->child = newchild; - next = newchild; - } - child = child->next; - } - if (newitem && newitem->child) - { - newitem->child->prev = newchild; - } - - return newitem; - -fail: - if (newitem != NULL) - { - cJSON_Delete(newitem); - } - - return NULL; -} - -static void skip_oneline_comment(char **input) -{ - *input += static_strlen("//"); - - for (; (*input)[0] != '\0'; ++(*input)) - { - if ((*input)[0] == '\n') { - *input += static_strlen("\n"); - return; - } - } -} - -static void skip_multiline_comment(char **input) -{ - *input += static_strlen("/*"); - - for (; (*input)[0] != '\0'; ++(*input)) - { - if (((*input)[0] == '*') && ((*input)[1] == '/')) - { - *input += static_strlen("*/"); - return; - } - } -} - -static void minify_string(char **input, char **output) { - (*output)[0] = (*input)[0]; - *input += static_strlen("\""); - *output += static_strlen("\""); - - - for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { - (*output)[0] = (*input)[0]; - - if ((*input)[0] == '\"') { - (*output)[0] = '\"'; - *input += static_strlen("\""); - *output += static_strlen("\""); - return; - } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { - (*output)[1] = (*input)[1]; - *input += static_strlen("\""); - *output += static_strlen("\""); - } - } -} - -CJSON_PUBLIC(void) cJSON_Minify(char *json) -{ - char *into = json; - - if (json == NULL) - { - return; - } - - while (json[0] != '\0') - { - switch (json[0]) - { - case ' ': - case '\t': - case '\r': - case '\n': - json++; - break; - - case '/': - if (json[1] == '/') - { - skip_oneline_comment(&json); - } - else if (json[1] == '*') - { - skip_multiline_comment(&json); - } else { - json++; - } - break; - - case '\"': - minify_string(&json, (char**)&into); - break; - - default: - into[0] = json[0]; - json++; - into++; - } - } - - /* and null-terminate. */ - *into = '\0'; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Invalid; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_False; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xff) == cJSON_True; -} - - -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & (cJSON_True | cJSON_False)) != 0; -} -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_NULL; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Number; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_String; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Array; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Object; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Raw; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) -{ - if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) - { - return false; - } - - /* check if type is valid */ - switch (a->type & 0xFF) - { - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - case cJSON_Number: - case cJSON_String: - case cJSON_Raw: - case cJSON_Array: - case cJSON_Object: - break; - - default: - return false; - } - - /* identical objects are equal */ - if (a == b) - { - return true; - } - - switch (a->type & 0xFF) - { - /* in these cases and equal type is enough */ - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - return true; - - case cJSON_Number: - if (compare_double(a->valuedouble, b->valuedouble)) - { - return true; - } - return false; - - case cJSON_String: - case cJSON_Raw: - if ((a->valuestring == NULL) || (b->valuestring == NULL)) - { - return false; - } - if (strcmp(a->valuestring, b->valuestring) == 0) - { - return true; - } - - return false; - - case cJSON_Array: - { - cJSON *a_element = a->child; - cJSON *b_element = b->child; - - for (; (a_element != NULL) && (b_element != NULL);) - { - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } - - a_element = a_element->next; - b_element = b_element->next; - } - - /* one of the arrays is longer than the other */ - if (a_element != b_element) { - return false; - } - - return true; - } - - case cJSON_Object: - { - cJSON *a_element = NULL; - cJSON *b_element = NULL; - cJSON_ArrayForEach(a_element, a) - { - /* TODO This has O(n^2) runtime, which is horrible! */ - b_element = get_object_item(b, a_element->string, case_sensitive); - if (b_element == NULL) - { - return false; - } - - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } - } - - /* doing this twice, once on a and b to prevent true comparison if a subset of b - * TODO: Do this the proper way, this is just a fix for now */ - cJSON_ArrayForEach(b_element, b) - { - a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) - { - return false; - } - - if (!cJSON_Compare(b_element, a_element, case_sensitive)) - { - return false; - } - } - - return true; - } - - default: - return false; - } -} - -CJSON_PUBLIC(void *) cJSON_malloc(size_t size) -{ - return global_hooks.allocate(size); -} - -CJSON_PUBLIC(void) cJSON_free(void *object) -{ - global_hooks.deallocate(object); -} diff --git a/app/src/main/cpp/cJSON/cJSON.h b/app/src/main/cpp/cJSON/cJSON.h deleted file mode 100644 index 218cc9e..0000000 --- a/app/src/main/cpp/cJSON/cJSON.h +++ /dev/null @@ -1,300 +0,0 @@ -/* - Copyright (c) 2009-2017 Dave Gamble and cJSON contributors - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef cJSON__h -#define cJSON__h - -#ifdef __cplusplus -extern "C" -{ -#endif - -#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) -#define __WINDOWS__ -#endif - -#ifdef __WINDOWS__ - -/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: - -CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols -CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) -CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol - -For *nix builds that support visibility attribute, you can define similar behavior by - -setting default visibility to hidden by adding --fvisibility=hidden (for gcc) -or --xldscope=hidden (for sun cc) -to CFLAGS - -then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does - -*/ - -#define CJSON_CDECL __cdecl -#define CJSON_STDCALL __stdcall - -/* export symbols by default, this is necessary for copy pasting the C and header file */ -#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) -#define CJSON_EXPORT_SYMBOLS -#endif - -#if defined(CJSON_HIDE_SYMBOLS) -#define CJSON_PUBLIC(type) type CJSON_STDCALL -#elif defined(CJSON_EXPORT_SYMBOLS) -#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL -#elif defined(CJSON_IMPORT_SYMBOLS) -#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL -#endif -#else /* !__WINDOWS__ */ -#define CJSON_CDECL -#define CJSON_STDCALL - -#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) -#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type -#else -#define CJSON_PUBLIC(type) type -#endif -#endif - -/* project version */ -#define CJSON_VERSION_MAJOR 1 -#define CJSON_VERSION_MINOR 7 -#define CJSON_VERSION_PATCH 17 - -#include - -/* cJSON Types: */ -#define cJSON_Invalid (0) -#define cJSON_False (1 << 0) -#define cJSON_True (1 << 1) -#define cJSON_NULL (1 << 2) -#define cJSON_Number (1 << 3) -#define cJSON_String (1 << 4) -#define cJSON_Array (1 << 5) -#define cJSON_Object (1 << 6) -#define cJSON_Raw (1 << 7) /* raw json */ - -#define cJSON_IsReference 256 -#define cJSON_StringIsConst 512 - -/* The cJSON structure: */ -typedef struct cJSON -{ - /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *next; - struct cJSON *prev; - /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ - struct cJSON *child; - - /* The type of the item, as above. */ - int type; - - /* The item's string, if type==cJSON_String and type == cJSON_Raw */ - char *valuestring; - /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ - int valueint; - /* The item's number, if type==cJSON_Number */ - double valuedouble; - - /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ - char *string; -} cJSON; - -typedef struct cJSON_Hooks -{ - /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ - void *(CJSON_CDECL *malloc_fn)(size_t sz); - void (CJSON_CDECL *free_fn)(void *ptr); -} cJSON_Hooks; - -typedef int cJSON_bool; - -/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. - * This is to prevent stack overflows. */ -#ifndef CJSON_NESTING_LIMIT -#define CJSON_NESTING_LIMIT 1000 -#endif - -/* returns the version of cJSON as a string */ -CJSON_PUBLIC(const char*) cJSON_Version(void); - -/* Supply malloc, realloc and free functions to cJSON */ -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); - -/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); -CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); -/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ -/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); -CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); - -/* Render a cJSON entity to text for transfer/storage. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. */ -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); -/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); -/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ -/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); -/* Delete a cJSON entity and all subentities. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); - -/* Returns the number of items in an array (or object). */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); -/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); -/* Get item "string" from object. Case insensitive. */ -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); -/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); - -/* Check item type and return its value */ -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); - -/* These functions check the type of an item */ -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); - -/* These calls create a cJSON item of the appropriate type. */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); -/* raw json */ -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); - -/* Create a string where valuestring references a string so - * it will not be freed by cJSON_Delete */ -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); -/* Create an object/array that only references it's elements so - * they will not be freed by cJSON_Delete */ -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); - -/* These utilities create an Array of count items. - * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); - -/* Append item to the specified array/object. */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); -/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. - * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before - * writing to `item->string` */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); -/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); - -/* Remove/Detach items from Arrays/Objects. */ -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); - -/* Update array items. */ -CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); - -/* Duplicate a cJSON item */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); -/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will - * need to be released. With recurse!=0, it will duplicate any children connected to the item. - * The item->next and ->prev pointers are always zero on return from Duplicate. */ -/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. - * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); - -/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. - * The input pointer json cannot point to a read-only address area, such as a string constant, - * but should point to a readable and writable address area. */ -CJSON_PUBLIC(void) cJSON_Minify(char *json); - -/* Helper functions for creating and adding items to an object at the same time. - * They return the added item or NULL on failure. */ -CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); -CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); -CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); -CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); - -/* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) -/* helper for the cJSON_SetNumberValue macro */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); -#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) -/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ -CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); - -/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ -#define cJSON_SetBoolValue(object, boolValue) ( \ - (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ - (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ - cJSON_Invalid\ -) - -/* Macro for iterating over an array or object */ -#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) - -/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ -CJSON_PUBLIC(void *) cJSON_malloc(size_t size); -CJSON_PUBLIC(void) cJSON_free(void *object); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/app/src/main/cpp/json.hpp b/app/src/main/cpp/json.hpp new file mode 100644 index 0000000..8b72ea6 --- /dev/null +++ b/app/src/main/cpp/json.hpp @@ -0,0 +1,24765 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // nullptr_t +#include // exception +#if JSON_DIAGNOSTICS + #include // accumulate +#endif +#include // runtime_error +#include // to_string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// #include + + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifndef JSON_HAS_STATIC_RTTI + #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 + #define JSON_HAS_STATIC_RTTI 1 + #else + #define JSON_HAS_STATIC_RTTI 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType, \ + class CustomBaseClass> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) +{ + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static JSON_INLINE_VARIABLE constexpr T value{}; +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple +#include // char_traits + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // random_access_iterator_tag + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); + +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector + + // #include + + + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN + + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> + class basic_json; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; + + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; + + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ + +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +///////////////// +// char_traits // +///////////////// + +// Primary template of char_traits calls std char_traits +template +struct char_traits : std::char_traits +{}; + +// Explicitly define char traits for unsigned char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = unsigned char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +// Explicitly define char traits for signed char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = signed char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + + static constexpr auto value = + conjunction < + is_constructible, + is_detected_exact>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template