From 1b2644ec377e7a1a21b3759e2deadf43c62c222c Mon Sep 17 00:00:00 2001 From: chiteroman <98092901+chiteroman@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:29:12 +0100 Subject: [PATCH] v12.1 --- app/build.gradle.kts | 24 +- app/src/main/cpp/Android.mk | 67 - app/src/main/cpp/Application.mk | 5 - app/src/main/cpp/CMakeLists.txt | 14 + app/src/main/cpp/Dobby/.clang-format | 18 + .../cpp/Dobby/.github/workflows/Builder.yaml | 114 + app/src/main/cpp/Dobby/.gitignore | 80 + .../cpp/Dobby/.vscode/c_cpp_properties.json | 74 + app/src/main/cpp/Dobby/.vscode/launch.json | 17 + app/src/main/cpp/Dobby/.vscode/settings.json | 121 + app/src/main/cpp/Dobby/.vscode/tags | 13 + app/src/main/cpp/Dobby/CMakeLists.txt | 407 ++ 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 | 57 + .../SymbolResolver/dobby_symbol_resolver.h | 15 + .../elf/dobby_symbol_resolver.cc | 238 + .../macho/dobby_symbol_resolver.cc | 116 + .../macho/dobby_symbol_resolver_priv.h | 6 + .../SymbolResolver/macho/macho_ctx.cc | 419 ++ .../SymbolResolver/macho/macho_ctx.h | 89 + .../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 | 64 + .../pe/dobby_symbol_resolver.cc | 26 + app/src/main/cpp/Dobby/cmake/Macros.cmake | 3 + app/src/main/cpp/Dobby/cmake/Util.cmake | 19 + .../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 ++ .../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 | 43 + .../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 | 15 + .../Dobby/external/logging/kernel_logging.cc | 28 + .../cpp/Dobby/external/logging/logging.cc | 137 + .../external/logging/logging/check_logging.h | 87 + .../Dobby/external/logging/logging/logging.h | 204 + .../external/logging/priv_headers/_simple.h | 162 + .../cpp/Dobby/external/osbase/CMakeLists.txt | 3 + .../main/cpp/{dobby => Dobby/include}/dobby.h | 0 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 | 57 + .../Darwin/ProcessRuntimeUtility.cc | 133 + .../PlatformUtil/ProcessRuntimeUtility.h | 25 + .../UnifiedInterface/exec_mem_placeholder.asm | 10 + .../UnifiedInterface/platform-darwin.cc | 106 + .../KernelMode/UnifiedInterface/platform.h | 26 + .../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 | 139 + .../Linux/ProcessRuntimeUtility.cc | 237 + .../PlatformUtil/ProcessRuntimeUtility.h | 26 + .../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/platform.h | 83 + .../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 | 14 + .../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 + .../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 | 57 + app/src/main/cpp/Dobby/source/dobby/pac_kit.h | 31 + .../source/dobby/platform_detect_macro.h | 15 + .../Dobby/source/dobby/platform_features.h | 51 + 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/dobby/x86/libdobby.a | Bin 149066 -> 0 bytes app/src/main/cpp/dobby/x86_64/libdobby.a | Bin 205344 -> 0 bytes app/src/main/cpp/main.cpp | 185 +- 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 | 96 - .../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 | 409 -- 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 | 910 --- .../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 - changelog.md | 4 +- update.json | 6 +- 348 files changed, 26723 insertions(+), 16306 deletions(-) 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/.github/workflows/Builder.yaml create mode 100644 app/src/main/cpp/Dobby/.gitignore create mode 100644 app/src/main/cpp/Dobby/.vscode/c_cpp_properties.json create mode 100644 app/src/main/cpp/Dobby/.vscode/launch.json create mode 100644 app/src/main/cpp/Dobby/.vscode/settings.json create mode 100644 app/src/main/cpp/Dobby/.vscode/tags 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/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/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/kernel_logging.cc 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/priv_headers/_simple.h create mode 100644 app/src/main/cpp/Dobby/external/osbase/CMakeLists.txt rename app/src/main/cpp/{dobby => Dobby/include}/dobby.h (100%) 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/PlatformUtil/ProcessRuntimeUtility.h create mode 100644 app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/exec_mem_placeholder.asm 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/KernelMode/UnifiedInterface/platform.h 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/ProcessRuntimeUtility.h 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/platform.h 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/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/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/dobby/x86/libdobby.a delete mode 100644 app/src/main/cpp/dobby/x86_64/libdobby.a 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 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7091b85..018f753 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,6 +18,26 @@ android { targetSdk = 34 versionCode = 1 versionName = "1.0" + + externalNativeBuild { + cmake { + arguments += "-DANDROID_STL=none" + arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel" + + cFlags += "-fvisibility=hidden" + cFlags += "-fvisibility-inlines-hidden" + cFlags += "-ffunction-sections" + cFlags += "-fdata-sections" + + cppFlags += "-std=c++20" + cppFlags += "-fno-exceptions" + cppFlags += "-fno-rtti" + cppFlags += "-fvisibility=hidden" + cppFlags += "-fvisibility-inlines-hidden" + cppFlags += "-ffunction-sections" + cppFlags += "-fdata-sections" + } + } } buildTypes { @@ -34,8 +54,8 @@ android { } externalNativeBuild { - ndkBuild { - path = file("src/main/cpp/Android.mk") + cmake { + path = file("src/main/cpp/CMakeLists.txt") } } diff --git a/app/src/main/cpp/Android.mk b/app/src/main/cpp/Android.mk deleted file mode 100644 index ebb3a13..0000000 --- a/app/src/main/cpp/Android.mk +++ /dev/null @@ -1,67 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -ifeq ($(TARGET_ARCH_ABI),x86) - include $(CLEAR_VARS) - LOCAL_MODULE := dobby - LOCAL_SRC_FILES := $(LOCAL_PATH)/dobby/x86/libdobby.a - LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/dobby - include $(PREBUILT_STATIC_LIBRARY) -endif - -ifeq ($(TARGET_ARCH_ABI),x86_64) - include $(CLEAR_VARS) - LOCAL_MODULE := dobby - LOCAL_SRC_FILES := $(LOCAL_PATH)/dobby/x86_64/libdobby.a - LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/dobby - include $(PREBUILT_STATIC_LIBRARY) -endif - -include $(CLEAR_VARS) -LOCAL_MODULE := zygisk -LOCAL_SRC_FILES := main.cpp -LOCAL_C_INCLUDES := $(LOCAL_PATH) - -ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/*.c) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm/*.c) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/common/*.c) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/third_party/*/*.c) -endif - -ifeq ($(TARGET_ARCH_ABI),arm64-v8a) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/*.c) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm64/*.c) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/common/*.c) - LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/third_party/*/*.c) -endif - -ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/include - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/common - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/xdl - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/bsd - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/lss -endif - -ifeq ($(TARGET_ARCH_ABI),arm64-v8a) - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/include - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm64 - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/common - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/xdl - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/bsd - LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/lss -endif - -ifeq ($(TARGET_ARCH_ABI),x86) - LOCAL_STATIC_LIBRARIES := dobby -endif - -ifeq ($(TARGET_ARCH_ABI),x86_64) - LOCAL_STATIC_LIBRARIES := dobby -endif - -LOCAL_LDLIBS := -llog -lstdc++ -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 445e560..0000000 --- a/app/src/main/cpp/Application.mk +++ /dev/null @@ -1,5 +0,0 @@ -APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 -APP_CFLAGS := -DNDEBUG -g0 -Oz -fvisibility=hidden -fvisibility-inlines-hidden -APP_CPPFLAGS := -fno-exceptions -fno-rtti -APP_STL := none -APP_PLATFORM := android-26 \ 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..a43cf91 --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.22.1) +project(zygisk) + +include_directories(D:/libcxx_build/jni/libcxx/include) + +link_libraries(D:/libcxx_build/obj/local/${CMAKE_ANDROID_ARCH_ABI}/libcxx.a) + +add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp) + +add_subdirectory(Dobby) + +SET_OPTION(Plugin.Android.BionicLinkerUtil ON) + +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE 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/.github/workflows/Builder.yaml b/app/src/main/cpp/Dobby/.github/workflows/Builder.yaml new file mode 100644 index 0000000..761ff86 --- /dev/null +++ b/app/src/main/cpp/Dobby/.github/workflows/Builder.yaml @@ -0,0 +1,114 @@ +name: Builder + +on: + push: + branches: + - master + +env: + CMAKE_VERSION: 3.25.2 + LLVM_VERSION: 15.0.6 + NDK_VERSION: r25b + +jobs: + delete_latest_release: + runs-on: ubuntu-latest + steps: + - name: checkout master + uses: actions/checkout@master + + - name: delete latest release + uses: dev-drprasad/delete-tag-and-release@v0.2.1 + with: + delete_release: true + tag_name: latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + linux_and_android: + runs-on: ubuntu-latest + needs: delete_latest_release + steps: + - name: checkout master + uses: actions/checkout@master + + - name: init linux cross compile env + run: | + sh scripts/setup_linux_cross_compile.sh + mkdir -p artifact + shell: bash + + - name: compile linux + run: | + python3 scripts/platform_builder.py --platform=linux --arch=all --cmake_dir=$HOME/opt/cmake-$CMAKE_VERSION --llvm_dir=$HOME/opt/llvm-$LLVM_VERSION + cp include/dobby.h build/linux + tar -zcvf build/dobby-linux-all.tar.gz build/linux + cp build/dobby-linux-all.tar.gz artifact/ + + shell: bash + + - name: compile android + run: | + python3 scripts/platform_builder.py --platform=android --arch=all --cmake_dir=$HOME/opt/cmake-$CMAKE_VERSION --llvm_dir=$HOME/opt/llvm-$LLVM_VERSION --android_ndk_dir=$HOME/opt/ndk-$NDK_VERSION + cp include/dobby.h build/android + tar -zcvf build/dobby-android-all.tar.gz build/android + cp build/dobby-android-all.tar.gz artifact/ + shell: bash + + - name: print output + run: | + ls -lha . + + - name: update release + uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: latest + body: "a lightweight, multi-platform, multi-architecture exploit hook framework" + artifacts: "build/dobby-linux-all.tar.gz,build/dobby-android-all.tar.gz" + allowUpdates: true + replacesArtifacts: true + + macos_and_iphoneos: + runs-on: macos-latest + needs: delete_latest_release + steps: + - name: checkout dev + uses: actions/checkout@master + + - name: init macos compile env + run: | + sh scripts/setup_macos_cross_compile.sh + mkdir -p artifact + shell: bash + + - name: compile macos + run: | + python3 scripts/platform_builder.py --platform=macos --arch=all --cmake_dir=$HOME/opt/cmake-$CMAKE_VERSION/CMake.app/Contents + cp include/dobby.h build/macos + tar -zcvf build/dobby-macos-all.tar.gz build/macos + cp build/dobby-macos-all.tar.gz artifact/ + + shell: bash + + - name: compile iphoneos + run: | + python3 scripts/platform_builder.py --platform=iphoneos --arch=all --cmake_dir=$HOME/opt/cmake-$CMAKE_VERSION/CMake.app/Contents + cp include/dobby.h build/iphoneos + tar -zcvf build/dobby-iphoneos-all.tar.gz build/iphoneos + cp build/dobby-iphoneos-all.tar.gz artifact/ + shell: bash + + - name: print output + run: | + ls -lha . + + - name: update release + uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: latest + body: "a lightweight, multi-platform, multi-architecture exploit hook framework" + artifacts: "build/dobby-macos-all.tar.gz,build/dobby-iphoneos-all.tar.gz" + allowUpdates: true + replacesArtifacts: true \ 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/.vscode/c_cpp_properties.json b/app/src/main/cpp/Dobby/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c624a58 --- /dev/null +++ b/app/src/main/cpp/Dobby/.vscode/c_cpp_properties.json @@ -0,0 +1,74 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "/usr/local/include", + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1", + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/local/include", + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "macFrameworkPath": [ + "/System/Library/Frameworks", + "/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c11", + "cppStandard": "c++11", + "configurationProvider": "vector-of-bool.cmake-tools", + "compileCommands": "${workspaceRoot}/ninja-build/compile_commands.json" + }, + { + "name": "Linux", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "Win32", + "includePath": [ + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + } + ], + "version": 4 +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/.vscode/launch.json b/app/src/main/cpp/Dobby/.vscode/launch.json new file mode 100644 index 0000000..36ab19f --- /dev/null +++ b/app/src/main/cpp/Dobby/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(lldb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "enter program name, for example ${workspaceRoot}/a.out", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceRoot}", + "environment": [], + "externalConsole": true, + "MIMode": "lldb" + } + ] +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/.vscode/settings.json b/app/src/main/cpp/Dobby/.vscode/settings.json new file mode 100644 index 0000000..e925cff --- /dev/null +++ b/app/src/main/cpp/Dobby/.vscode/settings.json @@ -0,0 +1,121 @@ +{ + "files.autoSave": "onFocusChange", + "files.autoSaveDelay": 3000, + "editor.formatOnSave": true, + "cmake.environment": { + "ANDROID_NDK": "/Users/jmpews/Library/Android/sdk/ndk-bundle" + }, + "cmake.configureArgs": [ + "-DCMAKE_SYSTEM_NAME=Android", + "-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a", + "-DCMAKE_ANDROID_NDK=/Users/jmpews/Library/Android/sdk/ndk/21.3.6528147", + "-DCMAKE_SYSTEM_VERSION=16", + "-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang" + ], + "cmake.buildArgs": [], + "cmake.buildToolArgs": [], + "cmake.parallelJobs": 1, + "files.associations": { + "stack": "cpp", + "regex": "cpp", + "bitset": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "__bit_reference": "cpp", + "__functional_base": "cpp", + "algorithm": "cpp", + "atomic": "cpp", + "chrono": "cpp", + "deque": "cpp", + "optional": "cpp", + "limits": "cpp", + "locale": "cpp", + "ratio": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "vector": "cpp", + "utility": "cpp", + "__functional_03": "cpp", + "__locale": "cpp", + "__hash_table": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "hash_map": "cpp", + "hash_set": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "string_view": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "initializer_list": "cpp", + "hashtable": "cpp", + "__config": "cpp", + "__nullptr": "cpp", + "cstddef": "cpp", + "exception": "cpp", + "new": "cpp", + "stdexcept": "cpp", + "typeinfo": "cpp", + "*.tcc": "cpp", + "xstring": "cpp", + "xlocmon": "cpp", + "xtr1common": "cpp", + "list": "cpp", + "xhash": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "iosfwd": "cpp", + "__debug": "cpp", + "__mutex_base": "cpp", + "__string": "cpp", + "__threading_support": "cpp", + "__tuple": "cpp", + "cctype": "cpp", + "cstdarg": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "ios": "cpp", + "iostream": "cpp", + "istream": "cpp", + "mutex": "cpp", + "ostream": "cpp", + "streambuf": "cpp", + "cmath": "cpp", + "array": "cpp", + "fstream": "cpp", + "stdio.h": "c", + "__functional_base_03": "cpp", + "filesystem": "cpp", + "queue": "cpp", + "random": "cpp", + "__errc": "cpp", + "__node_handle": "cpp", + "bit": "cpp", + "complex": "cpp", + "iomanip": "cpp", + "sstream": "cpp", + "stdarg.h": "c", + "clocale": "cpp", + "codecvt": "cpp", + "condition_variable": "cpp", + "numeric": "cpp", + "shared_mutex": "cpp", + "thread": "cpp", + "memory_resource": "cpp", + "cinttypes": "cpp", + "shared_cache_internal.h": "c", + "coroutine": "cpp", + "__bits": "cpp" + }, + "C_Cpp.configurationWarnings": "Disabled", + "lldb.showDisassembly": "auto", + "lldb.dereferencePointers": true, +} \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/.vscode/tags b/app/src/main/cpp/Dobby/.vscode/tags new file mode 100644 index 0000000..db0ba7e --- /dev/null +++ b/app/src/main/cpp/Dobby/.vscode/tags @@ -0,0 +1,13 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.8 // +HookZzCodeSegment ../tools/zzsolidifyhook.py /^HookZzCodeSegment = [$/;" kind:variable line:4 +lief ../tools/zzsolidifyhook.py /^import lief$/;" kind:namespace line:2 +new_target_file ../tools/zzsolidifyhook.py /^new_target_file = "\/Users\/jmpews\/Desktop\/test\/test.hook.dylib"$/;" kind:variable line:31 +target_file ../tools/zzsolidifyhook.py /^target_file = "\/Users\/jmpews\/Desktop\/test\/test.dylib"$/;" kind:variable line:30 +zz_macho_get_segment_with_name ../tools/zzsolidifyhook.py /^def zz_macho_get_segment_with_name(target_parsed, seg_name):$/;" kind:function line:13 +zz_macho_insert_segment ../tools/zzsolidifyhook.py /^def zz_macho_insert_segment(target_file, new_target_file):$/;" kind:function line:20 +zzsolidifyhook.py ../tools/zzsolidifyhook.py 1;" kind:file line:1 diff --git a/app/src/main/cpp/Dobby/CMakeLists.txt b/app/src/main/cpp/Dobby/CMakeLists.txt new file mode 100644 index 0000000..2e6cbd4 --- /dev/null +++ b/app/src/main/cpp/Dobby/CMakeLists.txt @@ -0,0 +1,407 @@ +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 DOBBY_BUILD_KERNEL_MODE) + include_directories( + source/Backend/KernelMode + ) +else () + 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 DOBBY_BUILD_KERNEL_MODE) + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + # platform util + source/Backend/KernelMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc + + # kernel mode - platform interface + source/Backend/KernelMode/UnifiedInterface/platform-darwin.cc + source/Backend/KernelMode/UnifiedInterface/exec_mem_placeholder.asm + + # kernel mode - executable memory + source/Backend/KernelMode/ExecMemory/code-patch-tool-darwin.cc + source/Backend/KernelMode/ExecMemory/clear-cache-tool-all.c + ) +elseif (SYSTEM.Darwin) + set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST} + # platform util + source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc + + # user mode - platform interface + source/Backend/UserMode/UnifiedInterface/platform-posix.cc + + # user mode - executable memory + 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} + # platform util + source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc + + # user mode - platform interface + source/Backend/UserMode/UnifiedInterface/platform-posix.cc + + # user mode - executable memory + 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} + # platform util + source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc + + # user mode - platform interface + source/Backend/UserMode/UnifiedInterface/platform-windows.cc + + # user mode - executable memory + 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}") + +# --- + +add_library(dobby SHARED + ${dobby.HEADER_FILE_LIST} + ${dobby.SOURCE_FILE_LIST} + ${logging.SOURCE_FILE_LIST} + ${misc_helper.SOURCE_FILE_LIST} + ${dobby.plugin.SOURCE_FILE_LIST} + ) + +target_include_directories(dobby PUBLIC + include + ) + +# --- + +add_library(dobby_static STATIC + ${dobby.HEADER_FILE_LIST} + ${dobby.SOURCE_FILE_LIST} + ${logging.SOURCE_FILE_LIST} + ${misc_helper.SOURCE_FILE_LIST} + ${dobby.plugin.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..f3c9f0b --- /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..2bf6a6a --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/CMakeLists.txt @@ -0,0 +1,57 @@ +set(SOURCE_FILE_LIST) + +if (NOT DEFINED DOBBY_DIR) + message(FATAL_ERROR "DOBBY_DIR must be set!") +endif () + +if (SYSTEM.Darwin AND (NOT DOBBY_BUILD_KERNEL_MODE)) + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + ${CMAKE_CURRENT_SOURCE_DIR}/macho/macho_ctx.cc + ${CMAKE_CURRENT_SOURCE_DIR}/macho/shared_cache_ctx.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/macho/dobby_symbol_resolver.cc + + + ${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc + ) +endif () +if (SYSTEM.Darwin AND DOBBY_BUILD_KERNEL_MODE) + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + ${CMAKE_CURRENT_SOURCE_DIR}/macho/dobby_symbol_resolver.cc + + ${DOBBY_DIR}/source/Backend/KernelMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc + ) +endif () +if (SYSTEM.Linux OR SYSTEM.Android) + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + ${CMAKE_CURRENT_SOURCE_DIR}/elf/dobby_symbol_resolver.cc + + ${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc + ) +endif () +if (SYSTEM.Windows) + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + ${CMAKE_CURRENT_SOURCE_DIR}/pe/dobby_symbol_resolver.cc + + ${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc + ) +endif () + +add_library(macho_ctx_kit + macho/macho_ctx.h + macho/macho_ctx.cc + ) + +add_library(shared_cache_ctx_kit + macho/shared_cache_ctx.h + macho/shared_cache_ctx.cpp + ) + +add_library(dobby_symbol_resolver + ${SOURCE_FILE_LIST} + ) + + +include_directories( + . +) + 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..5fe236c --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver.cc @@ -0,0 +1,116 @@ +#include "dobby_symbol_resolver.h" +#include "macho/dobby_symbol_resolver_priv.h" + +#include "dobby/common.h" + +#include +#include + +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include "macho_ctx.h" +#include "shared_cache_ctx.h" + +#if defined(BUILDING_KERNEL) +#else + +#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 + + result = macho_symbol_resolve(header, 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 ctx; + macho_ctx_init(&ctx, dyld_header, true); + result = (uintptr_t)macho_ctx_symbol_resolve(&ctx, 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..05b55a8 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.cc @@ -0,0 +1,419 @@ +#include "macho_ctx.h" + +#include "string.h" +#include "SymbolResolver/mmap_file_util.h" + +#include +#include +#include +#include + +#define ASSERT(x) + +void macho_ctx_init(macho_ctx_t *ctx, mach_header_t *header, bool is_runtime_mode) { + memset(ctx, 0, sizeof(macho_ctx_t)); + + ctx->is_runtime_mode = is_runtime_mode; + + ctx->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 + ctx->segments[ctx->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 == false) { + // 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; + } + + ctx->text_seg = text_segment; + ctx->text_exec_seg = text_exec_segment; + ctx->data_seg = data_segment; + ctx->data_const_seg = data_const_segment; + ctx->linkedit_seg = linkedit_segment; + + ctx->symtab_cmd = symtab_cmd; + ctx->dysymtab_cmd = dysymtab_cmd; + ctx->dyld_info_cmd = dyld_info_cmd; + ctx->exports_trie_cmd = exports_trie_cmd; + ctx->chained_fixups_cmd = chained_fixups_cmd; + + ctx->slide = slide; + ctx->linkedit_base = linkedit_base; + + ctx->symtab = (nlist_t *)(ctx->linkedit_base + ctx->symtab_cmd->symoff); + ctx->strtab = (char *)(ctx->linkedit_base + ctx->symtab_cmd->stroff); + ctx->indirect_symtab = (uint32_t *)(ctx->linkedit_base + ctx->dysymtab_cmd->indirectsymoff); +} + +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; +} + +uintptr_t macho_ctx_iterate_symbol_table(macho_ctx_t *ctx, const char *symbol_name_pattern) { + nlist_t *symtab = ctx->symtab; + uint32_t symtab_count = ctx->symtab_cmd->nsyms; + char *strtab = ctx->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_iterate_exported_symbol(macho_ctx_t *ctx, const char *symbol_name, uint64_t *out_flags) { + if (ctx->text_seg == NULL || ctx->linkedit_seg == NULL) { + return 0; + } + + struct dyld_info_command *dyld_info_cmd = ctx->dyld_info_cmd; + struct linkedit_data_command *exports_trie_cmd = ctx->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 *)(ctx->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_symbol_resolve_options(macho_ctx_t *ctx, const char *symbol_name_pattern, + resolve_symbol_type_t type) { + if (type & RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE) { + uintptr_t result = macho_ctx_iterate_symbol_table(ctx, symbol_name_pattern); + if (result) { + result = result + (ctx->is_runtime_mode ? ctx->slide : 0); + return result; + } + } + + if (type & RESOLVE_SYMBOL_TYPE_EXPORTED) { + // binary exported table(uleb128) + uint64_t flags; + uintptr_t result = macho_ctx_iterate_exported_symbol(ctx, symbol_name_pattern, &flags); + if (result) { + switch (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: { + result += (uintptr_t)ctx->header; + } break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: { + result += (uintptr_t)ctx->header; + } break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: { + } break; + default: + break; + } + return result; + } + } + return 0; +} + +uintptr_t macho_ctx_symbol_resolve(macho_ctx_t *ctx, const char *symbol_name_pattern) { + return macho_ctx_symbol_resolve_options(ctx, symbol_name_pattern, RESOLVE_SYMBOL_TYPE_ALL); +} + +uintptr_t macho_symbol_resolve_options(mach_header_t *header, const char *symbol_name_pattern, + resolve_symbol_type_t type) { + macho_ctx_t ctx; + macho_ctx_init(&ctx, header, true); + return macho_ctx_symbol_resolve_options(&ctx, symbol_name_pattern, type); +} + +uintptr_t macho_symbol_resolve(mach_header_t *header, const char *symbol_name_pattern) { + return macho_symbol_resolve_options(header, symbol_name_pattern, RESOLVE_SYMBOL_TYPE_ALL); +} + +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; + macho_ctx_init(&ctx, header, false); + return macho_ctx_symbol_resolve_options(&ctx, 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) { + 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_ctx.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.h new file mode 100644 index 0000000..e41d1cc --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/macho_ctx.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#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 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct macho_ctx { + bool is_runtime_mode; + + mach_header_t *header; + + 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; +} macho_ctx_t; + +intptr_t read_sleb128(const uint8_t **pp, const uint8_t *end); + +uintptr_t read_uleb128(const uint8_t **pp, const uint8_t *end); + +void macho_ctx_init(macho_ctx_t *ctx, mach_header_t *header, bool is_runtime_mode); + +uintptr_t macho_ctx_iterate_symbol_table(macho_ctx_t *ctx, const char *symbol_name_pattern); + +uintptr_t macho_ctx_iterate_exported_symbol(macho_ctx_t *ctx, const char *symbol_name, uint64_t *out_flags); + +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; + +uintptr_t macho_ctx_symbol_resolve_options(macho_ctx_t *ctx, const char *symbol_name_pattern, + resolve_symbol_type_t type); + +uintptr_t macho_ctx_symbol_resolve(macho_ctx_t *ctx, const char *symbol_name_pattern); + +uintptr_t macho_symbol_resolve_options(mach_header_t *header, const char *symbol_name_pattern, + resolve_symbol_type_t type); + +uintptr_t macho_symbol_resolve(mach_header_t *header, const char *symbol_name_pattern); + +uintptr_t macho_iterate_symbol_table(char *name_pattern, nlist_t *symtab, uint32_t symtab_count, char *strtab); + +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); + +#ifdef __cplusplus +} +#endif 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..6439274 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/mmap_file_util.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +class MmapFileManager { + const char *file_; + uint8_t *mmap_buffer_; + size_t mmap_buffer_size_; + +public: + 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 _size, off_t _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, _size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, _off); + if (mmap_buffer == MAP_FAILED) { + // printf("mmap %s failed\n", file_); + return NULL; + } + + close(fd); + + mmap_buffer_ = mmap_buffer; + mmap_buffer_size_ = _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..6a722a2 --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/Util.cmake @@ -0,0 +1,19 @@ +# 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() 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..9e7a67f --- /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(BUILDING_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..9fc1754 --- /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") + +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/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..5f8a6f7 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/new.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_NEW_H +#define TINYSTL_NEW_H + +#include + +namespace tinystl { + + struct placeholder {}; +} + +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..fabc633 --- /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..a11dfa9 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/CMakeLists.txt @@ -0,0 +1,15 @@ +include_directories(.) + +if(NOT DOBBY_BUILD_KERNEL_MODE) + set(SOURCE_FILE_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/logging.cc + ) +else() + set(SOURCE_FILE_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/kernel_logging.cc + ) +endif() +add_library(logging + ${SOURCE_FILE_LIST} + ${SOURCE_HEADER_LIST} +) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/external/logging/kernel_logging.cc b/app/src/main/cpp/Dobby/external/logging/kernel_logging.cc new file mode 100644 index 0000000..996f9f9 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/kernel_logging.cc @@ -0,0 +1,28 @@ +#include "logging/logging.h" + +#include +#include "utility_macro.h" + +#if defined(BUILDING_KERNEL) +#define abort() +#else +#include +#endif + +static int _log_level = 1; +PUBLIC void log_set_level(int level) { + _log_level = level; +} + +PUBLIC int log_internal_impl(int level, const char *fmt, ...) { + if (level < _log_level) + return 0; + + va_list ap; + va_start(ap, fmt); + + vprintf(fmt, ap); + + va_end(ap); + return 0; +} 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..99ec360 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/logging/logging.h @@ -0,0 +1,204 @@ +#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: + const char *log_tag_; + + const char *log_file_; +#if defined(USE_CXX_FILESTREAM) + std::fstream *log_file_stream_; +#else + FILE *log_file_stream_; +#endif + + LogLevel log_level_; + + 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__); \ + abort(); \ + } 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/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/dobby.h b/app/src/main/cpp/Dobby/include/dobby.h similarity index 100% rename from app/src/main/cpp/dobby/dobby.h rename to app/src/main/cpp/Dobby/include/dobby.h 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..636c413 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/code-patch-tool-darwin.cc @@ -0,0 +1,57 @@ +#include "dobby/dobby_internal.h" + +#include "PlatformUnifiedInterface/ExecMemory/ClearCacheTool.h" + +#include +#include +#include +#include + +#undef max +#undef min +#include + +#define DobbySymbolResolverAuth(o_var, name) \ + do { \ + static void *func_ptr = nullptr; \ + if (func_ptr == nullptr) { \ + func_ptr = DobbySymbolResolver(nullptr, name); \ + if (func_ptr) { \ + func_ptr = ptrauth_strip((void *)func_ptr, ptrauth_key_asia); \ + func_ptr = ptrauth_sign_unauthenticated(func_ptr, ptrauth_key_asia, 0); \ + } \ + } \ + o_var = (typeof(o_var))func_ptr; \ + } while (0); + +#define KERN_RETURN_ERROR(kr, failure) \ + do { \ + if (kr != KERN_SUCCESS) { \ + ERROR_LOG("mach error: %d", kr); \ + return failure; \ + } \ + } while (0); + +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 kMemoryOperationError; + } + + kern_return_t kr; + + { + paddr_t dst_paddr = pmap_kit_kvtophys(kernel_pmap, (vaddr_t)address); + paddr_t src_paddr = pmap_kit_kvtophys(kernel_pmap, (vaddr_t)buffer); + pmap_kit_bcopy_phys((addr_t)buffer, dst_paddr, buffer_size, cppvPsnk); + DEBUG_LOG("bcopy_phys: src: %p, dst: %p", src_paddr, dst_paddr); + + pmap_kit_kva_to_pte(kernel_pmap, (vaddr_t)address); + pmap_kit_set_perm(kernel_pmap, (vaddr_t)address, (vaddr_t)address + PAGE_SIZE, VM_PROT_READ | VM_PROT_EXECUTE); + + if (memcmp(address, buffer, buffer_size)) + return kMemoryOperationError; + } + + return 0; +} 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..3635c7b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc @@ -0,0 +1,133 @@ +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include + +typedef struct _loaded_kext_summary { + char name[KMOD_MAX_NAME]; + uuid_t uuid; + uint64_t address; + uint64_t size; + uint64_t version; + uint32_t loadTag; + uint32_t flags; + uint64_t reference_list; + uint64_t text_exec_address; + size_t text_exec_size; +} OSKextLoadedKextSummary; +typedef struct _loaded_kext_summary_header { + uint32_t version; + uint32_t entry_size; + uint32_t numSummaries; + uint32_t reserved; /* explicit alignment for gdb */ + OSKextLoadedKextSummary summaries[0]; +} OSKextLoadedKextSummaryHeader; + +#undef min +#undef max +#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 + +// Generate the name for an offset. +#define KERN_PARAM_OFFSET(type_, member_) __##type_##__##member_##__offset_ +#define KERN_STRUCT_OFFSET KERN_PARAM_OFFSET + +struct vm_map_links { + struct vm_map_entry *prev; + struct vm_map_entry *next; + vm_map_offset_t start; + vm_map_offset_t end; +}; + +struct vm_map_header { + struct vm_map_links links; + uint8_t placeholder_[]; +}; + +static inline vm_map_offset_t vme_start(vm_map_entry_t entry) { + uint KERN_STRUCT_OFFSET(vm_map_entry, links) = 0; + return ((vm_map_header *)((addr_t)entry + KERN_STRUCT_OFFSET(vm_map_entry, links)))->links.start; +} +static inline vm_map_entry_t vm_map_to_entry(vm_map_t map) { + return nullptr; +} +static inline vm_map_entry_t vm_map_first_entry(vm_map_t map) { + uint KERN_STRUCT_OFFSET(vm_map, hdr) = 4; + return ((vm_map_header *)((addr_t)map + KERN_STRUCT_OFFSET(vm_map, hdr)))->links.next; +} + +// --- + +static tinystl::vector regions; +const tinystl::vector &ProcessRuntimeUtility::GetProcessMemoryLayout() { + return regions; +} + +// --- + +#include + +extern "C" void *kernel_info_load_base(); +; + +tinystl::vector modules; +const tinystl::vector *ProcessRuntimeUtility::GetProcessModuleMap() { + modules.clear(); + + // brute force kernel base ? so rude :) + static void *kernel_base = nullptr; + static OSKextLoadedKextSummaryHeader *_gLoadedKextSummaries = nullptr; + if (kernel_base == nullptr) { + kernel_base = kernel_info_load_base(); + if (kernel_base == nullptr) { + ERROR_LOG("kernel base not found"); + return &modules; + } + DEBUG_LOG("kernel base at: %p", kernel_base); + + extern void *DobbyMachOSymbolResolver(void *header_, const char *symbol_name); + OSKextLoadedKextSummaryHeader **_gLoadedKextSummariesPtr; + _gLoadedKextSummariesPtr = + (typeof(_gLoadedKextSummariesPtr))DobbyMachOSymbolResolver(kernel_base, "_gLoadedKextSummaries"); + if (_gLoadedKextSummariesPtr == nullptr) { + ERROR_LOG("failed resolve gLoadedKextSummaries symbol"); + return &modules; + } + _gLoadedKextSummaries = *_gLoadedKextSummariesPtr; + DEBUG_LOG("gLoadedKextSummaries at: %p", _gLoadedKextSummaries); + } + + // only kernel + RuntimeModule module = {0}; + strncpy(module.path, "kernel", sizeof(module.path) - 1); + module.load_address = (void *)kernel_base; + modules.push_back(module); + + // kext + for (int i = 0; i < _gLoadedKextSummaries->numSummaries; ++i) { + strncpy(module.path, _gLoadedKextSummaries->summaries[i].name, sizeof(module.path) - 1); + module.load_address = (void *)_gLoadedKextSummaries->summaries[i].address; + modules.push_back(module); + } + + return &modules; +} + +RuntimeModule ProcessRuntimeUtility::GetProcessModule(const char *name) { + const tinystl::vector *modules = GetProcessModuleMap(); + return RuntimeModule{0}; +} diff --git a/app/src/main/cpp/Dobby/source/Backend/KernelMode/PlatformUtil/ProcessRuntimeUtility.h b/app/src/main/cpp/Dobby/source/Backend/KernelMode/PlatformUtil/ProcessRuntimeUtility.h new file mode 100644 index 0000000..af2df8e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/PlatformUtil/ProcessRuntimeUtility.h @@ -0,0 +1,25 @@ +#pragma once + +#include "PlatformUnifiedInterface/MemoryAllocator.h" + +#include "UnifiedInterface/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/Backend/KernelMode/UnifiedInterface/exec_mem_placeholder.asm b/app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/exec_mem_placeholder.asm new file mode 100644 index 0000000..137da1e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/exec_mem_placeholder.asm @@ -0,0 +1,10 @@ +#include + +#define PAGE_SHIFT 14 +.align PAGE_SHIFT + + .globl EXT(kernel_executable_memory_placeholder) +EXT(kernel_executable_memory_placeholder): +.rept 0x4000/4 +.long 0x41414141 +.endr \ No newline at end of file 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..f0c217d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/platform-darwin.cc @@ -0,0 +1,106 @@ +#include "UnifiedInterface/platform.h" + +#include +#include +#include +#include + +// ================================================================ +// base :: OSMemory + +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(0x4000); +} + +void *OSMemory::Allocate(size_t size, MemoryPermission access) { + return OSMemory::Allocate(size, access, nullptr); +} + +extern "C" void *kernel_executable_memory_placeholder; +void *OSMemory::Allocate(size_t size, MemoryPermission access, void *fixed_address) { + int prot = GetProtectionFromMemoryPermission(access); + + void *addr = nullptr; + int flags = VM_FLAGS_ANYWHERE; + if (fixed_address != nullptr) { + flags = VM_FLAGS_FIXED; + addr = fixed_address; + } + + // fixme: wire at pmap + if (prot & PROT_EXEC || prot == PROT_NONE) { + addr = &kernel_executable_memory_placeholder; + } else { + kern_return_t ret = mach_vm_allocate(kernel_map, (mach_vm_address_t *)&addr, size, flags); + if (ret != KERN_SUCCESS) { + panic("mach_vm_allocate"); + return nullptr; + } + ret = vm_map_wire(kernel_map, (mach_vm_address_t)addr, (mach_vm_address_t)addr + size, PROT_NONE, false); + if (ret != KERN_SUCCESS) { + panic("vm_map_wire"); + return nullptr; + } + + // make fault before at rw prot + bzero(addr, size); + { memcpy(addr, "AAAAAAAA", 8); } + + if (access == kNoAccess) { + access = kReadExecute; + } + if (!OSMemory::SetPermission((void *)addr, size, access)) { + OSMemory::Free(addr, size); + return nullptr; + } + + { + if (memcmp(addr, "AAAAAAAA", 8) != 0) { + return nullptr; + } + } + } + + return addr; +} + +bool OSMemory::Free(void *address, size_t size) { + DCHECK_EQ(0, reinterpret_cast(address) % PageSize()); + DCHECK_EQ(0, size % PageSize()); + + auto ret = mach_vm_deallocate(kernel_map, (mach_vm_address_t)address, size); + return ret == KERN_SUCCESS; +} + +bool OSMemory::Release(void *address, size_t size) { + DCHECK_EQ(0, reinterpret_cast(address) % PageSize()); + DCHECK_EQ(0, size % PageSize()); + + auto ret = mach_vm_deallocate(kernel_map, (mach_vm_address_t)address, size); + return ret == KERN_SUCCESS; +} + +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); + auto ret = mach_vm_protect(kernel_map, (mach_vm_address_t)address, size, false, prot); + return ret == KERN_SUCCESS; +} diff --git a/app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/platform.h b/app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/platform.h new file mode 100644 index 0000000..5d8a879 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/UnifiedInterface/platform.h @@ -0,0 +1,26 @@ +#ifndef PLATFORM_INTERFACE_COMMON_PLATFORM_H +#define PLATFORM_INTERFACE_COMMON_PLATFORM_H + +#include "dobby/common.h" + +// ================================================================ +// base :: OSMemory + +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); +}; + +#endif 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..1046221 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc @@ -0,0 +1,139 @@ +#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() { + 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/ProcessRuntimeUtility.h b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/ProcessRuntimeUtility.h new file mode 100644 index 0000000..f861d7a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/ProcessRuntimeUtility.h @@ -0,0 +1,26 @@ +#pragma once + +#include "PlatformUnifiedInterface/MemoryAllocator.h" + +#include "UnifiedInterface/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/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..f7928e5 --- /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 "UnifiedInterface/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..fd36108 --- /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 "UnifiedInterface/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/platform.h b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform.h new file mode 100644 index 0000000..62bfe8f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +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/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..249d75b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.h @@ -0,0 +1,14 @@ +#pragma once + +#include "dobby/common.h" + +#include "MemoryAllocator/AssemblyCodeBuilder.h" + +#include "x86_insn_decode/x86_insn_decode.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/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..6206267 --- /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/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..c843796 --- /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 __DOBBY_BUILD_VERSION__; +} + +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..26e9912 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/dobby_internal.h @@ -0,0 +1,13 @@ +#pragma once + +#include "dobby/common.h" + +#include "UnifiedInterface/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..66d7a1a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/kernel_mode_header.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *vm_map_entry_t; +extern vm_map_t kernel_map; + +typedef void *pmap_paddr_t; +struct pmap; +typedef struct pmap *pmap_t; +extern pmap_t kernel_pmap; + +extern task_t kernel_task; + +#ifdef __cplusplus +} +#endif + +// ----- pmap ----- + +typedef void *pmap_paddr_t; +struct pmap; +typedef struct pmap *pmap_t; + +typedef uint64_t vaddr_t; +typedef uint64_t paddr_t; + +struct pmap; +typedef struct pmap *pmap_t; + +#ifdef __cplusplus +extern "C" { +#endif + +extern pmap_t kernel_pmap; + +void pmap_kit_init(); + +paddr_t pmap_kit_kvtophys(pmap_t pmap, vaddr_t va); + +int pmap_kit_set_perm(pmap_t pmap, vaddr_t start, vaddr_t end, unsigned int prot); + +#define cppvPsnk 1 +#define cppvPsrc 2 +void pmap_kit_bcopy_phys(paddr_t src, paddr_t dst, size_t size, int flags); + +typedef uint64_t pt_entry_t; +pt_entry_t pmap_kit_kva_to_pte(pmap_t pmap, vaddr_t va); + +#ifdef __cplusplus +} +#endif \ No newline at end of file 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..68b5d5d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/pac_kit.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#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)); +} \ 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..94d8abe --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby/platform_features.h @@ -0,0 +1,51 @@ +#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 +#ifdef __cplusplus +// #include "TINYSTL/vector.h" +// #include "TINYSTL/unordered_map.h" +#include "TINYSTL/vector.h" +#include "TINYSTL/unordered_map.h" +#endif +#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/dobby/x86/libdobby.a b/app/src/main/cpp/dobby/x86/libdobby.a deleted file mode 100644 index bfe8ff9ad01711174fa7400f18aa2592d91fb3cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149066 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 diff --git a/app/src/main/cpp/dobby/x86_64/libdobby.a b/app/src/main/cpp/dobby/x86_64/libdobby.a deleted file mode 100644 index ebb072fbf3bf0e941ac60a733b722b39ecb51556..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 205344 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 -#include #include #include #include +#include +#include +#include +#include #include "zygisk.hpp" - -#if defined(__arm__) - -#include "shadowhook.h" - -#elif defined(__aarch64__) - -#include "shadowhook.h" - -#elif defined(__i386__) - #include "dobby.h" -#elif defined(__x86_64__) - -#include "dobby.h" - -#endif - #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__) -void (*o_callback)(void *, const char *, const char *, uint32_t); +inline static const std::map PROPS_MAP = { + {"ro.product.first_api_level", "24"}, + {"ro.secure", "1"}, + {"ro.debuggable", "0"}, + {"sys.usb.state", "none"}, + {"ro.boot.verifiedbootstate", "green"}, + {"ro.boot.flash.locked", "1"}, + {"ro.boot.vbmeta.device_state", "locked"} +}; + +typedef void (*T_Callback)(void *, const char *, const char *, uint32_t); + +static std::map map; static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) { - if (strcmp(name, "ro.product.first_api_level") == 0) value = "24"; + if (name != nullptr) { + std::string prop(name); - if (strncmp(name, "cache", 5) != 0) LOGD("[%s] -> %s | (%p) (%d)", name, value, cookie, serial); + if (PROPS_MAP.contains(prop)) value = PROPS_MAP.at(prop).c_str(); - return o_callback(cookie, name, value, serial); + if (!prop.starts_with("cache")) LOGD("[%s] -> %s", name, value); + + prop.clear(); + prop.shrink_to_fit(); + } + + return map[cookie](cookie, name, value, serial); } static void (*o_system_property_read_callback)(const prop_info *, - void (*)(void *, const char *, - const char *, uint32_t), + T_Callback, void *); static void my_system_property_read_callback(const prop_info *pi, - void (*callback)(void *cookie, const char *name, - const char *value, uint32_t serial), + T_Callback callback, void *cookie) { - o_callback = callback; + + if (pi == nullptr || callback == nullptr || cookie == nullptr) { + return o_system_property_read_callback(pi, callback, cookie); + } + map[cookie] = callback; return o_system_property_read_callback(pi, modify_callback, cookie); } static void doHook() { LOGD("Starting to hook..."); - void *handle; - -#if defined(__arm__) - shadowhook_init(SHADOWHOOK_MODE_UNIQUE, true); - handle = shadowhook_hook_sym_name("libc.so", "__system_property_read_callback", - (void *) my_system_property_read_callback, - (void **) &o_system_property_read_callback); -#elif defined(__aarch64__) - shadowhook_init(SHADOWHOOK_MODE_UNIQUE, true); - handle = shadowhook_hook_sym_name("libc.so", "__system_property_read_callback", - (void *) my_system_property_read_callback, - (void **) &o_system_property_read_callback); -#elif defined(__i386__) - handle = DobbySymbolResolver(nullptr, "__system_property_read_callback"); -#elif defined(__x86_64__) - handle = DobbySymbolResolver(nullptr, "__system_property_read_callback"); -#endif + auto handle = DobbySymbolResolver(nullptr, "__system_property_read_callback"); if (handle == nullptr) { LOGD("Couldn't get __system_property_read_callback handle."); + return; } else { LOGD("Got __system_property_read_callback handle and hooked it at %p", handle); } -#if defined(__i386__) DobbyHook(handle, (void *) my_system_property_read_callback, (void **) &o_system_property_read_callback); -#elif defined(__x86_64__) - DobbyHook(handle, (void *) my_system_property_read_callback, - (void **) &o_system_property_read_callback); -#endif } class PlayIntegrityFix : public zygisk::ModuleBase { @@ -93,47 +79,52 @@ public: } void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { - bool isGms = false; - bool isGmsUnstable = false; + auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr); + std::string process(rawProcess); + env->ReleaseStringUTFChars(args->nice_name, rawProcess); - auto process = env->GetStringUTFChars(args->nice_name, nullptr); + if (process.starts_with("com.google.android.gms")) { - if (process != nullptr) { - isGms = strncmp(process, "com.google.android.gms", 22) == 0; - isGmsUnstable = strcmp(process, "com.google.android.gms.unstable") == 0; + api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); + + if (process == "com.google.android.gms.unstable") { + isGmsUnstable = true; + + int fd = api->connectCompanion(); + + long size; + read(fd, &size, sizeof(size)); + + if (size > 0) { + + LOGD("Received %ld bytes from socket", size); + + char buffer[size]; + read(fd, buffer, size); + buffer[size] = 0; + + moduleDex.insert(moduleDex.end(), buffer, buffer + size); + + } else { + LOGD("Received invalid bytes from socket. Does classes.dex file exist?"); + } + + close(fd); + } } - env->ReleaseStringUTFChars(args->nice_name, process); + process.clear(); + process.shrink_to_fit(); - if (!isGms) { - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - return; - } - - api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); - - if (!isGmsUnstable) { - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - return; - } - - auto fd = api->connectCompanion(); - - read(fd, &moduleDexSize, sizeof(moduleDexSize)); - - moduleDex = static_cast(malloc(moduleDexSize)); - - read(fd, moduleDex, moduleDexSize); - - close(fd); - - moduleDex[moduleDexSize] = 0; + if (!isGmsUnstable) api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); } void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { - if (moduleDex == nullptr || moduleDexSize == 0) return; + if (!isGmsUnstable) return; + doHook(); - injectDex(); + + if (!moduleDex.empty()) injectDex(); } void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { @@ -143,8 +134,8 @@ public: private: zygisk::Api *api = nullptr; JNIEnv *env = nullptr; - char *moduleDex = nullptr; - long moduleDexSize = 0; + bool isGmsUnstable = false; + std::vector moduleDex; void injectDex() { LOGD("get system classloader"); @@ -154,7 +145,7 @@ private: auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader); LOGD("create buffer"); - auto buf = env->NewDirectByteBuffer(moduleDex, moduleDexSize); + auto buf = env->NewDirectByteBuffer(moduleDex.data(), static_cast(moduleDex.size())); LOGD("create class loader"); auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); auto dexClInit = env->GetMethodID(dexClClass, "", @@ -173,7 +164,8 @@ private: env->CallStaticVoidMethod(entryClass, entryInit); LOGD("clean"); - free(moduleDex); + moduleDex.clear(); + moduleDex.shrink_to_fit(); env->DeleteLocalRef(clClass); env->DeleteLocalRef(systemClassLoader); env->DeleteLocalRef(buf); @@ -186,18 +178,25 @@ private: }; static void companion(int fd) { - FILE *file = fopen("/data/adb/modules/playintegrityfix/classes.dex", "rb"); + std::ifstream ifs("/data/adb/modules/playintegrityfix/classes.dex", + std::ios::binary | std::ios::ate); - fseek(file, 0, SEEK_END); - long size = ftell(file); - fseek(file, 0, SEEK_SET); + if (ifs.bad()) { + long i = -1; + write(fd, &i, sizeof(i)); + close(fd); + return; + } + + long size = ifs.tellg(); + ifs.seekg(std::ios::beg); char buffer[size]; - fread(buffer, size, 1, file); - fclose(file); - + ifs.read(buffer, size); buffer[size] = 0; + ifs.close(); + write(fd, &size, sizeof(size)); write(fd, buffer, size); diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c deleted file mode 100644 index d71e4f8..0000000 --- a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright (c) 2021-2022 ByteDance Inc. -// -// 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. -// - -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. - -#include "sh_a32.h" - -#include -#include -#include - -#include "sh_log.h" - -// https://developer.arm.com/documentation/ddi0406/latest -// https://developer.arm.com/documentation/ddi0597/latest - -typedef enum { - IGNORED = 0, - B_A1, - BX_A1, - BL_IMM_A1, - BLX_IMM_A2, - ADD_REG_A1, - ADD_REG_PC_A1, - SUB_REG_A1, - SUB_REG_PC_A1, - ADR_A1, - ADR_A2, - MOV_REG_A1, - MOV_REG_PC_A1, - LDR_LIT_A1, - LDR_LIT_PC_A1, - LDRB_LIT_A1, - LDRD_LIT_A1, - LDRH_LIT_A1, - LDRSB_LIT_A1, - LDRSH_LIT_A1, - LDR_REG_A1, - LDR_REG_PC_A1, - LDRB_REG_A1, - LDRD_REG_A1, - LDRH_REG_A1, - LDRSB_REG_A1, - LDRSH_REG_A1 -} sh_a32_type_t; - -static sh_a32_type_t sh_a32_get_type(uint32_t inst) { - if (((inst & 0x0F000000u) == 0x0A000000) && ((inst & 0xF0000000) != 0xF0000000)) - return B_A1; - else if (((inst & 0x0FFFFFFFu) == 0x012FFF1F) && ((inst & 0xF0000000) != 0xF0000000)) - return BX_A1; - else if (((inst & 0x0F000000u) == 0x0B000000) && ((inst & 0xF0000000) != 0xF0000000)) - return BL_IMM_A1; - else if ((inst & 0xFE000000) == 0xFA000000) - return BLX_IMM_A2; - else if (((inst & 0x0FE00010u) == 0x00800000) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) && - (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F))) - return ((inst & 0x0000F000u) == 0x0000F000) ? ADD_REG_PC_A1 : ADD_REG_A1; - else if (((inst & 0x0FE00010u) == 0x00400000) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) && - (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F))) - return ((inst & 0x0000F000u) == 0x0000F000) ? SUB_REG_PC_A1 : SUB_REG_A1; - else if (((inst & 0x0FFF0000u) == 0x028F0000) && ((inst & 0xF0000000) != 0xF0000000)) - return ADR_A1; - else if (((inst & 0x0FFF0000u) == 0x024F0000) && ((inst & 0xF0000000) != 0xF0000000)) - return ADR_A2; - else if (((inst & 0x0FEF001Fu) == 0x01A0000F) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x0010F000u) != 0x0010F000) && - (!(((inst & 0x0000F000u) == 0x0000F000) && ((inst & 0x00000FF0u) != 0x00000000)))) - return ((inst & 0x0000F000u) == 0x0000F000) ? MOV_REG_PC_A1 : MOV_REG_A1; - else if (((inst & 0x0F7F0000u) == 0x051F0000) && ((inst & 0xF0000000) != 0xF0000000)) - return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_A1 : LDR_LIT_A1; - else if (((inst & 0x0F7F0000u) == 0x055F0000) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRB_LIT_A1; - else if (((inst & 0x0F7F00F0u) == 0x014F00D0) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRD_LIT_A1; - else if (((inst & 0x0F7F00F0u) == 0x015F00B0) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRH_LIT_A1; - else if (((inst & 0x0F7F00F0u) == 0x015F00D0) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRSB_LIT_A1; - else if (((inst & 0x0F7F00F0u) == 0x015F00F0) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRSH_LIT_A1; - else if (((inst & 0x0E5F0010u) == 0x061F0000) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_REG_PC_A1 : LDR_REG_A1; - else if (((inst & 0x0E5F0010u) == 0x065F0000) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRB_REG_A1; - else if (((inst & 0x0E5F0FF0u) == 0x000F00D0) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRD_REG_A1; - else if (((inst & 0x0E5F0FF0u) == 0x001F00B0) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRH_REG_A1; - else if (((inst & 0x0E5F0FF0u) == 0x001F00D0) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRSB_REG_A1; - else if (((inst & 0x0E5F0FF0u) == 0x001F00F0) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRSH_REG_A1; - else - return IGNORED; -} - -size_t sh_a32_get_rewrite_inst_len(uint32_t inst) { - static uint8_t map[] = { - 4, // IGNORED - 12, // B_A1 - 12, // BX_A1 - 16, // BL_IMM_A1 - 16, // BLX_IMM_A2 - 32, // ADD_REG_A1 - 32, // ADD_REG_PC_A1 - 32, // SUB_REG_A1 - 32, // SUB_REG_PC_A1 - 12, // ADR_A1 - 12, // ADR_A2 - 32, // MOV_REG_A1 - 12, // MOV_REG_PC_A1 - 24, // LDR_LIT_A1 - 36, // LDR_LIT_PC_A1 - 24, // LDRB_LIT_A1 - 24, // LDRD_LIT_A1 - 24, // LDRH_LIT_A1 - 24, // LDRSB_LIT_A1 - 24, // LDRSH_LIT_A1 - 32, // LDR_REG_A1 - 36, // LDR_REG_PC_A1 - 32, // LDRB_REG_A1 - 32, // LDRD_REG_A1 - 32, // LDRH_REG_A1 - 32, // LDRSB_REG_A1 - 32 // LDRSH_REG_A1 - }; - - return (size_t)(map[sh_a32_get_type(inst)]); -} - -static bool sh_a32_is_addr_need_fix(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) { - return (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr); -} - -static uintptr_t sh_a32_fix_addr(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) { - if (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr) { - uintptr_t cursor_addr = rinfo->overwrite_start_addr; - size_t offset = 0; - for (size_t i = 0; i < rinfo->rewrite_inst_lens_cnt; i++) { - if (cursor_addr >= addr) break; - cursor_addr += 4; - offset += rinfo->rewrite_inst_lens[i]; - } - uintptr_t fixed_addr = (uintptr_t)rinfo->rewrite_buf + offset; - SH_LOG_INFO("a32 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr); - return fixed_addr; - } - - return addr; -} - -static size_t sh_a32_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, - sh_a32_rewrite_info_t *rinfo) { - uint32_t cond; - if (type == B_A1 || type == BL_IMM_A1 || type == BX_A1) - cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - else - // type == BLX_IMM_A2 - cond = 0xE; // 1110 None (AL) - - uint32_t addr; - if (type == B_A1 || type == BL_IMM_A1) { - uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0); - uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(imm24 << 2u, 26u); - addr = pc + imm32; // arm -> arm - } else if (type == BLX_IMM_A2) { - uint32_t h = SH_UTIL_GET_BIT_32(inst, 24); - uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0); - uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32((imm24 << 2u) | (h << 1u), 26u); - addr = SH_UTIL_SET_BIT0(pc + imm32); // arm -> thumb - } else { - // type == BX_A1 - // BX PC - // PC must be even, and the "arm" instruction must be at a 4-byte aligned address, - // so the instruction set must keep "arm" unchanged. - addr = pc; // arm -> arm - } - addr = sh_a32_fix_addr(addr, rinfo); - - size_t idx = 0; - if (type == BL_IMM_A1 || type == BLX_IMM_A2) { - buf[idx++] = 0x028FE008u | (cond << 28u); // ADD LR, PC, #8 - } - buf[idx++] = 0x059FF000u | (cond << 28u); // LDR PC, [PC, #0] - buf[idx++] = 0xEA000000; // B #0 - buf[idx++] = addr; - return idx * 4; // 12 or 16 -} - -static size_t sh_a32_rewrite_add_or_sub(uint32_t *buf, uint32_t inst, uintptr_t pc) { - // ADD{S} , , PC{, } or ADD{S} , PC, {, } - // SUB{S} , , PC{, } or SUB{S} , PC, {, } - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t rn = SH_UTIL_GET_BITS_32(inst, 19, 16); - uint32_t rm = SH_UTIL_GET_BITS_32(inst, 3, 0); - uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); - - uint32_t rx; // r0 - r3 - for (rx = 3;; --rx) - if (rx != rn && rx != rm && rx != rd) break; - - if (rd == 0xF) // Rd == PC - { - uint32_t ry; // r0 - r4 - for (ry = 4;; --ry) - if (ry != rn && ry != rm && ry != rd && ry != rx) break; - - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000005; // B #20 - buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC} - buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] - if (rn == 0xF) - // Rn == PC - buf[4] = - (inst & 0x0FF00FFFu) | 0xE0000000 | (ry << 12u) | (rx << 16u); // ADD/SUB Ry, Rx, Rm{, } - else - // Rm == PC - buf[4] = (inst & 0x0FFF0FF0u) | 0xE0000000 | (ry << 12u) | rx; // ADD/SUB Ry, Rn, Rx{, } - buf[5] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8] - buf[6] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC} - buf[7] = pc; - return 32; - } else { - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000005; // B #20 - buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} - buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] - if (rn == 0xF) - // Rn == PC - buf[4] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // ADD/SUB{S} Rd, Rx, Rm{, } - else - // Rm == PC - buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // ADD/SUB{S} Rd, Rn, Rx{, } - buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx} - buf[6] = 0xEA000000; // B #0 - buf[7] = pc; - return 32; - } -} - -static size_t sh_a32_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, - sh_a32_rewrite_info_t *rinfo) { - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); // r0 - r15 - uint32_t imm12 = SH_UTIL_GET_BITS_32(inst, 11, 0); - uint32_t imm32 = sh_util_arm_expand_imm(imm12); - uint32_t addr = (type == ADR_A1 ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32)); - if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed - - buf[0] = 0x059F0000u | (cond << 28u) | (rd << 12u); // LDR Rd, [PC, #0] - buf[1] = 0xEA000000; // B #0 - buf[2] = addr; - return 12; -} - -static size_t sh_a32_rewrite_mov(uint32_t *buf, uint32_t inst, uintptr_t pc) { - // MOV{S} , PC - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); - uint32_t rx = (rd == 0) ? 1 : 0; - - if (rd == 0xF) // Rd == PC (MOV PC, PC) - { - buf[0] = 0x059FF000u | (cond << 28u); // LDR PC, [PC, #0] - buf[1] = 0xEA000000; // B #0 - buf[2] = pc; - return 12; - } else { - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000005; // B #20 - buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} - buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] - buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // MOV{S} Rd, Rx{, #/RRX} - buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx} - buf[6] = 0xEA000000; // B #0 - buf[7] = pc; - return 32; - } -} - -static size_t sh_a32_rewrite_ldr_lit(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, - sh_a32_rewrite_info_t *rinfo) { - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t u = SH_UTIL_GET_BIT_32(inst, 23); - uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12); - - uint32_t imm32; - if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1) - imm32 = SH_UTIL_GET_BITS_32(inst, 11, 0); - else - imm32 = (SH_UTIL_GET_BITS_32(inst, 11, 8) << 4u) + SH_UTIL_GET_BITS_32(inst, 3, 0); - uint32_t addr = (u ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32)); - if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed - - if (type == LDR_LIT_PC_A1 && rt == 0xF) { - // Rt == PC - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000006; // B #24 - buf[2] = 0xE92D0003; // PUSH {R0, R1} - buf[3] = 0xE59F0000; // LDR R0, [PC, #0] - buf[4] = 0xEA000000; // B #0 - buf[5] = addr; // - buf[6] = 0xE5900000; // LDR R0, [R0] - buf[7] = 0xE58D0004; // STR R0, [SP, #4] - buf[8] = 0xE8BD8001; // POP {R0, PC} - return 36; - } else { - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000003; // B #12 - buf[2] = 0xE59F0000 | (rt << 12u); // LDR Rt, [PC, #0] - buf[3] = 0xEA000000; // B #0 - buf[4] = addr; // -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wswitch" - switch (type) { - case LDR_LIT_A1: - buf[5] = 0xE5900000 | (rt << 16u) | (rt << 12u); // LDR Rt, [Rt] - break; - case LDRB_LIT_A1: - buf[5] = 0xE5D00000 | (rt << 16u) | (rt << 12u); // LDRB Rt, [Rt] - break; - case LDRD_LIT_A1: - buf[5] = 0xE1C000D0 | (rt << 16u) | (rt << 12u); // LDRD Rt, [Rt] - break; - case LDRH_LIT_A1: - buf[5] = 0xE1D000B0 | (rt << 16u) | (rt << 12u); // LDRH Rt, [Rt] - break; - case LDRSB_LIT_A1: - buf[5] = 0xE1D000D0 | (rt << 16u) | (rt << 12u); // LDRSB Rt, [Rt] - break; - case LDRSH_LIT_A1: - buf[5] = 0xE1D000F0 | (rt << 16u) | (rt << 12u); // LDRSH Rt, [Rt] - break; - } -#pragma clang diagnostic pop - return 24; - } -} - -static size_t sh_a32_rewrite_ldr_reg(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type) { - // LDR , [PC,+/-{, }]{!} - // ...... - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12); - uint32_t rt2 = rt + 1; - uint32_t rm = SH_UTIL_GET_BITS_16(inst, 3, 0); - uint32_t rx; // r0 - r3 - for (rx = 3;; --rx) - if (rx != rt && rx != rt2 && rx != rm) break; - - if (type == LDR_REG_PC_A1 && rt == 0xF) { - // Rt == PC - uint32_t ry; // r0 - r4 - for (ry = 4;; --ry) - if (ry != rt && ry != rt2 && ry != rm && ry != rx) break; - - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000006; // B #24 - buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC} - buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #8] - buf[4] = 0xEA000000; // B #0 - buf[5] = pc; - buf[6] = - (inst & 0x0FF00FFFu) | 0xE0000000 | (rx << 16u) | (ry << 12u); // LDRxx Ry, [Rx],+/-Rm{, } - buf[7] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8] - buf[8] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC} - return 36; - } else { - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000005; // B #20 - buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} - buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #0] - buf[4] = 0xEA000000; // B #0 - buf[5] = pc; - buf[6] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // LDRxx Rt, [Rx],+/-Rm{, } - buf[7] = 0xE49D0004 | (rx << 12u); // POP {Rx} - return 32; - } -} - -size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo) { - sh_a32_type_t type = sh_a32_get_type(inst); - SH_LOG_INFO("a32 rewrite: type %d, inst %" PRIx32, type, inst); - - // We will only overwrite 4 to 8 bytes on A32, so PC cannot be in the coverage. - // In this case, the add/sub/mov/ldr_reg instruction does not need to consider - // the problem of PC in the coverage area when rewriting. - - if (type == B_A1 || type == BX_A1 || type == BL_IMM_A1 || type == BLX_IMM_A2) - return sh_a32_rewrite_b(buf, inst, pc, type, rinfo); - else if (type == ADD_REG_A1 || type == ADD_REG_PC_A1 || type == SUB_REG_A1 || type == SUB_REG_PC_A1) - return sh_a32_rewrite_add_or_sub(buf, inst, pc); - else if (type == ADR_A1 || type == ADR_A2) - return sh_a32_rewrite_adr(buf, inst, pc, type, rinfo); - else if (type == MOV_REG_A1 || type == MOV_REG_PC_A1) - return sh_a32_rewrite_mov(buf, inst, pc); - else if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1 || type == LDRD_LIT_A1 || - type == LDRH_LIT_A1 || type == LDRSB_LIT_A1 || type == LDRSH_LIT_A1) - return sh_a32_rewrite_ldr_lit(buf, inst, pc, type, rinfo); - else if (type == LDR_REG_A1 || type == LDR_REG_PC_A1 || type == LDRB_REG_A1 || type == LDRD_REG_A1 || - type == LDRH_REG_A1 || type == LDRSB_REG_A1 || type == LDRSH_REG_A1) - return sh_a32_rewrite_ldr_reg(buf, inst, pc, type); - else { - // IGNORED - buf[0] = inst; - return 4; - } -} - -size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr) { - buf[0] = 0xE51FF004; // LDR PC, [PC, #-4] - buf[1] = addr; - return 8; -} - -size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) { - buf[0] = 0xEA000000 | (((addr - pc) & 0x03FFFFFFu) >> 2u); // B