From da8e61115b75be85fe5dd926413dcf1fe66ad395 Mon Sep 17 00:00:00 2001 From: chiteroman <98092901+chiteroman@users.noreply.github.com> Date: Fri, 19 Jan 2024 00:29:17 +0100 Subject: [PATCH] v15.2 --- .idea/vcs.xml | 1 + app/build.gradle.kts | 27 +- app/proguard-rules.pro | 4 +- app/src/main/cpp/Android.mk | 16 - app/src/main/cpp/Application.mk | 6 - app/src/main/cpp/CMakeLists.txt | 33 + 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 | 362 +++++++ 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 + .../ObjcRuntimeReplace/objc_runtime_repalce.h | 18 + .../objc_runtime_replace.mm | 56 ++ .../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 | 44 + .../SymbolResolver/dobby_symbol_resolver.h | 15 + .../elf/dobby_symbol_resolver.cc | 292 ++++++ .../macho/dobby_symbol_resolver.cc | 454 +++++++++ .../macho/dobby_symbol_resolver_priv.h | 46 + ...dyld_shared_cache_symbol_table_iterator.cc | 241 +++++ .../macho/shared-cache/dyld_cache_format.h | 530 ++++++++++ .../macho/shared_cache_internal.h | 70 ++ .../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 | 53 + .../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 | 90 ++ .../main/cpp/Dobby/examples/CMakeLists.txt | 17 + app/src/main/cpp/Dobby/examples/main.cc | 14 + .../main/cpp/Dobby/examples/socket_example.cc | 212 ++++ .../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 | 336 +++++++ .../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 | 17 + .../cpp/Dobby/external/logging/cxxlogging.cc | 36 + .../Dobby/external/logging/kernel_logging.c | 28 + .../main/cpp/Dobby/external/logging/logging.c | 124 +++ .../external/logging/logging/check_logging.h | 87 ++ .../external/logging/logging/cxxlogging.h | 15 + .../Dobby/external/logging/logging/logging.h | 88 ++ .../main/cpp/{dobby => Dobby/include}/dobby.h | 334 ++++--- app/src/main/cpp/Dobby/scripts/Dockerfile | 10 + .../cpp/Dobby/scripts/platform_builder.py | 240 +++++ .../scripts/setup_linux_cross_compile.sh | 43 + .../scripts/setup_macos_cross_compile.sh | 22 + .../ExecMemory/clear-cache-tool-all.c | 3 + .../ExecMemory/code-patch-tool-darwin.cc | 109 ++ .../Darwin/ProcessRuntimeUtility.cc | 131 +++ .../PlatformUtil/ProcessRuntimeUtility.h | 26 + .../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 | 81 ++ .../ExecMemory/code-patch-tool-posix.cc | 37 + .../ExecMemory/code-patch-tool-windows.cc | 27 + .../mach_interface_support/substrated.defs | 24 + .../MultiThreadSupport/ThreadSupport.cpp | 22 + .../MultiThreadSupport/ThreadSupport.h | 63 ++ .../Darwin/ProcessRuntimeUtility.cc | 132 +++ .../Linux/ProcessRuntimeUtility.cc | 244 +++++ .../PlatformUtil/ProcessRuntimeUtility.h | 26 + .../Windows/ProcessRuntimeUtility.cc | 81 ++ .../Backend/UserMode/Thread/PlatformThread.cc | 19 + .../Backend/UserMode/Thread/PlatformThread.h | 36 + .../UserMode/Thread/platform-thread-posix.cc | 71 ++ .../Thread/platform-thread-windows.cc | 25 + .../platform-darwin/mach_vm.h | 933 ++++++++++++++++++ .../UnifiedInterface/platform-posix.cc | 205 ++++ .../UnifiedInterface/platform-windows.cc | 87 ++ .../UserMode/UnifiedInterface/platform.h | 109 ++ .../UserMode/UnifiedInterface/semaphore.cc | 160 +++ .../UserMode/UnifiedInterface/semaphore.h | 98 ++ .../InstructionRelocation.h | 5 + .../arm/InstructionRelocationARM.cc | 917 +++++++++++++++++ .../arm/InstructionRelocationARM.h | 300 ++++++ .../arm64/InstructionRelocationARM64.cc | 366 +++++++ .../arm64/InstructionRelocationARM64.h | 13 + .../arm64/inst_constants.h | 49 + .../arm64/inst_decode_encode_kit.h | 110 +++ .../x64/InstructionRelocationX64.cc | 78 ++ .../x64/InstructionRelocationX64.h | 9 + .../x86/InstructionRelocationX86.cc | 79 ++ .../x86/InstructionRelocationX86.h | 9 + .../x86/InstructionRelocationX86Shared.cc | 196 ++++ .../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 | 18 + .../main/cpp/Dobby/source/InterceptEntry.h | 30 + .../InterceptRouting/InterceptRouting.cpp | 98 ++ .../InterceptRouting/InterceptRouting.h | 62 ++ .../FunctionInlineHook/FunctionInlineHook.cc | 51 + .../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 | 44 + .../InstructionInstrumentRouting.h | 30 + .../InstructionInstrument/RoutingImpl.cc | 42 + .../instrument_routing_handler.cc | 21 + .../instrument_routing_handler.h | 7 + .../NearBranchTrampoline.cc | 47 + .../NearBranchTrampoline.h | 15 + .../near_trampoline_arm64.cc | 82 ++ .../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 | 11 + .../CodeBuffer/code_buffer_arm.h | 56 ++ .../CodeBuffer/code_buffer_arm64.h | 21 + .../CodeBuffer/code_buffer_x64.h | 14 + .../CodeBuffer/code_buffer_x86.h | 13 + .../source/MemoryAllocator/MemoryAllocator.cc | 106 ++ .../MemoryAllocator/NearMemoryAllocator.cc | 234 +++++ .../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 | 40 + .../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 | 47 + ...ynamic-closure-trampoline-template-arm64.S | 31 + .../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 | 23 + .../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 | 61 ++ .../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 | 98 ++ .../cpp/Dobby/source/core/arch/x86/cpu-x86.h | 120 +++ .../source/core/arch/x86/registers-x86.h | 124 +++ .../core/assembler/AssemblerPseudoLabel.h | 94 ++ .../source/core/assembler/assembler-arch.h | 28 + .../source/core/assembler/assembler-arm.cc | 41 + .../source/core/assembler/assembler-arm.h | 357 +++++++ .../source/core/assembler/assembler-arm64.cc | 25 + .../source/core/assembler/assembler-arm64.h | 563 +++++++++++ .../source/core/assembler/assembler-ia32.cc | 34 + .../source/core/assembler/assembler-ia32.h | 470 +++++++++ .../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 | 710 +++++++++++++ .../Dobby/source/core/assembler/assembler.cc | 65 ++ .../Dobby/source/core/assembler/assembler.h | 76 ++ .../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 | 32 + .../main/cpp/Dobby/source/dobby_internal.h | 18 + .../cpp/Dobby/source/include/common_header.h | 9 + .../Dobby/source/include/kernel_mode_header.h | 57 ++ .../main/cpp/Dobby/source/include/list_c.h | 52 + .../Dobby/source/include/platform_header.h | 52 + .../cpp/Dobby/source/include/platform_macro.h | 15 + .../cpp/Dobby/source/include/type_header.h | 14 + .../cpp/Dobby/source/include/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/arm64-v8a/libdobby.a | Bin 212502 -> 0 bytes app/src/main/cpp/dobby/armeabi-v7a/libdobby.a | Bin 166506 -> 0 bytes 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 | 176 ++-- .../playintegrityfix/CustomKeyStoreSpi.java | 105 -- .../playintegrityfix/CustomProvider.java | 8 +- .../playintegrityfix/EntryPoint.java | 33 +- changelog.md | 11 +- module/customize.sh | 5 - module/pif.json | 11 - update.json | 6 +- 292 files changed, 26681 insertions(+), 435 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/objc_runtime_repalce.h create mode 100644 app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/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/dyld_shared_cache_symbol_table_iterator.cc 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_internal.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/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/cxxlogging.cc create mode 100644 app/src/main/cpp/Dobby/external/logging/kernel_logging.c create mode 100644 app/src/main/cpp/Dobby/external/logging/logging.c 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/cxxlogging.h create mode 100644 app/src/main/cpp/Dobby/external/logging/logging/logging.h rename app/src/main/cpp/{dobby => Dobby/include}/dobby.h (64%) 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_internal.h create mode 100644 app/src/main/cpp/Dobby/source/include/common_header.h create mode 100644 app/src/main/cpp/Dobby/source/include/kernel_mode_header.h create mode 100644 app/src/main/cpp/Dobby/source/include/list_c.h create mode 100644 app/src/main/cpp/Dobby/source/include/platform_header.h create mode 100644 app/src/main/cpp/Dobby/source/include/platform_macro.h create mode 100644 app/src/main/cpp/Dobby/source/include/type_header.h create mode 100644 app/src/main/cpp/Dobby/source/include/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/arm64-v8a/libdobby.a delete mode 100644 app/src/main/cpp/dobby/armeabi-v7a/libdobby.a 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/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java delete mode 100644 module/pif.json diff --git a/.idea/vcs.xml b/.idea/vcs.xml index c8397c9..aa851fd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 177d9d0..c633c66 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -23,8 +23,16 @@ android { versionName = "v15.2" externalNativeBuild { - ndk { - jobs = Runtime.getRuntime().availableProcessors() + cmake { + arguments += "-DANDROID_STL=none" + arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel" + arguments += "-DPlugin.Android.BionicLinkerUtil=ON" + + cppFlags += "-std=c++20" + cppFlags += "-fno-exceptions" + cppFlags += "-fno-rtti" + cppFlags += "-fvisibility=hidden" + cppFlags += "-fvisibility-inlines-hidden" } } } @@ -33,7 +41,9 @@ android { release { isMinifyEnabled = true isShrinkResources = true - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" + ) } } @@ -43,8 +53,9 @@ android { } externalNativeBuild { - ndkBuild { - path = file("src/main/cpp/Android.mk") + cmake { + path = file("src/main/cpp/CMakeLists.txt") + version = "3.22.1" } } } @@ -71,8 +82,10 @@ tasks.register("copyFiles") { doLast { val moduleFolder = project.rootDir.resolve("module") - val dexFile = project.layout.buildDirectory.get().asFile.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex") - val soDir = project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/out/lib") + val dexFile = + project.layout.buildDirectory.get().asFile.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex") + val soDir = + project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/out/lib") dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 2f11af0..47abbdb 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,3 +1 @@ --keep class es.chiteroman.playintegrityfix.EntryPoint {public ;} --keep class es.chiteroman.playintegrityfix.CustomProvider --keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi \ No newline at end of file +-keep class es.chiteroman.playintegrityfix.EntryPoint {public ;} \ No newline at end of file diff --git a/app/src/main/cpp/Android.mk b/app/src/main/cpp/Android.mk deleted file mode 100644 index bd44239..0000000 --- a/app/src/main/cpp/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := dobby -LOCAL_SRC_FILES := $(LOCAL_PATH)/dobby/$(TARGET_ARCH_ABI)/libdobby.a -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/dobby -include $(PREBUILT_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := zygisk -LOCAL_SRC_FILES := main.cpp -LOCAL_STATIC_LIBRARIES := libcxx dobby -LOCAL_LDLIBS := -llog -include $(BUILD_SHARED_LIBRARY) - -include $(LOCAL_PATH)/libcxx/Android.mk \ 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 6e01d55..0000000 --- a/app/src/main/cpp/Application.mk +++ /dev/null @@ -1,6 +0,0 @@ -APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 -APP_CFLAGS := -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -Oz -flto -APP_CPPFLAGS := -std=c++20 -fno-exceptions -fno-rtti -APP_LDFLAGS := -Oz -flto -Wl,--exclude-libs,ALL -Wl,--gc-sections -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..e0525a2 --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.22.1) + +project("playintegrityfix") + +include_directories(${CMAKE_SOURCE_DIR}/libcxx/include) + +link_libraries(${CMAKE_SOURCE_DIR}/libcxx/${CMAKE_ANDROID_ARCH_ABI}/libcxx.a) + +add_library(${CMAKE_PROJECT_NAME} SHARED + main.cpp) + +if (NOT TARGET dobby) + set(DOBBY_DIR ${CMAKE_SOURCE_DIR}/Dobby) + macro(SET_OPTION option value) + set(${option} ${value} CACHE INTERNAL "" FORCE) + endmacro() + SET_OPTION(DOBBY_DEBUG OFF) + SET_OPTION(DOBBY_GENERATE_SHARED OFF) + SET_OPTION(Plugin.Android.BionicLinkerUtil ON) + add_subdirectory(${DOBBY_DIR} dobby) + get_property(DOBBY_INCLUDE_DIRECTORIES + TARGET dobby + PROPERTY INCLUDE_DIRECTORIES) + include_directories( + . + ${DOBBY_INCLUDE_DIRECTORIES} + $ + ) +endif () + +target_link_libraries(${CMAKE_PROJECT_NAME} + log + dobby) \ 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..015497c --- /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.20.2 + LLVM_VERSION: 14.0.0 + 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.0 + 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..20f87f0 --- /dev/null +++ b/app/src/main/cpp/Dobby/CMakeLists.txt @@ -0,0 +1,362 @@ +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)$") + +# ===== handle option ===== + +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(BUILD_EXAMPLE "Build example" OFF) + +option(BUILD_TEST "Build test" OFF) + +# private +option(Obfuscation "Enable llvm obfuscation" OFF) + +# private +option(BUILD_KERNEL_MODE "Build xnu kernel mode" OFF) + +# Enable debug will log more information +if ((NOT DEFINED CMAKE_BUILD_TYPE) OR (CMAKE_BUILD_TYPE STREQUAL "Debug")) + set(DOBBY_DEBUG ON) +endif () + +if (DOBBY_DEBUG) + add_definitions(-DDOBBY_DEBUG) + add_definitions(-DLOGGING_DEBUG) + message(STATUS "[Dobby] Enable debug logging") +endif () + +# Enable full floating point register pack +# for arm64, allow access q8 - q31 +if (FullFloatingPointRegisterPack) + add_definitions(-DFULL_FLOATING_POINT_REGISTER_PACK) + message(STATUS "[Dobby] Save and pack all floating-point registers") +endif () + +if (BUILD_KERNEL_MODE) + set(BUILDING_KERNEL ON) + add_definitions(-DBUILDING_KERNEL) + message(STATUS "[Dobby] Build xnu kernel mode") +endif () + +if (CMAKE_GENERATOR STREQUAL Xcode) +endif () + +include(cmake/compiler_and_linker.cmake) + +# --- + +include_directories( + . + ./include + ./source + ./source/include + + ./external + ./external/logging + + ./builtin-plugin +) + +if (SYSTEM.Darwin AND BUILDING_KERNEL) + 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 BUILDING_KERNEL) + 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 BUILDING_KERNEL)) + include_directories( + source/Backend/UserMode/ExecMemory/substrated + ) + add_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) + message(STATUS "[Dobby] Enable near branch trampoline") + 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) + +# --- + +if (Plugin.SymbolResolver) + message(STATUS "[Dobby] Enable symbol resolver") + include_directories(builtin-plugin/SymbolResolver) + add_subdirectory(builtin-plugin/SymbolResolver) + get_target_property(symbol_resolver.SOURCE_FILE_LIST 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 +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") + execute_process( + COMMAND git rev-parse --short --verify HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE VERSION_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (VERSION_COMMIT_HASH) + set(VERSION_REVISION "${VERSION_COMMIT_HASH}") + endif () +endif () +set(DOBBY_BUILD_VERSION "Dobby${VERSION_REVISION}") +add_definitions(-D__DOBBY_BUILD_VERSION__="${DOBBY_BUILD_VERSION}") +message(STATUS "[Dobby] ${DOBBY_BUILD_VERSION}") + +# --- + +if (DOBBY_GENERATE_SHARED) + message(STATUS "[Dobby] Generate shared library") + set(DOBBY_LIBRARY_TYPE SHARED) +else () + message(STATUS "[Dobby] Generate static library") + set(DOBBY_LIBRARY_TYPE STATIC) +endif () +add_library(dobby ${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}) + +# --- + +target_include_directories(dobby PUBLIC include) + +# --- + +if (Obfuscation) + set(linker_flags "${linker_flags} -Wl,-mllvm -Wl,-obfuscator-conf=all") +endif () + +set_target_properties(dobby + PROPERTIES + LINK_FLAGS "${linker_flags}" + COMPILE_FLAGS "${compiler_flags}" + ) + +# --- + +if (SYSTEM.Android) + target_link_libraries(dobby log) + if (PROCESSOR.ARM) + set_target_properties(dobby + PROPERTIES + ANDROID_ARM_MODE arm + ) + endif () +endif () + +if (SYSTEM.Linux) + target_link_libraries(dobby dl) +endif () + +# --- + +if (BUILD_EXAMPLE AND (NOT BUILDING_KERNEL)) + add_subdirectory(examples) +endif () + +if (BUILD_TEST AND (NOT BUILDING_KERNEL)) + add_subdirectory(tests) +endif () + +# --- + +if (SYSTEM.Darwin AND (NOT BUILDING_KERNEL)) + include(cmake/platform/platform-darwin.cmake) +endif () 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..5de4284 --- /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 "common_header.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); + LOG(1, "[-] 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); + LOG(1, "[-] 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) { + LOG(1, "[-] 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) { + LOG(1, "[-] 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..4aaa2f4 --- /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 "common_header.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); + LOG(1, "[-] 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); + LOG(1, "[-] 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) { + LOG(1, "[-] 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) { + LOG(1, "[-] 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) { + LOG(1, "[-] 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..8314a18 --- /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) { + LOG(1, "[-] 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) { + LOG(1, "[-] 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) { + LOG(1, "[-] 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..095d45b --- /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"); + LOG(1, "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 + LOG(1, "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..8860ce1 --- /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 "common_header.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); + LOG(1, "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) { + LOG(1, "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); + + LOG(1, "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..44c513a --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ImportTableReplace/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(import_table_repalce 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..900f063 --- /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 "common_header.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 + LOG(1, "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..754610b --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(objc_runtime_replace + 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/objc_runtime_repalce.h b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/objc_runtime_repalce.h new file mode 100644 index 0000000..da959e7 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/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/objc_runtime_replace.mm b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/objc_runtime_replace.mm new file mode 100644 index 0000000..bd57340 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/ObjcRuntimeReplace/objc_runtime_replace.mm @@ -0,0 +1,56 @@ +#include "ObjcRuntimeReplace/objc_runtime_repalce.h" +#include "dobby_internal.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_) { + DLOG(0, "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_) { + DLOG(0, "Not found class: %s, selector: %s method\n", class_name, selector_name); + return; + } + + void *orig_impl = NULL; + 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_) { + DLOG(0, "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..25b2043 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/mach_system_call_log_handler.cc @@ -0,0 +1,193 @@ +#include "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..140de57 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/sensitive_api_monitor.cc @@ -0,0 +1,95 @@ +#include "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; + // LOG(2, "syscall svc ptrace deny"); + } + } + if (syscall_rum == SYS_exit) { + // LOG(2, "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; + // LOG(2, "svc ptrace deny"); + } + } + if (syscall_rum == SYS_exit) { + // LOG(2, "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) { + LOG(2, "not found func %s", sensitive_func_array[i]); + continue; + } + int func_svc_offset = get_func_svc_offset(func_addr); + if (func_svc_offset == 0) { + LOG(2, "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..762a2bb --- /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_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)) { + LOG(2, "[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"); + LOG(2, "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..c78db6a --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/system_call_log_handler.cc @@ -0,0 +1,98 @@ +#include "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..862c4e1 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SupervisorCallMonitor/test_supervisor_call_monitor.cc @@ -0,0 +1,16 @@ + +#include "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..0a0efe3 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/CMakeLists.txt @@ -0,0 +1,44 @@ +set(SOURCE_FILE_LIST ) + +if(NOT DEFINED DOBBY_DIR) + message(FATAL_ERROR "DOBBY_DIR must be set!") +endif() + +if(SYSTEM.Darwin AND (NOT BUILDING_KERNEL)) + set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST} + ${CMAKE_CURRENT_SOURCE_DIR}/macho/dyld_shared_cache_symbol_table_iterator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/macho/dobby_symbol_resolver.cc + + ${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc + ) +endif() +if(SYSTEM.Darwin AND BUILDING_KERNEL) + 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(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..75328cb --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/elf/dobby_symbol_resolver.cc @@ -0,0 +1,292 @@ +#include "SymbolResolver/dobby_symbol_resolver.h" +#include "common_header.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#include + +#undef LOG_TAG +#define LOG_TAG "DobbySymbolResolver" + +static void file_mmap(const char *file_path, uint8_t **data_ptr, size_t *data_size_ptr) { + uint8_t *mmap_data = NULL; + size_t file_size = 0; + + int fd = open(file_path, O_RDONLY, 0); + if (fd < 0) { + ERROR_LOG("%s open failed", file_path); + goto finished; + } + + { + struct stat s; + int rt = fstat(fd, &s); + if (rt != 0) { + ERROR_LOG("mmap failed"); + goto finished; + } + file_size = s.st_size; + } + + // auto align + mmap_data = (uint8_t *)mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, 0); + if (mmap_data == MAP_FAILED) { + ERROR_LOG("mmap failed"); + goto finished; + } + +finished: + close(fd); + + if (data_size_ptr) + *data_size_ptr = file_size; + if (data_ptr) + *data_ptr = mmap_data; +} + +static void file_unmap(void *data, size_t data_size) { + int ret = munmap(data, data_size); + if (ret != 0) { + ERROR_LOG("munmap failed"); + return; + } +} + +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); + + uint8_t *file_mem = NULL; + size_t file_mem_size = 0; + if (module.load_address) + file_mmap(module.path, &file_mem, &file_mem_size); + + 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 (file_mem) + file_unmap(file_mem, file_mem_size); + } + + if (!result) { + auto ProcessModuleMap = ProcessRuntimeUtility::GetProcessModuleMap(); + for (auto module : ProcessModuleMap) { + uint8_t *file_mem = NULL; + size_t file_mem_size = 0; + + if (module.load_address) + file_mmap(module.path, &file_mem, &file_mem_size); + + 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 (file_mem) + file_unmap(file_mem, file_mem_size); + + 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..3ea6b1e --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver.cc @@ -0,0 +1,454 @@ +#include "dobby_symbol_resolver.h" +#include "macho/dobby_symbol_resolver_priv.h" + +#include "common_header.h" + +#include +#include + +#include "PlatformUtil/ProcessRuntimeUtility.h" + +#if defined(BUILDING_KERNEL) +#else + +#include +#include +#include "SymbolResolver/macho/shared_cache_internal.h" + +#endif + +#undef LOG_TAG +#define LOG_TAG "DobbySymbolResolver" + +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 +uint8_t *walk_exported_trie(const uint8_t *start, const uint8_t *end, const char *symbol) { + const uint8_t *p = start; + while (p != NULL) { + uintptr_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)) { + //dyld::log("trieWalk(%p) returning %p\n", start, p); + return (uint8_t *) p; + } + const uint8_t *children = p + terminalSize; + if (children > end) { + // dyld::log("trieWalk() malformed trie node, terminalSize=0x%lx extends past end of trie\n", terminalSize); + return NULL; + } + //dyld::log("trieWalk(%p) sym=%s, terminalSize=%lu, children=%p\n", start, s, terminalSize, children); + uint8_t childrenRemaining = *children++; + p = children; + uintptr_t nodeOffset = 0; + for (; childrenRemaining > 0; --childrenRemaining) { + const char *ss = symbol; + //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p); + 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) { + // dyld::log("trieWalk() malformed trie node, child node extends past end of trie\n"); + return NULL; + } + } 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)) { + // dyld::log("trieWalk() malformed trie child, nodeOffset=0x%lx out of range\n", nodeOffset); + return NULL; + } + symbol = ss; + //dyld::log("trieWalk() found matching edge advancing to node 0x%lx\n", nodeOffset); + break; + } + } + if (nodeOffset != 0) + p = &start[nodeOffset]; + else + p = NULL; + } + //dyld::log("trieWalk(%p) return NULL\n", start); + return NULL; +} + +uintptr_t iterate_exported_symbol(mach_header_t *header, const char *symbol_name, uint64_t *out_flags) { + segment_command_t *curr_seg_cmd; + struct dyld_info_command *dyld_info_cmd = nullptr; + struct linkedit_data_command *exports_trie_cmd = nullptr; + segment_command_t *text_segment = nullptr, *data_segment = nullptr, *linkedit_segment = nullptr; + + curr_seg_cmd = (segment_command_t *) ((uintptr_t) header + sizeof(mach_header_t)); + for (int i = 0; i < header->ncmds; i++) { + switch (curr_seg_cmd->cmd) { + case LC_SEGMENT_ARCH_DEPENDENT: { + if (strcmp(curr_seg_cmd->segname, "__LINKEDIT") == 0) { + linkedit_segment = curr_seg_cmd; + } else if (strcmp(curr_seg_cmd->segname, "__TEXT") == 0) { + text_segment = curr_seg_cmd; + } + } + break; + case LC_DYLD_EXPORTS_TRIE: { + exports_trie_cmd = (struct linkedit_data_command *) curr_seg_cmd; + } + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: { + dyld_info_cmd = (struct dyld_info_command *) curr_seg_cmd; + } + break; + default: + break; + }; + curr_seg_cmd = (segment_command_t *) ((uintptr_t) curr_seg_cmd + curr_seg_cmd->cmdsize); + } + + if (text_segment == NULL || linkedit_segment == NULL) { + return 0; + } + + 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; + + uintptr_t slide = (uintptr_t) header - (uintptr_t) text_segment->vmaddr; + uintptr_t linkedit_base = (uintptr_t) slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; + + void *exports = (void *) (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 *) walk_exported_trie(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 (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { + return 0; + } + if (out_flags) + *out_flags = flags; + 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 +} + +void macho_ctx_init(macho_ctx_t *ctx, mach_header_t *header) { + ctx->header = header; + segment_command_t *curr_seg_cmd; + segment_command_t *text_segment = nullptr, *text_exec_segment = nullptr, *data_segment = nullptr, *data_const_segment = nullptr, + *linkedit_segment = nullptr; + struct symtab_command *symtab_cmd = nullptr; + struct dysymtab_command *dysymtab_cmd = nullptr; + struct dyld_info_command *dyld_info_cmd = nullptr; + + 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; + } + 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; + + 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->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 iterate_symbol_table(char *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 + LOG(1, "> %s", symbol_name); +#endif + if (strcmp(name_pattern, symbol_name) == 0) { + return symtab[i].n_value; + } + if (symbol_name[0] == '_') { + if (strcmp(name_pattern, &symbol_name[1]) == 0) { + return symtab[i].n_value; + } + } + } + } + return 0; +} + +static uintptr_t macho_kit_get_slide(mach_header_t *header) { + uintptr_t slide = 0; + + segment_command_t *curr_seg_cmd; + + 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 (strcmp(curr_seg_cmd->segname, "__TEXT") == 0) { + slide = (uintptr_t) header - curr_seg_cmd->vmaddr; + return slide; + } + } + curr_seg_cmd = (segment_command_t *) ((addr_t) curr_seg_cmd + curr_seg_cmd->cmdsize); + } + return 0; +} + +PUBLIC void *DobbyMachOSymbolResolverOptions(void *header_, const char *symbol_name, bool add_slide) { + mach_header_t *header = (mach_header_t *) header_; + uintptr_t result = 0; + + size_t slide = 0; + + if (add_slide) + slide = macho_kit_get_slide(header); + + // binary symbol table + macho_ctx_t macho_ctx; + memset(&macho_ctx, 0, sizeof(macho_ctx_t)); + macho_ctx_init(&macho_ctx, header); + result = iterate_symbol_table((char *) symbol_name, macho_ctx.symtab, macho_ctx.symtab_cmd->nsyms, macho_ctx.strtab); + if (result) { + result = result + slide; + return (void *) result; + } + return nullptr; +} + +PUBLIC void *DobbyMachOSymbolResolver(void *header_, const char *symbol_name) { + return DobbyMachOSymbolResolverOptions(header_, symbol_name, true); +} + +PUBLIC void *DobbySymbolResolver(const char *image_name, const char *symbol_name_pattern) { + uintptr_t result = 0; + + const std::vector modules = ProcessRuntimeUtility::GetProcessModuleMap(); + + for (auto iter = modules.begin(); iter != modules.end(); iter++) { + auto module = *iter; + // for (auto module : *modules) { + if (image_name != NULL && strnstr(module.path, image_name, strlen(module.path)) == NULL) + continue; + + mach_header_t *header = (mach_header_t *) module.load_address; + if (header == nullptr) + continue; + + size_t slide = 0; + if (header->magic == MH_MAGIC_64) + slide = macho_kit_get_slide(header); + +#if 0 + LOG(0, "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; + memset(&shared_cache_ctx, 0, sizeof(shared_cache_ctx_t)); + shared_cache_ctx_init(&shared_cache_ctx); + } + if (shared_cache_ctx.runtime_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 = iterate_symbol_table((char *) symbol_name_pattern, symtab, symtab_count, strtab); + } + if (result) { + result = result + slide; + break; + } +#endif +#endif + + // binary symbol table + macho_ctx_t macho_ctx; + memset(&macho_ctx, 0, sizeof(macho_ctx_t)); + macho_ctx_init(&macho_ctx, header); + result = iterate_symbol_table((char *) symbol_name_pattern, macho_ctx.symtab, macho_ctx.symtab_cmd->nsyms, + macho_ctx.strtab); + if (result) { + result = result + slide; + break; + } + + // binary exported table(uleb128) + uint64_t flags; + result = iterate_exported_symbol((mach_header_t *) header, symbol_name_pattern, &flags); + if (result) { + switch (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: { + result += (uintptr_t) header; + } + break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: { + result += (uintptr_t) header; + } + break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: { + } + break; + default: + break; + } + return (void *) result; + } + } + +#if !defined(BUILDING_KERNEL) + mach_header_t *dyld_header = NULL; + if (image_name != NULL && strcmp(image_name, "dyld") == 0) { + // task info + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if (task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t) &task_dyld_info, &count)) { + return NULL; + } + + // get dyld load address + const struct dyld_all_image_infos *infos = + (struct dyld_all_image_infos *) (uintptr_t) task_dyld_info.all_image_info_addr; + dyld_header = (mach_header_t *) infos->dyldImageLoadAddress; + + macho_ctx_t dyld_ctx; + memset(&dyld_ctx, 0, sizeof(macho_ctx_t)); + macho_ctx_init(&dyld_ctx, dyld_header); + result = + iterate_symbol_table((char *) symbol_name_pattern, dyld_ctx.symtab, dyld_ctx.symtab_cmd->nsyms, dyld_ctx.strtab); + if (result) { + result = result + (addr_t) dyld_header; + } + } +#endif + + if (result == 0) { + LOG(0, "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..3d69448 --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dobby_symbol_resolver_priv.h @@ -0,0 +1,46 @@ +#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 + +typedef struct macho_ctx { + 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; + + nlist_t *symtab; + char *strtab; + uint32_t *indirect_symtab; + +} macho_ctx_t; + +void macho_ctx_init(macho_ctx_t *ctx, mach_header_t *header); + +uintptr_t iterate_symbol_table(char *name_pattern, nlist_t *symtab, uint32_t symtab_count, char *strtab); diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dyld_shared_cache_symbol_table_iterator.cc b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dyld_shared_cache_symbol_table_iterator.cc new file mode 100644 index 0000000..d7f85fb --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/dyld_shared_cache_symbol_table_iterator.cc @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include + +#include // pthread_once +#include // mmap +#include // open + +#include "SymbolResolver/macho/shared_cache_internal.h" +#include "SymbolResolver/macho/shared-cache/dyld_cache_format.h" + +#include "logging/logging.h" + +#undef LOG_TAG +#define LOG_TAG "DobbySymbolResolverCache" + +#if 0 +extern "C" { +int __shared_region_check_np(uint64_t *startaddress); +} +#endif + +extern "C" const char *dyld_shared_cache_file_path(); + +static pthread_once_t mmap_dyld_shared_cache_once = PTHREAD_ONCE_INIT; + +extern "C" int __shared_region_check_np(uint64_t *startaddress); + +#include + +static char *fast_get_shared_cache_path() { +#if defined(_M_IX86) || defined(__i386__) || defined(_M_X64) || defined(__x86_64__) + return NULL; +#endif + char *result = NULL; + char path_buffer[2048] = {0}; + + const char *path = NULL; + do { + path = dyld_shared_cache_file_path(); + if (path != NULL) { + break; + } else { + struct stat statbuf; + int r = 0; + + path = IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64"; + r = stat(path, &statbuf); + if (r == 0) { + break; + } + path = IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e"; + r = stat(path, &statbuf); + if (r == 0) { + break; + } + path = MACOSX_MRM_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64"; + r = stat(path, &statbuf); + if (r == 0) { + break; + } + path = MACOSX_MRM_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e"; + r = stat(path, &statbuf); + if (r == 0) { + break; + } + } + } while (0); + + if (path != NULL) { + strcpy(path_buffer, path); + result = (char *)malloc(strlen(path_buffer) + 1); + strcpy(result, path_buffer); + } + + return result; +} + +#include +#include +#include +struct dyld_cache_header *shared_cache_get_load_addr() { + static struct dyld_cache_header *shared_cache_load_addr = 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; + shared_cache_load_addr = (struct dyld_cache_header *)infos->sharedCacheBaseAddress; + + return shared_cache_load_addr; + +#if 0 + if (shared_cache_load_addr) + return shared_cache_load_addr; +#if 0 + if (syscall(294, &shared_cache_load_addr) == 0) { +#else + if (__shared_region_check_np((uint64_t *)&shared_cache_load_addr) != 0) { +#endif + shared_cache_load_addr = 0; +} +#endif + return shared_cache_load_addr; +} + +int shared_cache_ctx_init(shared_cache_ctx_t *ctx) { + int fd; + const char *cache_file_path = NULL; + + cache_file_path = fast_get_shared_cache_path(); + if (cache_file_path == NULL) { + return -1; + } + + fd = open(cache_file_path, O_RDONLY, 0); + if (fd == -1) { + return KERN_FAILURE; + } + + struct dyld_cache_header *runtime_shared_cache; + struct dyld_cache_header *mmap_shared_cache; + + // auto align + runtime_shared_cache = shared_cache_get_load_addr(); + if (runtime_shared_cache == NULL) { + return KERN_FAILURE; + } + + // maybe shared cache is apple silicon + if (runtime_shared_cache->localSymbolsSize == 0) { + return KERN_FAILURE; + } + + size_t mmap_length = runtime_shared_cache->localSymbolsSize; + off_t mmap_offset = runtime_shared_cache->localSymbolsOffset; + mmap_shared_cache = + (struct dyld_cache_header *)mmap(0, mmap_length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, mmap_offset); + if (mmap_shared_cache == MAP_FAILED) { + DLOG(0, "mmap shared cache failed"); + return KERN_FAILURE; + } + + // fake shared cache header + mmap_shared_cache = + (struct dyld_cache_header *)((addr_t)mmap_shared_cache - runtime_shared_cache->localSymbolsOffset); + + ctx->runtime_shared_cache = runtime_shared_cache; + ctx->mmap_shared_cache = mmap_shared_cache; + + // shared cache slide + const struct dyld_cache_mapping_info *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; + + // shared cache symbol table + static struct dyld_cache_local_symbols_info *localInfo = NULL; + localInfo = + (struct dyld_cache_local_symbols_info *)((char *)mmap_shared_cache + runtime_shared_cache->localSymbolsOffset); + + static struct dyld_cache_local_symbols_entry *localEntries = NULL; + localEntries = (struct dyld_cache_local_symbols_entry *)((char *)localInfo + localInfo->entriesOffset); + + ctx->local_symbols_info = localInfo; + ctx->local_symbols_entries = localEntries; + + ctx->symtab = (nlist_t *)((char *)localInfo + localInfo->nlistOffset); + ctx->strtab = ((char *)localInfo) + localInfo->stringsOffset; + 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(); + } + + const struct dyld_cache_mapping_info *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); + uintptr_t unslidStart = (uintptr_t)addr - slide; + + // quick out if after end of cache + if (unslidStart > (mappings[2].address + mappings[2].size)) + return false; + + // walk cache regions + const struct dyld_cache_mapping_info *mappingsEnd = &mappings[runtime_shared_cache->mappingCount]; + uintptr_t unslidEnd = unslidStart + length; + for (const struct dyld_cache_mapping_info *m = mappings; m < mappingsEnd; ++m) { + if ((unslidStart >= m->address) && (unslidEnd < (m->address + m->size))) { + 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) { + struct dyld_cache_header *runtime_shared_cache = NULL; + + runtime_shared_cache = ctx->runtime_shared_cache; + + uint64_t textOffsetInCache = (uint64_t)image_header - (uint64_t)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->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]; + +#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; + LOG(1, "dyld image: %s", image_name); +#endif + } + } + *out_symtab = localNlists; + *out_symtab_count = (uint32_t)localNlistCount; + *out_strtab = (char *)ctx->strtab; + return 0; +} 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..29e046b --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared-cache/dyld_cache_format.h @@ -0,0 +1,530 @@ +/* -*- 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 +#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 imagesOffset; // file offset to first dyld_cache_image_info + uint32_t imagesCount; // number of dyld_cache_image_info entries + 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 + uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses + uint32_t branchPoolsCount; // number of uint64_t entries + uint64_t accelerateInfoAddr; // (unslid) address of optimization info + uint64_t accelerateInfoSize; // size of optimization info + 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 of region cache can be mapped into + 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 +}; + +// 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, +}; + +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_patch_info +{ + uint64_t patchTableArrayAddr; // (unslid) address of array for dyld_cache_image_patches for each image + uint64_t patchTableArrayCount; // count of patch table entries + uint64_t patchExportArrayAddr; // (unslid) address of array for patch exports for each image + uint64_t patchExportArrayCount; // count of patch exports entries + uint64_t patchLocationArrayAddr; // (unslid) address of array for patch locations for each patch + uint64_t patchLocationArrayCount;// count of patch location entries + uint64_t patchExportNamesAddr; // blob of strings of export names for patches + uint64_t patchExportNamesSize; // size of string blob of export names for patches +}; + +struct dyld_cache_image_patches +{ + uint32_t patchExportsStartIndex; + uint32_t patchExportsCount; +}; + +struct dyld_cache_patchable_export +{ + uint32_t cacheOffsetOfImpl; + uint32_t patchLocationsStartIndex; + uint32_t patchLocationsCount; + uint32_t exportNameOffset; +}; + +struct dyld_cache_patchable_location +{ + uint64_t cacheOffset : 32, + high7 : 7, + addend : 5, // 0..31 + authenticated : 1, + usesAddressDiversity : 1, + key : 2, + discriminator : 16; +}; + + +// 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/" +#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" + +static const uint64_t kDyldSharedCacheTypeDevelopment = 0; +static const uint64_t kDyldSharedCacheTypeProduction = 1; + + + + +#endif // __DYLD_CACHE_FORMAT__ + + diff --git a/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_internal.h b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_internal.h new file mode 100644 index 0000000..9facadf --- /dev/null +++ b/app/src/main/cpp/Dobby/builtin-plugin/SymbolResolver/macho/shared_cache_internal.h @@ -0,0 +1,70 @@ +#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 + +#if __i386__ +#define ARCH_NAME "i386" +#define ARCH_CACHE_MAGIC "dyld_v1 i386" +#elif __x86_64__ +#define ARCH_NAME "x86_64" +#define ARCH_CACHE_MAGIC "dyld_v1 x86_64" +#define ARCH_NAME_H "x86_64h" +#define ARCH_CACHE_MAGIC_H "dyld_v1 x86_64h" +#elif __ARM_ARCH_7K__ +#define ARCH_NAME "armv7k" +#define ARCH_CACHE_MAGIC "dyld_v1 armv7k" +#elif __ARM_ARCH_7A__ +#define ARCH_NAME "armv7" +#define ARCH_CACHE_MAGIC "dyld_v1 armv7" +#elif __ARM_ARCH_7S__ +#define ARCH_NAME "armv7s" +#define ARCH_CACHE_MAGIC "dyld_v1 armv7s" +#elif __arm64e__ +#define ARCH_NAME "arm64e" +#define ARCH_CACHE_MAGIC "dyld_v1 arm64e" +#elif __arm64__ +#if __LP64__ +#define ARCH_NAME "arm64" +#define ARCH_CACHE_MAGIC "dyld_v1 arm64" +#else +#define ARCH_NAME "arm64_32" +#define ARCH_CACHE_MAGIC "dyld_v1arm64_32" +#endif +#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; + + struct dyld_cache_local_symbols_info *local_symbols_info; + struct dyld_cache_local_symbols_entry *local_symbols_entries; + + nlist_t *symtab; + char *strtab; + +} shared_cache_ctx_t; + +int shared_cache_ctx_init(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); 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..65920a0 --- /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 "common_header.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..b45a229 --- /dev/null +++ b/app/src/main/cpp/Dobby/cmake/compiler_and_linker.cmake @@ -0,0 +1,53 @@ +# :< 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 () + +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..4ea0af6 --- /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 BUILDING_KERNEL)) +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..79ded1e --- /dev/null +++ b/app/src/main/cpp/Dobby/docs/compile.md @@ -0,0 +1,90 @@ +# 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(BUILD_EXAMPLE "Build example" OFF) + +option(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.20.2 --llvm_dir=$HOME/opt/llvm-14.0.0 +``` + +#### Build for android + +``` +# prepare and download cmake/llvm/ndk +sh scripts/setup_linux_cross_compile.sh +sh scripts/setup_linux_cross_compile.sh +python3 scripts/platform_builder.py --platform=linux --arch=all --cmake_dir=$HOME/opt/cmake-3.20.2 --llvm_dir=$HOME/opt/llvm-14.0.0 --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..71e8243 --- /dev/null +++ b/app/src/main/cpp/Dobby/examples/CMakeLists.txt @@ -0,0 +1,17 @@ +add_executable(socket_example + main.cc + socket_example.cc + ) + +target_link_libraries(socket_example + dobby + ) + + +add_library(socket_example_x SHARED + socket_example.cc + ) + +target_link_libraries(socket_example_x + 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..4be7468 --- /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) { + LOG(1, "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()) { + LOG(1, "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() { + void *func = NULL; + log_set_level(0); + + 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) { + LOG(1, "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); + LOG(1, "[server] %s", buffer); + + send(new_socket, hello, strlen(hello), 0); + LOG(1, "[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); + LOG(1, "[client] Hello message sent"); + + int ret = recv(sock, buffer, 1024, 0); + LOG(1, "[client] %s", buffer); + return 0; +} + +#endif 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..beae9c4 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/string_view.h @@ -0,0 +1,147 @@ +/*- + * Copyright 2012-1017 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_STRING_VIEW_H +#define TINYSTL_STRING_VIEW_H + +#include + +namespace tinystl { + + class string_view + { + public: + typedef char value_type; + typedef char* pointer; + typedef const char* const_pointer; + typedef char& reference; + typedef const char& const_reference; + typedef const_pointer iterator; + typedef const_pointer const_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + static constexpr size_type npos = size_type(-1); + + constexpr string_view(); + constexpr string_view(const char* s, size_type count); + constexpr string_view(const char* s); + constexpr string_view(const string_view&) = default; + string_view& operator=(const string_view&) = default; + + constexpr const char* data() const; + constexpr char operator[](size_type pos) const; + constexpr size_type size() const; + constexpr bool empty() const; + constexpr iterator begin() const; + constexpr const_iterator cbegin() const; + constexpr iterator end() const; + constexpr const_iterator cend() const; + constexpr string_view substr(size_type pos = 0, size_type count = npos) const; + constexpr void swap(string_view& v); + + private: + string_view(decltype(nullptr)) = delete; + + static constexpr size_type strlen(const char*); + + const char* m_str; + size_type m_size; + }; + + constexpr string_view::string_view() + : m_str(nullptr) + , m_size(0) + { + } + + constexpr string_view::string_view(const char* s, size_type count) + : m_str(s) + , m_size(count) + { + } + + constexpr string_view::string_view(const char* s) + : m_str(s) + , m_size(strlen(s)) + { + } + + constexpr const char* string_view::data() const { + return m_str; + } + + constexpr char string_view::operator[](size_type pos) const { + return m_str[pos]; + } + + constexpr string_view::size_type string_view::size() const { + return m_size; + } + + constexpr bool string_view::empty() const { + return 0 == m_size; + } + + constexpr string_view::iterator string_view::begin() const { + return m_str; + } + + constexpr string_view::const_iterator string_view::cbegin() const { + return m_str; + } + + constexpr string_view::iterator string_view::end() const { + return m_str + m_size; + } + + constexpr string_view::const_iterator string_view::cend() const { + return m_str + m_size; + } + + constexpr string_view string_view::substr(size_type pos, size_type count) const { + return string_view(m_str + pos, npos == count ? m_size - pos : count); + } + + constexpr void string_view::swap(string_view& v) { + const char* strtmp = m_str; + size_type sizetmp = m_size; + m_str = v.m_str; + m_size = v.m_size; + v.m_str = strtmp; + v.m_size = sizetmp; + } + + constexpr string_view::size_type string_view::strlen(const char* s) { + for (size_t len = 0; ; ++len) { + if (0 == s[len]) { + return len; + } + } + } +} + +#endif // TINYSTL_STRING_VIEW_H diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/traits.h b/app/src/main/cpp/Dobby/external/TINYSTL/traits.h new file mode 100644 index 0000000..84574ee --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/traits.h @@ -0,0 +1,100 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_TRAITS_H +#define TINYSTL_TRAITS_H + +#include + +#if defined(__GNUC__) +# define TINYSTL_TRY_POD_OPTIMIZATION(t) __is_pod(t) +#elif defined(_MSC_VER) +# define TINYSTL_TRY_POD_OPTIMIZATION(t) (!__is_class(t) || __is_pod(t)) +#else +# define TINYSTL_TRY_POD_OPTIMIZATION(t) false +#endif + +namespace tinystl { + template struct pod_traits {}; + + template struct swap_holder; + + template + static inline void move_impl(T& a, T& b, ...) { + a = b; + } + + template + static inline void move_impl(T& a, T& b, T*, swap_holder* = 0) { + a.swap(b); + } + + template + static inline void move(T& a, T&b) { + move_impl(a, b, (T*)0); + } + + template + static inline void move_construct_impl(T* a, T& b, ...) { + new(placeholder(), a) T(b); + } + + template + static inline void move_construct_impl(T* a, T& b, void*, swap_holder* = 0) { + // If your type T does not have a default constructor, simply insert: + // struct tinystl_nomove_construct; + // in the class definition + new(placeholder(), a) T; + a->swap(b); + } + + template + static inline void move_construct_impl(T* a, T& b, T*, typename T::tinystl_nomove_construct* = 0) { + new(placeholder(), a) T(b); + } + + template + static inline void move_construct(T* a, T& b) { + move_construct_impl(a, b, (T*)0); + } + + template + struct remove_reference { + typedef T type; + }; + + template + struct remove_reference { + typedef T type; + }; + + template + struct remove_reference { + typedef T type; + }; +} + +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/unordered_map.h b/app/src/main/cpp/Dobby/external/TINYSTL/unordered_map.h new file mode 100644 index 0000000..4be6a78 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/unordered_map.h @@ -0,0 +1,289 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_UNORDERED_MAP_H +#define TINYSTL_UNORDERED_MAP_H + +#include +#include +#include +#include + +namespace tinystl { + + template + class unordered_map { + public: + unordered_map(); + unordered_map(const unordered_map& other); + unordered_map(unordered_map&& other); + ~unordered_map(); + + unordered_map& operator=(const unordered_map& other); + unordered_map& operator=(unordered_map&& other); + + typedef pair value_type; + + typedef unordered_hash_iterator > const_iterator; + typedef unordered_hash_iterator > iterator; + + iterator begin(); + iterator end(); + + const_iterator begin() const; + const_iterator end() const; + + void clear(); + bool empty() const; + size_t size() const; + + const_iterator find(const Key& key) const; + iterator find(const Key& key); + pair insert(const pair& p); + pair emplace(pair&& p); + void erase(const_iterator where); + + Value& operator[](const Key& key); + + void swap(unordered_map& other); + + private: + + void rehash(size_t nbuckets); + + typedef unordered_hash_node* pointer; + + size_t m_size; + tinystl::buffer m_buckets; + }; + + template + inline unordered_map::unordered_map() + : m_size(0) + { + buffer_init(&m_buckets); + buffer_resize(&m_buckets, 9, 0); + } + + template + inline unordered_map::unordered_map(const unordered_map& other) + : m_size(other.m_size) + { + const size_t nbuckets = (size_t)(other.m_buckets.last - other.m_buckets.first); + buffer_init(&m_buckets); + buffer_resize(&m_buckets, nbuckets, 0); + + for (pointer it = *other.m_buckets.first; it; it = it->next) { + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(it->first, it->second); + newnode->next = newnode->prev = 0; + + unordered_hash_node_insert(newnode, hash(it->first), m_buckets.first, nbuckets - 1); + } + } + + template + inline unordered_map::unordered_map(unordered_map&& other) + : m_size(other.m_size) + { + buffer_move(&m_buckets, &other.m_buckets); + other.m_size = 0; + } + + template + inline unordered_map::~unordered_map() { + if (m_buckets.first != m_buckets.last) + clear(); + buffer_destroy(&m_buckets); + } + + template + inline unordered_map& unordered_map::operator=(const unordered_map& other) { + unordered_map(other).swap(*this); + return *this; + } + + template + inline unordered_map& unordered_map::operator=(unordered_map&& other) { + unordered_map(static_cast(other)).swap(*this); + return *this; + } + + template + inline typename unordered_map::iterator unordered_map::begin() { + iterator it; + it.node = *m_buckets.first; + return it; + } + + template + inline typename unordered_map::iterator unordered_map::end() { + iterator it; + it.node = 0; + return it; + } + + template + inline typename unordered_map::const_iterator unordered_map::begin() const { + const_iterator cit; + cit.node = *m_buckets.first; + return cit; + } + + template + inline typename unordered_map::const_iterator unordered_map::end() const { + const_iterator cit; + cit.node = 0; + return cit; + } + + template + inline bool unordered_map::empty() const { + return m_size == 0; + } + + template + inline size_t unordered_map::size() const { + return m_size; + } + + template + inline void unordered_map::clear() { + pointer it = *m_buckets.first; + while (it) { + const pointer next = it->next; + it->~unordered_hash_node(); + Alloc::static_deallocate(it, sizeof(unordered_hash_node)); + + it = next; + } + + m_buckets.last = m_buckets.first; + buffer_resize(&m_buckets, 9, 0); + m_size = 0; + } + + template + inline typename unordered_map::iterator unordered_map::find(const Key& key) { + iterator result; + result.node = unordered_hash_find(key, m_buckets.first, (size_t)(m_buckets.last - m_buckets.first)); + return result; + } + + template + inline typename unordered_map::const_iterator unordered_map::find(const Key& key) const { + iterator result; + result.node = unordered_hash_find(key, m_buckets.first, (size_t)(m_buckets.last - m_buckets.first)); + return result; + } + + template + inline void unordered_map::rehash(size_t nbuckets) { + if (m_size + 1 > 4 * nbuckets) { + pointer root = *m_buckets.first; + + const size_t newnbuckets = ((size_t)(m_buckets.last - m_buckets.first) - 1) * 8; + m_buckets.last = m_buckets.first; + buffer_resize(&m_buckets, newnbuckets + 1, 0); + unordered_hash_node** buckets = m_buckets.first; + + while (root) { + const pointer next = root->next; + root->next = root->prev = 0; + unordered_hash_node_insert(root, hash(root->first), buckets, newnbuckets); + root = next; + } + } + } + + template + inline pair::iterator, bool> unordered_map::insert(const pair& p) { + pair result; + result.second = false; + + result.first = find(p.first); + if (result.first.node != 0) + return result; + + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(p.first, p.second); + newnode->next = newnode->prev = 0; + + const size_t nbuckets = (size_t)(m_buckets.last - m_buckets.first); + unordered_hash_node_insert(newnode, hash(p.first), m_buckets.first, nbuckets - 1); + + ++m_size; + rehash(nbuckets); + + result.first.node = newnode; + result.second = true; + return result; + } + + template + inline pair::iterator, bool> unordered_map::emplace(pair&& p) { + pair result; + result.second = false; + + result.first = find(p.first); + if (result.first.node != 0) + return result; + + const size_t keyhash = hash(p.first); + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(static_cast(p.first), static_cast(p.second)); + newnode->next = newnode->prev = 0; + + const size_t nbuckets = (size_t)(m_buckets.last - m_buckets.first); + unordered_hash_node_insert(newnode, keyhash, m_buckets.first, nbuckets - 1); + + ++m_size; + rehash(nbuckets); + + result.first.node = newnode; + result.second = true; + return result; + } + + template + inline void unordered_map::erase(const_iterator where) { + unordered_hash_node_erase(where.node, hash(where->first), m_buckets.first, (size_t)(m_buckets.last - m_buckets.first) - 1); + + where->~unordered_hash_node(); + Alloc::static_deallocate((void*)where.node, sizeof(unordered_hash_node)); + --m_size; + } + + template + inline Value& unordered_map::operator[](const Key& key) { + return insert(pair(key, Value())).first->second; + } + + template + inline void unordered_map::swap(unordered_map& other) { + size_t tsize = other.m_size; + other.m_size = m_size, m_size = tsize; + buffer_swap(&m_buckets, &other.m_buckets); + } +} +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/unordered_set.h b/app/src/main/cpp/Dobby/external/TINYSTL/unordered_set.h new file mode 100644 index 0000000..450fbc5 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/unordered_set.h @@ -0,0 +1,265 @@ +/*- + * Copyright 2012-2018 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYSTL_UNORDERED_SET_H +#define TINYSTL_UNORDERED_SET_H + +#include +#include +#include +#include + +namespace tinystl { + + template + class unordered_set { + public: + unordered_set(); + unordered_set(const unordered_set& other); + unordered_set(unordered_set&& other); + ~unordered_set(); + + unordered_set& operator=(const unordered_set& other); + unordered_set& operator=(unordered_set&& other); + + typedef unordered_hash_iterator > const_iterator; + typedef const_iterator iterator; + + iterator begin() const; + iterator end() const; + + void clear(); + bool empty() const; + size_t size() const; + + iterator find(const Key& key) const; + pair insert(const Key& key); + pair emplace(Key&& key); + void erase(iterator where); + size_t erase(const Key& key); + + void swap(unordered_set& other); + + private: + + void rehash(size_t nbuckets); + + typedef unordered_hash_node* pointer; + + size_t m_size; + tinystl::buffer m_buckets; + }; + + template + inline unordered_set::unordered_set() + : m_size(0) + { + buffer_init(&m_buckets); + buffer_resize(&m_buckets, 9, 0); + } + + template + inline unordered_set::unordered_set(const unordered_set& other) + : m_size(other.m_size) + { + const size_t nbuckets = (size_t)(other.m_buckets.last - other.m_buckets.first); + buffer_init(&m_buckets); + buffer_resize(&m_buckets, nbuckets, 0); + + for (pointer it = *other.m_buckets.first; it; it = it->next) { + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(*it); + newnode->next = newnode->prev = 0; + unordered_hash_node_insert(newnode, hash(it->first), m_buckets.first, nbuckets - 1); + } + } + + template + inline unordered_set::unordered_set(unordered_set&& other) + : m_size(other.m_size) + { + buffer_move(&m_buckets, &other.m_buckets); + other.m_size = 0; + } + + template + inline unordered_set::~unordered_set() { + if (m_buckets.first != m_buckets.last) + clear(); + buffer_destroy(&m_buckets); + } + + template + inline unordered_set& unordered_set::operator=(const unordered_set& other) { + unordered_set(other).swap(*this); + return *this; + } + + template + inline unordered_set& unordered_set::operator=(unordered_set&& other) { + unordered_set(static_cast(other)).swap(*this); + return *this; + } + + template + inline typename unordered_set::iterator unordered_set::begin() const { + iterator cit; + cit.node = *m_buckets.first; + return cit; + } + + template + inline typename unordered_set::iterator unordered_set::end() const { + iterator cit; + cit.node = 0; + return cit; + } + + template + inline bool unordered_set::empty() const { + return m_size == 0; + } + + template + inline size_t unordered_set::size() const { + return m_size; + } + + template + inline void unordered_set::clear() { + pointer it = *m_buckets.first; + while (it) { + const pointer next = it->next; + it->~unordered_hash_node(); + Alloc::static_deallocate(it, sizeof(unordered_hash_node)); + + it = next; + } + + m_buckets.last = m_buckets.first; + buffer_resize(&m_buckets, 9, 0); + m_size = 0; + } + + template + inline typename unordered_set::iterator unordered_set::find(const Key& key) const { + iterator result; + result.node = unordered_hash_find(key, m_buckets.first, (size_t)(m_buckets.last - m_buckets.first)); + return result; + } + + template + inline void unordered_set::rehash(size_t nbuckets) { + if (m_size + 1 > 4 * nbuckets) { + pointer root = *m_buckets.first; + + const size_t newnbuckets = ((size_t)(m_buckets.last - m_buckets.first) - 1) * 8; + m_buckets.last = m_buckets.first; + buffer_resize(&m_buckets, newnbuckets + 1, 0); + unordered_hash_node** buckets = m_buckets.first; + + while (root) { + const pointer next = root->next; + root->next = root->prev = 0; + unordered_hash_node_insert(root, hash(root->first), buckets, newnbuckets); + root = next; + } + } + } + + template + inline pair::iterator, bool> unordered_set::insert(const Key& key) { + pair result; + result.second = false; + + result.first = find(key); + if (result.first.node != 0) + return result; + + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(key); + newnode->next = newnode->prev = 0; + + const size_t nbuckets = (size_t)(m_buckets.last - m_buckets.first); + unordered_hash_node_insert(newnode, hash(key), m_buckets.first, nbuckets - 1); + + ++m_size; + rehash(nbuckets); + + result.first.node = newnode; + result.second = true; + return result; + } + + template + inline pair::iterator, bool> unordered_set::emplace(Key&& key) { + pair result; + result.second = false; + + result.first = find(key); + if (result.first.node != 0) + return result; + + const size_t keyhash = hash(key); + unordered_hash_node* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node))) unordered_hash_node(static_cast(key)); + newnode->next = newnode->prev = 0; + + const size_t nbuckets = (size_t)(m_buckets.last - m_buckets.first); + unordered_hash_node_insert(newnode, keyhash, m_buckets.first, nbuckets - 1); + + ++m_size; + rehash(nbuckets); + + result.first.node = newnode; + result.second = true; + return result; + } + + template + inline void unordered_set::erase(iterator where) { + unordered_hash_node_erase(where.node, hash(where.node->first), m_buckets.first, (size_t)(m_buckets.last - m_buckets.first) - 1); + + where.node->~unordered_hash_node(); + Alloc::static_deallocate((void*)where.node, sizeof(unordered_hash_node)); + --m_size; + } + + template + inline size_t unordered_set::erase(const Key& key) { + const iterator it = find(key); + if (it.node == 0) + return 0; + + erase(it); + return 1; + } + + template + void unordered_set::swap(unordered_set& other) { + size_t tsize = other.m_size; + other.m_size = m_size, m_size = tsize; + buffer_swap(&m_buckets, &other.m_buckets); + } +} +#endif diff --git a/app/src/main/cpp/Dobby/external/TINYSTL/vector.h b/app/src/main/cpp/Dobby/external/TINYSTL/vector.h new file mode 100644 index 0000000..32eae1f --- /dev/null +++ b/app/src/main/cpp/Dobby/external/TINYSTL/vector.h @@ -0,0 +1,336 @@ +/*- + * 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 { + 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& param); + + void shrink_to_fit(); + + void swap(vector& other); + + typedef T value_type; + + typedef T* iterator; + 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& param); + + iterator erase(iterator where); + iterator erase(iterator first, iterator last); + + iterator erase_unordered(iterator where); + iterator erase_unordered(iterator first, iterator last); + + 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& param) { + 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& param) { + buffer_insert(&m_buffer, where, ¶m, ¶m + 1); + } +} + +#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..115c9fd --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories(.) + +if(NOT BUILDING_KERNEL) + 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..07ca440 --- /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)) + RAW_LOG(0, "\n"); + RAW_LOG(0, "%02X ", bytes[ix]); + } + RAW_LOG(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..3f9a434 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/deprecated/misc-helper/misc-helper/format_printer.h @@ -0,0 +1,3 @@ +#include "common_header.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..d83925d --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/CMakeLists.txt @@ -0,0 +1,17 @@ +include_directories(.) + +if(NOT BUILDING_KERNEL) + set(SOURCE_FILE_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/cxxlogging.cc + ${CMAKE_CURRENT_SOURCE_DIR}/logging.c + ) +else() + set(SOURCE_FILE_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/cxxlogging.cc + ${CMAKE_CURRENT_SOURCE_DIR}/kernel_logging.c + ) +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/cxxlogging.cc b/app/src/main/cpp/Dobby/external/logging/cxxlogging.cc new file mode 100644 index 0000000..430b4f9 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/cxxlogging.cc @@ -0,0 +1,36 @@ +#include "logging/cxxlogging.h" + +#if 1 || defined(BUILDING_KERNEL) +void Logger::setLogLevel(LogLevel level) { + log_level_ = level; +} + +void Logger::log(LogLevel level, const char *tag, const char *fmt, ...) { + +} + +void Logger::LogFatal(const char *fmt, ...) { +} +#else +#include +#include +#include +#include + +void Logger::setLogLevel(LogLevel level) { + log_level_ = level; +} + +void Logger::log(LogLevel level, const char *tag, const char *fmt, ...) { + if (level > log_level_) { + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + } +} + +void Logger::LogFatal(const char *fmt, ...) { +} +#endif diff --git a/app/src/main/cpp/Dobby/external/logging/kernel_logging.c b/app/src/main/cpp/Dobby/external/logging/kernel_logging.c new file mode 100644 index 0000000..996f9f9 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/kernel_logging.c @@ -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.c b/app/src/main/cpp/Dobby/external/logging/logging.c new file mode 100644 index 0000000..2a9ebc7 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/logging.c @@ -0,0 +1,124 @@ +#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 +#endif + +#if defined(_WIN32) +#define PUBLIC +#else +#define PUBLIC __attribute__((visibility("default"))) +#define INTERNAL __attribute__((visibility("internal"))) +#endif + +static int g_log_level = 1; +static char g_log_tag[64] = {0}; +static bool time_tag_enabled = false; +static bool syslog_enabled = false; +static bool file_log_enabled = false; +static const char *log_file_path = NULL; +static int log_file_fd = -1; +static FILE *log_file_stream = NULL; + +PUBLIC void log_set_level(int level) { + g_log_level = level; +} + +PUBLIC void log_set_tag(const char *tag) { + sprintf(g_log_tag, "[%s] ", tag); +} + +PUBLIC void log_enable_time_tag() { + time_tag_enabled = true; +} + +PUBLIC void log_switch_to_syslog(void) { + syslog_enabled = 1; +} + +PUBLIC void log_switch_to_file(const char *path) { + file_log_enabled = 1; + log_file_path = strdup(path); + log_file_stream = fopen(log_file_path, "w+"); + if (log_file_stream == NULL) { + file_log_enabled = false; + ERROR_LOG("open log file %s failed, %s", path, strerror(errno)); + } +} + +PUBLIC int log_internal_impl(int level, const char *fmt, ...) { + if (level < g_log_level) + return 0; + + char buffer[4096] = {0}; + + if (g_log_tag[0] != 0) { + snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer), "%s ", g_log_tag); + } + + if (time_tag_enabled) { + time_t now = time(NULL); + struct tm *tm = localtime(&now); + snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(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(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer), "%s\n", fmt); + + fmt = buffer; + + va_list ap; + va_start(ap, fmt); + +#pragma clang diagnostic ignored "-Wformat" + + if (syslog_enabled) { +#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, ap, os_log_with_args); +#elif defined(_POSIX_VERSION) + vsyslog(LOG_ERR, fmt, ap); +#endif + } + + if (file_log_enabled) { + char buffer[4096] = {0}; + vsnprintf(buffer, 4096 - 1, fmt, ap); + if (fwrite(buffer, sizeof(char), strlen(buffer) + 1, log_file_stream) == -1) { + file_log_enabled = false; + } + fflush(log_file_stream); + } + + if (!syslog_enabled && !file_log_enabled) { +#if defined(__ANDROID__) +#define ANDROID_LOG_TAG "Dobby" +#include + __android_log_vprint(ANDROID_LOG_INFO, ANDROID_LOG_TAG, fmt, ap); +#else + vprintf(fmt, ap); +#endif + } + +#pragma clang diagnostic warning "-Wformat" + va_end(ap); + return 0; +} 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..d13e947 --- /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("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("%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(" Check failed: %s.\n", #lhs " " #op " " #rhs); \ + } \ + } while (0) + +#define DCHECK_OP(name, op, lhs, rhs) \ + do { \ + if (!((lhs)op(rhs))) { \ + FATAL("%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/cxxlogging.h b/app/src/main/cpp/Dobby/external/logging/logging/cxxlogging.h new file mode 100644 index 0000000..e41b149 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/logging/cxxlogging.h @@ -0,0 +1,15 @@ +#pragma once + +#include "logging.h" + +class Logger { +public: + void setLogLevel(LogLevel level); + + void log(LogLevel level, const char *tag, const char *fmt, ...); + + void LogFatal(const char *fmt, ...); + +private: + LogLevel log_level_; +}; \ No newline at end of file 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..8bf9075 --- /dev/null +++ b/app/src/main/cpp/Dobby/external/logging/logging/logging.h @@ -0,0 +1,88 @@ +#pragma once + +#define LOG_TAG NULL + +typedef enum { + LOG_LEVEL_VERBOSE = 0, + LOG_LEVEL_DEBUG = 1, + LOG_LEVEL_INFO = 2, + LOG_LEVEL_WARN = 3, + LOG_LEVEL_ERROR = 4, + LOG_LEVEL_FATAL = 5 +} LogLevel; + +#if 1 +#ifdef __cplusplus +extern "C" { +#endif + +void log_set_level(int level); + +void log_set_tag(const char *tag); + +void log_enable_time_tag(); + +void log_switch_to_syslog(); + +void log_switch_to_file(const char *path); + +#if !defined(LOG_FUNCTION_IMPL) +#define LOG_FUNCTION_IMPL log_internal_impl +#endif + +int log_internal_impl(int level, const char *, ...); + +#if defined(LOGGING_DISABLE) +#define LOG_FUNCTION_IMPL(...) +#endif + +#ifdef __cplusplus +} +#endif +#else +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif +#endif + +#define RAW_LOG(level, fmt, ...) \ + do { \ + LOG_FUNCTION_IMPL(level, fmt, ##__VA_ARGS__); \ + } while (0) + +#define LOG(level, fmt, ...) \ + do { \ + if (LOG_TAG) \ + LOG_FUNCTION_IMPL(level, "[%s] " fmt, LOG_TAG, ##__VA_ARGS__); \ + else \ + LOG_FUNCTION_IMPL(level, fmt, ##__VA_ARGS__); \ + } while (0) + +#define INFO_LOG(fmt, ...) \ + do { \ + LOG(LOG_LEVEL_INFO, "[*] " 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(fmt, ...) \ + do { \ + LOG(LOG_LEVEL_ERROR, "[!] [%s:%d:%s]" fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + assert(0); \ + } while (0) + +#if defined(LOGGING_DEBUG) +#define DLOG(level, fmt, ...) LOG(level, fmt, ##__VA_ARGS__) +#else +#define DLOG(level, fmt, ...) +#endif + +#define UNIMPLEMENTED() FATAL("%s\n", "unimplemented code!!!") +#define UNREACHABLE() FATAL("%s\n", "unreachable code!!!") \ 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 64% rename from app/src/main/cpp/dobby/dobby.h rename to app/src/main/cpp/Dobby/include/dobby.h index 484d314..45ec88b 100644 --- a/app/src/main/cpp/dobby/dobby.h +++ b/app/src/main/cpp/Dobby/include/dobby.h @@ -1,152 +1,182 @@ -#ifndef dobby_h -#define dobby_h - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -typedef uintptr_t addr_t; -typedef uint32_t addr32_t; -typedef uint64_t addr64_t; - -typedef void *dobby_dummy_func_t; -typedef void *asm_func_t; - -#if defined(__arm__) -typedef struct { - uint32_t dummy_0; - uint32_t dummy_1; - - uint32_t dummy_2; - uint32_t sp; - - union { - uint32_t r[13]; - struct { - uint32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12; - } regs; - } general; - - uint32_t lr; -} DobbyRegisterContext; -#elif defined(__arm64__) || defined(__aarch64__) -#define ARM64_TMP_REG_NDX_0 17 - -typedef union _FPReg { - __int128_t q; - struct { - double d1; - double d2; - } d; - struct { - float f1; - float f2; - float f3; - float f4; - } f; -} FPReg; - -// register context -typedef struct { - uint64_t dmmpy_0; // dummy placeholder - uint64_t sp; - - uint64_t dmmpy_1; // dummy placeholder - union { - uint64_t x[29]; - struct { - uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, - x23, x24, x25, x26, x27, x28; - } regs; - } general; - - uint64_t fp; - uint64_t lr; - - union { - FPReg q[32]; - struct { - FPReg q0, q1, q2, q3, q4, q5, q6, q7; - // [!!! READ ME !!!] - // for Arm64, can't access q8 - q31, unless you enable full floating-point register pack - FPReg q8, q9, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21, q22, q23, q24, q25, q26, q27, q28, q29, - q30, q31; - } regs; - } floating; -} DobbyRegisterContext; -#elif defined(_M_IX86) || defined(__i386__) -typedef struct _RegisterContext { - uint32_t dummy_0; - uint32_t esp; - - uint32_t dummy_1; - uint32_t flags; - - union { - struct { - uint32_t eax, ebx, ecx, edx, ebp, esp, edi, esi; - } regs; - } general; - -} DobbyRegisterContext; -#elif defined(_M_X64) || defined(__x86_64__) -typedef struct { - uint64_t dummy_0; - uint64_t rsp; - - union { - struct { - uint64_t rax, rbx, rcx, rdx, rbp, rsp, rdi, rsi, r8, r9, r10, r11, r12, r13, r14, r15; - } regs; - } general; - - uint64_t dummy_1; - uint64_t flags; -} DobbyRegisterContext; -#endif - -#define install_hook_name(name, fn_ret_t, fn_args_t...) \ - static fn_ret_t fake_##name(fn_args_t); \ - static fn_ret_t (*orig_##name)(fn_args_t); \ - /* __attribute__((constructor)) */ static void install_hook_##name(void *sym_addr) { \ - DobbyHook(sym_addr, (dobby_dummy_func_t)fake_##name, (dobby_dummy_func_t *)&orig_##name); \ - return; \ - } \ - fn_ret_t fake_##name(fn_args_t) - -// memory code patch -int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size); - -// function inline hook -int DobbyHook(void *address, dobby_dummy_func_t replace_func, dobby_dummy_func_t *origin_func); - -// dynamic binary instruction instrument -// for Arm64, can't access q8 - q31, unless enable full floating-point register pack -typedef void (*dobby_instrument_callback_t)(void *address, DobbyRegisterContext *ctx); -int DobbyInstrument(void *address, dobby_instrument_callback_t pre_handler); - -// destroy and restore code patch -int DobbyDestroy(void *address); - -const char *DobbyGetVersion(); - -// symbol resolver -void *DobbySymbolResolver(const char *image_name, const char *symbol_name); - -// import table replace -int DobbyImportTableReplace(char *image_name, char *symbol_name, dobby_dummy_func_t fake_func, - dobby_dummy_func_t *orig_func); - -// for arm, Arm64, try use b xxx instead of ldr absolute indirect branch -// for x86, x64, always use absolute indirect jump -void dobby_enable_near_branch_trampoline(); -void dobby_disable_near_branch_trampoline(); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef dobby_h +#define dobby_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void log_set_level(int level); +void log_set_tag(const char *tag); +void log_enable_time_tag(); +void log_switch_to_syslog(); +void log_switch_to_file(const char *path); + +typedef enum { + kMemoryOperationSuccess, + kMemoryOperationError, + kNotSupportAllocateExecutableMemory, + kNotEnough, + kNone +} MemoryOperationError; + +typedef uintptr_t addr_t; +typedef uint32_t addr32_t; +typedef uint64_t addr64_t; +typedef void (*dobby_dummy_func_t)(); +typedef void (*asm_func_t)(); + +MemoryOperationError DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size); + +#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 + +#if defined(TARGET_ARCH_ARM) +typedef struct { + uint32_t dummy_0; + uint32_t dummy_1; + + uint32_t dummy_2; + uint32_t sp; + + union { + uint32_t r[13]; + struct { + uint32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12; + } regs; + } general; + + uint32_t lr; +} DobbyRegisterContext; +#elif defined(TARGET_ARCH_ARM64) +#define ARM64_TMP_REG_NDX_0 17 + +typedef union _FPReg { + __int128_t q; + struct { + double d1; + double d2; + } d; + struct { + float f1; + float f2; + float f3; + float f4; + } f; +} FPReg; + +// register context +typedef struct { + uint64_t dmmpy_0; // dummy placeholder + uint64_t sp; + + uint64_t dmmpy_1; // dummy placeholder + union { + uint64_t x[29]; + struct { + uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, + x23, x24, x25, x26, x27, x28; + } regs; + } general; + + uint64_t fp; + uint64_t lr; + + union { + FPReg q[32]; + struct { + FPReg q0, q1, q2, q3, q4, q5, q6, q7; + // [!!! READ ME !!!] + // for Arm64, can't access q8 - q31, unless you enable full floating-point register pack + FPReg q8, q9, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21, q22, q23, q24, q25, q26, q27, q28, q29, + q30, q31; + } regs; + } floating; +} DobbyRegisterContext; +#elif defined(TARGET_ARCH_IA32) +typedef struct _RegisterContext { + uint32_t dummy_0; + uint32_t esp; + + uint32_t dummy_1; + uint32_t flags; + + union { + struct { + uint32_t eax, ebx, ecx, edx, ebp, esp, edi, esi; + } regs; + } general; + +} DobbyRegisterContext; +#elif defined(TARGET_ARCH_X64) +typedef struct { + uint64_t dummy_0; + uint64_t rsp; + + union { + struct { + uint64_t rax, rbx, rcx, rdx, rbp, rsp, rdi, rsi, r8, r9, r10, r11, r12, r13, r14, r15; + } regs; + } general; + + uint64_t dummy_1; + uint64_t flags; +} DobbyRegisterContext; +#endif + +#define RT_FAILED -1 +#define RT_SUCCESS 0 +typedef enum { RS_FAILED = -1, RS_SUCCESS = 0 } RetStatus; + +// DobbyWrap <==> DobbyInstrument, so use DobbyInstrument instead of DobbyWrap +#if 0 +// wrap function with pre_call and post_call +typedef void (*PreCallTy)(DobbyRegisterContext *ctx, const InterceptEntry *info); +typedef void (*PostCallTy)(DobbyRegisterContext *ctx, const InterceptEntry *info); +int DobbyWrap(void *function_address, PreCallTy pre_call, PostCallTy post_call); +#endif + +// function inline hook +int DobbyHook(void *address, dobby_dummy_func_t replace_func, dobby_dummy_func_t *origin_func); + +// dynamic binary instruction instrument +// [!!! READ ME !!!] +// for Arm64, can't access q8 - q31, unless enable full floating-point register pack +typedef void (*dobby_instrument_callback_t)(void *address, DobbyRegisterContext *ctx); +int DobbyInstrument(void *address, dobby_instrument_callback_t pre_handler); + +int DobbyDestroy(void *address); + +const char *DobbyGetVersion(); + +void *DobbySymbolResolver(const char *image_name, const char *symbol_name); + +int DobbyImportTableReplace(char *image_name, char *symbol_name, dobby_dummy_func_t fake_func, + dobby_dummy_func_t *orig_func); + +// [!!! READ ME !!!] +// for arm, Arm64, dobby will try use b xxx instead of ldr absolute indirect branch +// for x64, dobby always use absolute indirect jump +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) +void dobby_enable_near_branch_trampoline(); +void dobby_disable_near_branch_trampoline(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/Dobby/scripts/Dockerfile b/app/src/main/cpp/Dobby/scripts/Dockerfile new file mode 100644 index 0000000..149f448 --- /dev/null +++ b/app/src/main/cpp/Dobby/scripts/Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:focal + +ARG DEBIAN_FRONTEND='noninteractive' + +RUN apt-key adv --keyserver 'keyserver.ubuntu.com' --recv-key 'C99B11DEB97541F0' && + apt-add-repository -y -u 'https://cli.github.com/packages' && + apt-add-repository 'deb https://apt.kitware.com/ubuntu/ focal main' + +ADD setup_linux_cross_compile.sh /root/setup_linux_cross_compile.sh +RUN sh /root/setup_linux_cross_compile.sh diff --git a/app/src/main/cpp/Dobby/scripts/platform_builder.py b/app/src/main/cpp/Dobby/scripts/platform_builder.py new file mode 100644 index 0000000..ede9d30 --- /dev/null +++ b/app/src/main/cpp/Dobby/scripts/platform_builder.py @@ -0,0 +1,240 @@ +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 = "" + 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": + self.cmake_args += ["-DDOBBY_GENERATE_SHARED=ON"] + elif self.library_build_type == "static": + 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(["make", "-j8", "dobby"], cwd=self.cmake_build_dir, check=True) + + os.system(f"mkdir -p {self.output_dir}") + os.system(f"cp {self.cmake_build_dir}/{self.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) + + if self.library_build_type == "shared": + self.output_name = "libdobby.so" + else: + self.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) + + if self.library_build_type == "shared": + self.output_name = "libdobby.so" + else: + self.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"] + + if self.library_build_type == "shared": + self.output_name = "libdobby.dylib" + else: + self.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) + + cmd = ["lipo", "-create"] + files + ["-output", f"{project_dir}/build/{platform}/{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)) + + 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": + output_name = "libdobby.dylib" if library_build_type == "shared" else "libdobby.a" + DarwinPlatformBuilder.lipo_create_fat(project_dir, platform, 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..9900810 --- /dev/null +++ b/app/src/main/cpp/Dobby/scripts/setup_linux_cross_compile.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# if error, exit +set - + +sudo apt update +sudo apt-get install -y \ + apt-utils \ + build-essential \ + curl \ + wget \ + unzip \ + gcc-multilib \ + make \ + zsh + +mkdir -p ~/opt + +cd ~/opt +CMAKE_VERSION=3.20.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=14.0.0 +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..594877b --- /dev/null +++ b/app/src/main/cpp/Dobby/scripts/setup_macos_cross_compile.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# if error, exit +set - + +mkdir -p ~/opt + +cd ~/opt +CMAKE_VERSION=3.20.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=14.0.0 +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..fb807ba --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/ExecMemory/code-patch-tool-darwin.cc @@ -0,0 +1,109 @@ +#include "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 MemoryOperationError DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size) { + if (address == nullptr || buffer == nullptr || buffer_size == 0) { + FATAL("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); + LOG(0, "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; + } + + if (0) { + vm_map_t self_task = kernel_map; + + int page_size = PAGE_SIZE; + addr_t page_aligned_address = ALIGN_FLOOR(address, page_size); + int offset = (int)((addr_t)address - page_aligned_address); + + mach_vm_address_t remap_dummy_page = 0; + kr = mach_vm_allocate(self_task, &remap_dummy_page, page_size, VM_FLAGS_ANYWHERE); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + // copy original page + memcpy((void *)remap_dummy_page, (void *)page_aligned_address, page_size); + + // patch buffer + memcpy((void *)(remap_dummy_page + offset), buffer, buffer_size); + + // change permission + kr = mach_vm_protect(self_task, remap_dummy_page, page_size, false, VM_PROT_READ | VM_PROT_EXECUTE); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + static boolean_t (*vm_map_lookup_entry)(vm_map_t map, vm_map_offset_t address, vm_map_entry_t * entry) = nullptr; + if (vm_map_lookup_entry == nullptr) + vm_map_lookup_entry = (typeof(vm_map_lookup_entry))DobbySymbolResolver(nullptr, "_vm_map_lookup_entry"); + + vm_map_entry_t entry; + kr = vm_map_lookup_entry(kernel_map, (vm_map_offset_t)address, &entry); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + struct vm_map_entry_flags { + unsigned int dummy_bits : 17, permanent; + }; + struct vm_map_entry_flags *flags = (typeof(flags))((addr_t)entry + 0x48); + if (flags->permanent) { + flags->permanent = 0; + } + + mach_vm_address_t remap_dest_page = page_aligned_address; + vm_prot_t curr_protection, max_protection; + kr = mach_vm_remap(self_task, &remap_dest_page, page_size, 0, VM_FLAGS_OVERWRITE | VM_FLAGS_FIXED, self_task, + remap_dummy_page, TRUE, &curr_protection, &max_protection, VM_INHERIT_COPY); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + kr = mach_vm_deallocate(self_task, remap_dummy_page, page_size); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + ClearCache(address, (void *)((addr_t)address + buffer_size)); + flush_dcache((vm_offset_t)address, (vm_size_t)buffer_size, 0); + invalidate_icache((vm_offset_t)address, (vm_size_t)buffer_size, 0); + } + + return kMemoryOperationSuccess; +} 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..94745ed --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc @@ -0,0 +1,131 @@ +#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 std::vector regions; +const std::vector &ProcessRuntimeUtility::GetProcessMemoryLayout() { + return regions; +} + +// --- + +#include + +extern "C" void *kernel_info_load_base();; + +std::vector modules; +const std::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; + } + LOG(0, "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; + LOG(0, "gLoadedKextSummaries at: %p", _gLoadedKextSummaries); + } + + // only kernel + RuntimeModule module = {0}; + strncpy(module.path, "kernel", sizeof(module.path)); + 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)); + module.load_address = (void *)_gLoadedKextSummaries->summaries[i].address; + modules.push_back(module); + } + + return &modules; +} + +RuntimeModule ProcessRuntimeUtility::GetProcessModule(const char *name) { + const std::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..4bd15c4 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/KernelMode/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 std::vector &GetProcessMemoryLayout(); + + static const std::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..723a460 --- /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 "common_header.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..086cf6a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-darwin.cc @@ -0,0 +1,81 @@ +#include "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); + +PUBLIC MemoryOperationError DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size) { + if (address == nullptr || buffer == nullptr || buffer_size == 0) { + FATAL("invalid argument"); + return kMemoryOperationError; + } + + int 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) { + MemoryOperationError err = kMemoryOperationSuccess; + + void *address_a = address; + uint8_t *buffer_a = buffer; + uint32_t buffer_size_a = (patch_page + page_size - (addr_t)address); + err = DobbyCodePatch(address_a, buffer_a, buffer_size_a); + if (err != kMemoryOperationSuccess) { + return err; + } + + 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; + err = DobbyCodePatch(address_b, buffer_b, buffer_size_b); + return err; + } + + kern_return_t kr; + vm_map_t self_task = mach_task_self(); + + mach_vm_address_t remap_dummy_page = 0; + kr = mach_vm_allocate(self_task, &remap_dummy_page, page_size, VM_FLAGS_ANYWHERE); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + // copy original page + memcpy((void *)remap_dummy_page, (void *)patch_page, page_size); + + // patch buffer + int offset = (int)((addr_t)address - patch_page); + memcpy((void *)(remap_dummy_page + offset), buffer, buffer_size); + + // change permission + kr = mach_vm_protect(self_task, remap_dummy_page, page_size, false, VM_PROT_READ | VM_PROT_EXECUTE); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + mach_vm_address_t remap_dest_page = patch_page; + vm_prot_t curr_protection, max_protection; + kr = mach_vm_remap(self_task, &remap_dest_page, page_size, 0, VM_FLAGS_OVERWRITE | VM_FLAGS_FIXED, self_task, + remap_dummy_page, TRUE, &curr_protection, &max_protection, VM_INHERIT_COPY); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + kr = mach_vm_deallocate(self_task, remap_dummy_page, page_size); + KERN_RETURN_ERROR(kr, kMemoryOperationError); + + ClearCache(address, (void *)((addr_t)address + buffer_size)); + + return kMemoryOperationSuccess; +} 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..ccb948b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-posix.cc @@ -0,0 +1,37 @@ + +#include "dobby_internal.h" +#include "core/arch/Cpu.h" + +#include +#include +#include + +#if !defined(__APPLE__) +PUBLIC MemoryOperationError 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 kMemoryOperationSuccess; +} + +#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..21ec8e2 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/code-patch-tool-windows.cc @@ -0,0 +1,27 @@ +#include "dobby_internal.h" + +#include + +using namespace zz; + +PUBLIC MemoryOperationError 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 kMemoryOperationSuccess; +} 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..f8b1640 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/ExecMemory/substrated/mach_interface_support/substrated.defs @@ -0,0 +1,24 @@ +/* + * 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..6538d8f --- /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_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 { + std::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..07ab59d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc @@ -0,0 +1,132 @@ +#include "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); +} + +std::vector regions; + +const std::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 + DLOG(0, "%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 std::vector *modules; + +const std::vector &ProcessRuntimeUtility::GetProcessModuleMap() { + if (modules == nullptr) { + modules = new std::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)); + module.load_address = 0; + 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)); + module.load_address = (void *)info->imageLoadAddress; + modules->push_back(module); + } + } + + 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..218b8ec --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc @@ -0,0 +1,244 @@ +#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); +} + +std::vector regions; +const std::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) { + FATAL("/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 + DLOG(0, "%p --- %p", region_start, region_end); +#endif + + MemRegion region = MemRegion(region_start,region_end - region_start, permission); + regions.push_back(region); + } + std::qsort(®ions[0], regions.size(), sizeof(MemRegion), + +[](const void* a, const void* b) -> int { + const auto *i = static_cast(a); + const auto *j = static_cast(b); + if ((addr_t)i->start < (addr_t)j->start) return -1; + if ((addr_t)i->start > (addr_t)j->start) return 1; + return 0; + }); + + fclose(fp); + return regions; +} + +// ================================================================ +// GetProcessModuleMap + +static std::vector *modules; +static std::vector &get_process_map_with_proc_maps() { + if(modules == nullptr) { + modules = new std::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) { + FATAL("/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)); + module.load_address = (void *)region_start; + modules->push_back(module); + +#if 0 + DLOG(0, "module: %s", module.path); +#endif + } + + fclose(fp); + return *modules; +} + +#if defined(__LP64__) +static std::vector get_process_map_with_linker_iterator() { + std::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 std::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}; +} 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..190c6b1 --- /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 std::vector &GetProcessMemoryLayout(); + + static const std::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..96235b8 --- /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 + +std::vector ProcessMemoryLayout; +std::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 + +std::vector ProcessModuleMap; + +std::vector ProcessRuntimeUtility::GetProcessModuleMap() { + if (!ProcessMemoryLayout.empty()) { + ProcessMemoryLayout.clear(); + } + return ProcessModuleMap; +} + +RuntimeModule ProcessRuntimeUtility::GetProcessModule(const char *name) { + std::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..ea03091 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/Thread/PlatformThread.h @@ -0,0 +1,36 @@ +#ifndef USER_MODE_PLATFORM_THREAD_H +#define USER_MODE_PLATFORM_THREAD_H + +#include "common_header.h" + +namespace zz { + +class OSThread { +public: + typedef int LocalStorageKey; + + static int GetCurrentProcessId(); + + static int GetCurrentThreadId(); + + // Thread-local storage. + 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 + +#endif \ 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..00cab99 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-posix.cc @@ -0,0 +1,205 @@ +#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; + +// ================================================================ +// base :: Thread + +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) { + FATAL("pthread create failed"); + return false; + } + return true; +} + +Thread::Thread(const char *name) { + strncpy(name_, name, sizeof(name_)); +} + +bool Thread::Start() { + if (ThreadInterface::Create(this, &handle_) == false) { + return false; + } + return true; +} + +// --- 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(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; +} + +// --- OSPrint + +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 +} + +void OSPrint::PrintError(const char *format, ...) { + va_list args; + va_start(args, format); + VPrintError(format, args); + va_end(args); +} + +void OSPrint::VPrintError(const char *format, va_list args) { +#if defined(ANDROID) && !defined(ANDROID_LOG_STDOUT) + __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args); +#else + vfprintf(stderr, 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..ef23325 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform-windows.cc @@ -0,0 +1,87 @@ +#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..f54ddc9 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Backend/UserMode/UnifiedInterface/platform.h @@ -0,0 +1,109 @@ +#ifndef PLATFORM_INTERFACE_COMMON_PLATFORM_H +#define PLATFORM_INTERFACE_COMMON_PLATFORM_H + +#include +#include + +namespace base { +// ================================================================ +// base :: ThreadLocalStorageInterface + +class ThreadLocalStorageInterface { + using LocalStorageKey = int32_t; + + // Thread-local storage. + 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; + } +}; + +// ================================================================ +// base :: Thread + +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 *); +}; + +class Thread : public ThreadInterface, public ThreadInterface::Delegate { +public: + Thread(const char *name); + + bool Start(); + +private: + ThreadHandle handle_; + + char name_[256]; +}; +} // namespace base + +// ================================================================ +// 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); +}; + +// ================================================================ +// base :: OSPrint + +class OSPrint { +public: + // Print output to console. This is mostly used for debugging output. + // On platforms that has standard terminal output, the output + // should go to stdout. + static void Print(const char *format, ...); + + static void VPrint(const char *format, va_list args); + + // Print error output to console. This is mostly used for error message + // output. On platforms that has standard terminal output, the output + // should go to stderr. + static void PrintError(const char *format, ...); + + static void VPrintError(const char *format, va_list args); +}; + +#endif 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..12a15e0 --- /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..ebd0121 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/InstructionRelocation.h @@ -0,0 +1,5 @@ +#include "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..9b836c7 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.cc @@ -0,0 +1,917 @@ +#include "platform_macro.h" + +#if defined(TARGET_ARCH_ARM) + +#include "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; + + vmaddr_t src_vmaddr; + vmaddr_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) + DLOG(0, "%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 = new RelocLabel(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) { + DLOG(0, "%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); + RelocLabel *pseudoDataLabel = new RelocLabel(dst_vmaddr); + _ AppendRelocLabel(pseudoDataLabel); + + _ Ldr(regRd, pseudoDataLabel); + + 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) { + DLOG(0, "%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) { + DLOG(0, "%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 = new ThumbRelocLabelEntry(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()) { + DLOG(0, "%d:relo at %p", ctx->relocated_offset_map.size(), + relo_cur_src_vmaddr(ctx)); + + vmaddr_t dst_vmaddr = relo_cur_src_vmaddr(ctx); + auto label = new ThumbRelocLabelEntry(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()) { + DLOG(0, "%d:relo at %p", ctx->relocated_offset_map.size(), + relo_cur_src_vmaddr(ctx)); + + vmaddr_t dst_vmaddr = relo_cur_src_vmaddr(ctx); + auto label = new ThumbRelocLabelEntry(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) { + DLOG(0, "%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; + vmaddr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + dst_vmaddr = ALIGN_FLOOR(dst_vmaddr, 4); + rt = bits(insn, 8, 10); + + auto label = new ThumbRelocLabelEntry(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) { + DLOG(0, "%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; + vmaddr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm32; + + auto label = new ThumbRelocLabelEntry(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) { + DLOG(0, "%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); + vmaddr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + dst_vmaddr |= 1; + + auto label = new ThumbRelocLabelEntry(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) { + DLOG(0, "%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); + vmaddr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + + rn = bits(insn, 0, 2); + + auto label = new ThumbRelocLabelEntry(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) { + DLOG(0, "%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); + vmaddr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + imm; + + auto label = new ThumbRelocLabelEntry(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)) { + DLOG(0, "%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); + vmaddr_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) { + DLOG(0, "%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); + vmaddr_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) { + DLOG(0, "%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); + vmaddr_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) { + DLOG(0, "%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); + vmaddr_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; + + vmaddr_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; + + vmaddr_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(); + + DLOG(0, "[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); + DLOG(0, "[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(); + + DLOG(0, "[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)); + DLOG(0, "[arm] Relocate thumb2 insn: 0x%x", insn); + } else { + Thumb1RelocateSingleInsn(ctx, (uint16_t) insn); + DLOG(0, "[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 = (vmaddr_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..e33413d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm/InstructionRelocationARM.h @@ -0,0 +1,300 @@ +#pragma once +#include "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: + // 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_inst : ref_label_insts_) { + // instruction offset to label + thumb2_inst_t insn = _buffer->LoadThumb2Inst(ref_label_inst.offset_); + thumb1_inst_t insn1 = _buffer->LoadThumb1Inst(ref_label_inst.offset_); + thumb1_inst_t insn2 = _buffer->LoadThumb1Inst(ref_label_inst.offset_ + sizeof(thumb1_inst_t)); + + switch (ref_label_inst.type_) { + case kThumb1Ldr: { + UNREACHABLE(); + } break; + case kThumb2LiteralLdr: { + int64_t pc = ref_label_inst.offset_ + Thumb_PC_OFFSET; + assert(pc % 4 == 0); + int32_t imm12 = relocated_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_inst.offset_, insn1); + _buffer->RewriteThumb1Inst(ref_label_inst.offset_ + Thumb1_INST_LEN, insn2); + + DLOG(0, "[thumb label link] insn offset %d link offset %d", ref_label_inst.offset_, imm12); + } break; + default: + UNREACHABLE(); + break; + } + } + } +}; + +class ThumbRelocLabelEntry : public ThumbPseudoLabel, public RelocLabel { +public: + template + ThumbRelocLabelEntry(T value, bool is_pc_register) + : RelocLabel(value), ThumbPseudoLabel(), is_pc_register_(is_pc_register) { + } + + 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->relocated_pos()) { + int offset = label->relocated_pos() - buffer_->GetBufferSize(); + t2_ldr(rt, MemOperand(pc, offset)); + } else { + // record this ldr, and fix later. + label->link_to(kThumb2LiteralLdr, 0, 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->fixup_data(iter->second); + } + } + } + +private: + std::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..49d447c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.cc @@ -0,0 +1,366 @@ +#include "platform_macro.h" + +#if defined(TARGET_ARCH_ARM64) + +#include "InstructionRelocation/arm64/InstructionRelocationARM64.h" + +#include "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; + + vmaddr_t src_vmaddr; + vmaddr_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, vmaddr_t addr) { + if (ctx->label_map.count(addr)) { + return true; + } + return false; +} + +AssemblerPseudoLabel *relo_label_create_or_get(relo_ctx_t *ctx, vmaddr_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->relocated_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 + vmaddr_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)) { + DLOG(0, "%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; + + RelocLabel *dst_label = new RelocLabel(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)) { + DLOG(0, "%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)) { + DLOG(0, "%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)) { + DLOG(0, "%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)) { + DLOG(0, "%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); + } + + RelocLabel *dst_label = new RelocLabel(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)) { + DLOG(0, "%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); + } + + RelocLabel *dst_label = new RelocLabel(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)) { + DLOG(0, "%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); + } + + RelocLabel *dst_label = new RelocLabel(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 = (vmaddr_t)origin->addr; + ctx.dst_vmaddr = (vmaddr_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..c42bfd7 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.h @@ -0,0 +1,13 @@ +#pragma once + +#include "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..306618e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/arm64/inst_decode_encode_kit.h @@ -0,0 +1,110 @@ +#pragma once + +#include "common_header.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..eb052c9 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.cc @@ -0,0 +1,78 @@ +#include "platform_macro.h" + +#if defined(TARGET_ARCH_X64) + +#include "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; + + uint8_t *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_.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) { + DLOG(0, "pre-alloc code chunk not enough"); + return RT_FAILED; + } + + // generate executable code + { + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + relocated->reset(code->addr, code->size); + delete code; + } + + return RT_SUCCESS; +} + +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..04e6afa --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x64/InstructionRelocationX64.h @@ -0,0 +1,9 @@ +#pragma once + +#include "common_header.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..df1f62b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.cc @@ -0,0 +1,79 @@ +#include "platform_macro.h" + +#if defined(TARGET_ARCH_IA32) + +#include "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_.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) { + DLOG(0, "pre-alloc code chunk not enough"); + return RT_FAILED; + } + + // generate executable code + { + auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_); + relocated->reset(code->addr, code->size); + delete code; + } + + return RT_SUCCESS; +} + +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..df04f0d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86.h @@ -0,0 +1,9 @@ +#pragma once + +#include "common_header.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..8b573fd --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.cc @@ -0,0 +1,196 @@ +#include "platform_macro.h" + +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) + +#include "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 +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); +} + +int GenRelocateSingleX86Insn(addr_t curr_orig_ip, addr_t curr_relo_ip, uint8_t *buffer_cursor, + 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 insn + x86_insn_decode(&insn, (uint8_t *)buffer_cursor, &conf); + + // x86 ip register == next instruction + curr_orig_ip = curr_orig_ip + insn.length; + + int last_relo_offset = code_buffer->GetBufferSize(); + if (insn.primary_opcode >= 0x70 && insn.primary_opcode <= 0x7F) { // jc rel8 + DLOG(0, "[x86 relo] %p: jc rel8", buffer_cursor); + + int8_t offset = insn.immediate; + + uint8_t opcode = 0x80 | (insn.primary_opcode & 0x0f); + +#if defined(TARGET_ARCH_IA32) + curr_relo_ip = curr_relo_ip + 6; + int32_t new_offset = (int32_t)(curr_orig_ip + offset - curr_relo_ip); + + __ Emit8(0x0F); + __ Emit8(opcode); + __ Emit32(new_offset); +#else + curr_relo_ip = curr_relo_ip + 2 + 2 + 6 + 8; + uint64_t orig_insn_ref_addr = curr_orig_ip + offset; + + // jcc + __ Emit8(opcode); + __ Emit8(2); + + // jmp + __ Emit8(0xEB); + __ Emit8(6 + 8); + + // jmp abs addr + codegen_x64_jmp_absolute_addr(code_buffer, orig_insn_ref_addr); +#endif + + } else if (mode == 64 && (insn.flags & X86_INSN_DECODE_FLAG_IP_RELATIVE) && + (insn.operands[1].mem.base == RIP)) { // RIP + DLOG(0, "[x86 relo] %p: rip", buffer_cursor); + + curr_relo_ip = curr_relo_ip + 6 + 8; + + addr_t rip_insn_seq_addr = 0; + { + int32_t orig_disp = insn.operands[1].mem.disp; + addr_t orig_rip_ref_addr = curr_orig_ip + orig_disp; + + 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_rip_ref_addr, jmp_near_range); + + rip_insn_seq_addr = rip_insn_seq; + + auto rip_insn_seq_buffer = CodeBufferBase(); +#define ___ rip_insn_seq_buffer. + + auto rip_insn_req_ip = rip_insn_seq; + rip_insn_req_ip = rip_insn_req_ip + insn.length; // next insn addr + int32_t new_disp = (int32_t)(orig_rip_ref_addr - 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 + codegen_x64_jmp_absolute_addr(&rip_insn_seq_buffer, curr_relo_ip); + + DobbyCodePatch((void *)rip_insn_seq, rip_insn_seq_buffer.GetBuffer(), rip_insn_seq_buffer.GetBufferSize()); + } + + // jmp *(rip) => jmp to [rip insn seq] + __ Emit8(0xFF); + __ Emit8(0x25); // ModR/M: 00 100 101 + __ Emit32(0); + __ Emit64(rip_insn_seq_addr); + } else if (insn.primary_opcode == 0xEB) { // jmp rel8 + DLOG(0, "[x86 relo] %p: jmp rel8", buffer_cursor); + + int8_t offset = insn.immediate; + +#if defined(TARGET_ARCH_IA32) + curr_relo_ip = curr_relo_ip + 5; + int32_t new_offset = (int32_t)(curr_orig_ip + offset - curr_relo_ip); + + __ Emit8(0xE9); + __ Emit32(new_offset); +#else + curr_relo_ip = curr_relo_ip + 6 + 8; + uint64_t orig_insn_ref_addr = curr_orig_ip + offset; + + // jmp *(rip) + codegen_x64_jmp_absolute_addr(code_buffer, orig_insn_ref_addr); +#endif + } else if (insn.primary_opcode == 0xE8 || insn.primary_opcode == 0xE9) { // call or jmp rel32 + DLOG(0, "[x86 relo] %p:jmp or call rel32", buffer_cursor); + + int32_t offset = insn.immediate; + + assert(insn.immediate_offset == 1); + +#if defined(TARGET_ARCH_IA32) + curr_relo_ip = curr_relo_ip + 5; + int32_t new_offset = (int32_t)(curr_orig_ip + offset - curr_relo_ip); + + __ EmitBuffer(buffer_cursor, insn.immediate_offset); + __ Emit32(new_offset); +#else + curr_relo_ip = curr_relo_ip + 6 + 8; + uint64_t orig_insn_ref_addr = curr_orig_ip + offset; + + // jmp *(rip) + __ Emit8(0xFF); + if (insn.primary_opcode == 0xE8) + __ Emit8(0x15); // ModR/M: 00 010 101 + else + __ Emit8(0x25); // ModR/M: 00 100 101 + __ Emit32(0); + __ Emit64(orig_insn_ref_addr); +#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; + DLOG(0, "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 != RT_SUCCESS) { + 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..b4bb8f5 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/InstructionRelocationX86Shared.h @@ -0,0 +1,14 @@ +#pragma once + +#include "common_header.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, + 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..e9774d1 --- /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 { \ + DLOG(0, "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..84bfad7 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.cc @@ -0,0 +1,604 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) + +#include "./X86OpcodoDecodeTable.h" + +// clang-format on + +#define _xUnknownOpHanlder -1, -1, OpSz_0, ImmSz_0, _UnknownOpHanlder +void _UnknownOpHanlder(InstrMnemonic *instr, addr_t p) { + // printf("Unknown Operand\n"); + return; +} + +#define _xInvalidOpHanlder -1, -1, OpSz_0, ImmSz_0, _InValidOpHanlder +void _InValidOpHanlder(InstrMnemonic *instr, addr_t p) { + // printf("Invalid Operand\n"); + return; +} + +inline void _ContinueDispatch(InstrMnemonic *instr, addr_t p) { + OpcodeDecodeItem *item = &OpcodeDecodeTable[*(unsigned char *)p]; + item->DecodeHandler(instr, p); +} + +// ===== Decode LegacyPrefix and REXPrefix ===== + +// clang-format off +#define _xDecodePrefix_0F 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeLegacyPrefix +#define _xDecodePrefix_66 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodePrefix_66 +#define _xDecodePrefix_67 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeLegacyPrefix +#define _xDecodeREXPrefix 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeREXPrefix +#define _xDecodePrefix 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeLegacyPrefix +#define _xDecodeSegPrefix 1, OpEn_NONE, OpSz_0, ImmSz_0, _DecodeLegacyPrefix +// clang-format on + +void _DecodeREXPrefix(InstrMnemonic *instr, addr_t p) { + instr->instr.REX = *(byte_t *)p; + instr->len++; + instr->OperandSz = OpSz_64; + + _ContinueDispatch(instr, p + 1); // continue decode +} + +void _DecodeLegacyPrefix(InstrMnemonic *instr, addr_t p) { + instr->instr.prefix = *(byte_t *)p; + instr->len++; + + _ContinueDispatch(instr, p + 1); // continue decode +} + +void _DecodePrefix_66(InstrMnemonic *instr, addr_t p) { + instr->OperandSz = OpSz_16; + _DecodeLegacyPrefix(instr, p); +} + +// ===== Decode Opcode ===== + +static void _DecodeOp(InstrMnemonic *instr, addr_t p) { + instr->instr.opcode1 = *(byte_t *)p; + instr->len++; +} + +static void _DecodeOpExtraOp(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); +} + +static void _DecodeOpWithReg(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); +} + +#define _xDecodeOpEn_ZO 1, OpEn_ZO, OpSz_0, ImmSz_0, _DecodeOpEn_ZO +void _DecodeOpEn_ZO(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); +} + +#define _xDecodeOpEn_O 1, OpEn_O, OpSz_0, ImmSz_0, _DecodeOpEn_O +void _DecodeOpEn_O(InstrMnemonic *instr, addr_t p) { + _DecodeOpWithReg(instr, p); +} + +// ===== Decode Operand ===== + +// ===== Decode ModRM Operand ===== + +#define REX_W(byte) ((byte & 0b00001000) >> 3) +#define REX_R(byte) ((byte & 0b00000100) >> 2) +#define REX_X(byte) ((byte & 0b00000010) >> 1) +#define REX_B(byte) ((byte & 0b00000001) >> 0) + +#define ModRM_Mod(byte) ((byte & 0b11000000) >> 6) +#define ModRM_RegOpcode(byte) ((byte & 0b00111000) >> 3) +#define ModRM_RM(byte) (byte & 0b00000111) + +#define SIB_Scale(sib) ((sib & 0b11000000) >> 6) +#define SIB_Index(sib) ((sib & 0b00111000) >> 3) +#define SIB_Base(sib) ((sib & 0b00000111) >> 0) + +#define REX_SIB_Base(rex, sib) ((REX_B(rex) << 3) | SIB_Base(sib)) + +void _DecodeDisplacement8(InstrMnemonic *instr, addr_t p) { + *(byte_t *)&instr->instr.Displacement = *(byte_t *)p; + instr->len += 1; +} + +void _DecodeDisplacement32(InstrMnemonic *instr, addr_t p) { + instr->instr.DisplacementOffset = instr->len; + *(dword *)&instr->instr.Displacement = *(byte_t *)p; + instr->len += 4; +} + +void _DecodeSIB(InstrMnemonic *instr, addr_t p) { + instr->instr.SIB = *(byte_t *)p; + instr->len++; +} + +void _DecodeModRM(InstrMnemonic *instr, addr_t p) { + int init_len = instr->len; + + instr->instr.ModRM = *(byte_t *)p; + instr->len++; + +#if defined(_M_X64) || defined(__x86_64__) + if (ModRM_Mod(instr->instr.ModRM) == 0b00 && ModRM_RM(instr->instr.ModRM) == 0b101) { + // RIP-Relative Addressing + instr->flag = instr->flag | kIPRelativeAddress; + _DecodeDisplacement32(instr, p + (instr->len - init_len)); + return; + } +#endif + + // Addressing Forms with the SIB Byte + if (ModRM_Mod(instr->instr.ModRM) != 0b11 && ModRM_RM(instr->instr.ModRM) == 0b100) { + _DecodeSIB(instr, p + (instr->len - init_len)); + } + + // [REG] + if (ModRM_Mod(instr->instr.ModRM) == 0b00) { + if (ModRM_RM(instr->instr.ModRM) == 0b101) { + _DecodeDisplacement32(instr, p + (instr->len - init_len)); + return; + } + } + + // [REG+disp8} + if (ModRM_Mod(instr->instr.ModRM) == 0b01) { + _DecodeDisplacement8(instr, p + (instr->len - init_len)); + return; + } + + // [REG+disp32} + if (ModRM_Mod(instr->instr.ModRM) == 0b10) { + _DecodeDisplacement32(instr, p + (instr->len - init_len)); + return; + } + + // REG + if (ModRM_Mod(instr->instr.ModRM) == 0b11) { + } +} + +void _DecodeOpEn_M(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +void _DecodeOpEn_RM(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +void _DecodeOpEn_MR(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +void _DecodeOpEn_M1(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +void _DecodeOpEn_MC(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); +} + +// ===== Decode Immediate Operand ===== + +void _DecodeImmedite(InstrMnemonic *instr, addr_t p, int sz) { + + instr->instr.ImmediateOffset = instr->len; + + OpcodeDecodeItem *item = &OpcodeDecodeTable[instr->instr.opcode1]; + if (sz == ImmSz_0) { + sz = item->ImmediteSz; + if (sz == (ImmSz_16 | ImmSz_32)) { + if (instr->instr.prefix == 0x66) { + sz = ImmSz_16; + } else { + sz = ImmSz_32; // Default Immedite Size + } + } + } + + if (sz == ImmSz_8) { + *(byte_t *)&instr->instr.Immediate = *(byte_t *)p; + instr->len += 1; + } else if (sz == ImmSz_16) { + *(word *)&instr->instr.Immediate = *(dword *)p; + instr->len += 2; + } else if (sz == ImmSz_32) { + *(dword *)&instr->instr.Immediate = *(dword *)p; + instr->len += 4; + } +} + +void _DecodeOpEn_I(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeImmedite(instr, p + 1, instr->ImmediteSz); +} + +void _DecodeOpEn_OI(InstrMnemonic *instr, addr_t p) { + _DecodeOpWithReg(instr, p); + _DecodeImmedite(instr, p + 1, instr->ImmediteSz); +} + +void _DecodeOpEn_D(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeImmedite(instr, p + 1, instr->ImmediteSz); +} + +// ===== Decode ModRM Immediate Operand ===== + +void _DecodeOpEn_RMI(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + _DecodeModRM(instr, p + 1); + _DecodeImmedite(instr, p + 2, instr->ImmediteSz); +} + +void _DecodeOpEn_MI(InstrMnemonic *instr, addr_t p) { + _DecodeOpExtraOp(instr, p); + _DecodeModRM(instr, p + 1); + _DecodeImmedite(instr, p + 2, instr->ImmediteSz); +} + +// ===== Decode Specific Opcode ===== + +#define _xDecodeOpC8 1, 0, OpSz_0, ImmSz_0, _DecodeOpC8 +void _DecodeOpC8(InstrMnemonic *instr, addr_t p) { + _DecodeOp(instr, p); + + instr->len = instr->len + 2 + 1; +} + +// http://ref.x86asm.net/coder.html#x04 +OpcodeDecodeItem OpcodeDecodeTable[257] = { + + {0x00, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x01, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x02, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x03, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x04, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x05, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0x06, _xInvalidOpHanlder}, + {0x07, _xInvalidOpHanlder}, +#else + {0x06, _xDecodeOpEn_ZO}, + {0x07, _xDecodeOpEn_ZO}, +#endif + {0x08, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x09, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x0A, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x0B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x0C, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x0D, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0x0E, _xInvalidOpHanlder}, +#else + {0x0E, _xDecodeOpEn_ZO}, +#endif + {0x0F, _xDecodePrefix_0F}, + {0x10, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x11, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x12, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x13, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x14, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x15, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0x16, _xInvalidOpHanlder}, + {0x17, _xInvalidOpHanlder}, +#else + {0x16, _xDecodeOpEn_ZO}, + {0x17, _xDecodeOpEn_ZO}, +#endif + {0x18, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x19, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x1A, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x1B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x1C, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x1D, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0x1E, _xInvalidOpHanlder}, + {0x1F, _xInvalidOpHanlder}, +#else + {0x1E, _xDecodeOpEn_ZO}, + {0x1F, _xDecodeOpEn_ZO}, +#endif + {0x20, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x21, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x22, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x23, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x24, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x25, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x26, _xDecodeSegPrefix}, +#if defined(_M_X64) || defined(__x86_64__) + {0x27, _xInvalidOpHanlder}, +#else + {0x27, _xDecodeOpEn_ZO}, +#endif + {0x28, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x29, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x2A, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x2B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x2C, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x2D, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x2E, _xDecodeSegPrefix}, +#if defined(_M_X64) || defined(__x86_64__) + {0x2F, _xInvalidOpHanlder}, +#else + {0x2F, _xDecodeOpEn_ZO}, +#endif + {0x30, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x31, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x32, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x33, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x34, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x35, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x36, _xDecodeSegPrefix}, +#if defined(_M_X64) || defined(__x86_64__) + {0x37, _xInvalidOpHanlder}, +#else + {0x37, _xDecodeOpEn_ZO}, +#endif + {0x38, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x39, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x3A, 2, OpEn_RM, OpSz_8, ImmSz_0, _DecodeOpEn_RM}, + {0x3B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x3C, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x3D, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x3E, _xDecodeSegPrefix}, +#if defined(_M_X64) || defined(__x86_64__) + {0x3F, _xInvalidOpHanlder}, +#else + {0x3F, _xDecodeOpEn_ZO}, +#endif +#if defined(_M_X64) || defined(__x86_64__) // For REX Prefix + {0x40, _xDecodeREXPrefix}, + {0x41, _xDecodeREXPrefix}, + {0x42, _xDecodeREXPrefix}, + {0x43, _xDecodeREXPrefix}, + {0x44, _xDecodeREXPrefix}, + {0x45, _xDecodeREXPrefix}, + {0x46, _xDecodeREXPrefix}, + {0x47, _xDecodeREXPrefix}, + {0x48, _xDecodeREXPrefix}, + {0x49, _xDecodeREXPrefix}, + {0x4A, _xDecodeREXPrefix}, + {0x4B, _xDecodeREXPrefix}, + {0x4C, _xDecodeREXPrefix}, + {0x4D, _xDecodeREXPrefix}, + {0x4E, _xDecodeREXPrefix}, + {0x4F, _xDecodeREXPrefix}, +#else + {0x40, _xDecodeOpEn_O}, + {0x41, _xDecodeOpEn_O}, + {0x42, _xDecodeOpEn_O}, + {0x43, _xDecodeOpEn_O}, + {0x44, _xDecodeOpEn_O}, + {0x45, _xDecodeOpEn_O}, + {0x46, _xDecodeOpEn_O}, + {0x47, _xDecodeOpEn_O}, + {0x48, _xDecodeOpEn_O}, + {0x49, _xDecodeOpEn_O}, + {0x4A, _xDecodeOpEn_O}, + {0x4B, _xDecodeOpEn_O}, + {0x4C, _xDecodeOpEn_O}, + {0x4D, _xDecodeOpEn_O}, + {0x4E, _xDecodeOpEn_O}, + {0x4F, _xDecodeOpEn_O}, +#endif + {0x50, _xDecodeOpEn_O}, + {0x51, _xDecodeOpEn_O}, + {0x52, _xDecodeOpEn_O}, + {0x53, _xDecodeOpEn_O}, + {0x54, _xDecodeOpEn_O}, + {0x55, _xDecodeOpEn_O}, + {0x56, _xDecodeOpEn_O}, + {0x57, _xDecodeOpEn_O}, + {0x58, _xDecodeOpEn_O}, + {0x59, _xDecodeOpEn_O}, + {0x5A, _xDecodeOpEn_O}, + {0x5B, _xDecodeOpEn_O}, + {0x5C, _xDecodeOpEn_O}, + {0x5D, _xDecodeOpEn_O}, + {0x5E, _xDecodeOpEn_O}, + {0x5F, _xDecodeOpEn_O}, +#if defined(_M_X64) || defined(__x86_64__) + {0x60, _xInvalidOpHanlder}, + {0x61, _xInvalidOpHanlder}, + {0x62, _xInvalidOpHanlder}, +#else + {0x60, _xDecodeOpEn_ZO}, + {0x61, _xDecodeOpEn_ZO}, + {0x62, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, +#endif + {0x63, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x64, _xDecodeSegPrefix}, + {0x65, _xDecodeSegPrefix}, + {0x66, _xDecodePrefix_66}, + {0x67, _xDecodePrefix_67}, + {0x68, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0x69, 2, OpEn_RMI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_RMI}, + {0x6A, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0x6B, 1, OpEn_RMI, OpSz_16 | OpSz_32, ImmSz_8, _DecodeOpEn_RMI}, + {0x6C, _xDecodeOpEn_ZO}, + {0x6D, _xDecodeOpEn_ZO}, + {0x6E, _xDecodeOpEn_ZO}, + {0x6F, _xDecodeOpEn_ZO}, + {0x70, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x71, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x72, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x73, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x74, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x75, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x76, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x77, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x78, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x79, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7A, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7B, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7C, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7D, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7E, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x7F, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0x80, 2, OpEn_MI, OpSz_8, ImmSz_8, _DecodeOpEn_MI}, + {0x81, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_MI}, +#if defined(_M_X64) || defined(__x86_64__) + {0x82, _xInvalidOpHanlder}, +#else + {0x82, _xUnknownOpHanlder}, +#endif + {0x83, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_8, _DecodeOpEn_MI}, + {0x84, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x85, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x86, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x87, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x88, 2, OpEn_MR, OpSz_8, ImmSz_0, _DecodeOpEn_MR}, + {0x89, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x8A, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x8B, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x8C, 2, OpEn_MR, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MR}, + {0x8D, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x8E, 2, OpEn_RM, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_RM}, + {0x8F, 2, OpEn_M, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_M}, + {0x90, _xDecodeOpEn_ZO}, + {0x91, _xInvalidOpHanlder}, + {0x92, _xInvalidOpHanlder}, + {0x93, _xInvalidOpHanlder}, + {0x94, _xInvalidOpHanlder}, + {0x95, _xInvalidOpHanlder}, + {0x96, _xInvalidOpHanlder}, + {0x97, _xInvalidOpHanlder}, + {0x98, _xDecodeOpEn_ZO}, + {0x99, _xDecodeOpEn_ZO}, +#if defined(_M_X64) || defined(__x86_64__) + {0x9A, _xInvalidOpHanlder}, +#else + {0x9A, _xDecodeOpEn_ZO}, +#endif + {0x9B, _xDecodeOpEn_ZO}, + {0x9C, _xDecodeOpEn_ZO}, + {0x9D, _xDecodeOpEn_ZO}, + {0x9E, _xDecodeOpEn_ZO}, + {0x9F, _xDecodeOpEn_ZO}, + {0xA0, _xUnknownOpHanlder}, + {0xA1, _xUnknownOpHanlder}, + {0xA2, _xUnknownOpHanlder}, + {0xA3, _xUnknownOpHanlder}, + {0xA4, _xDecodeOpEn_ZO}, + {0xA5, _xDecodeOpEn_ZO}, + {0xA6, _xDecodeOpEn_ZO}, + {0xA7, _xDecodeOpEn_ZO}, + {0xA8, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xA9, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0xAA, _xDecodeOpEn_ZO}, + {0xAB, _xDecodeOpEn_ZO}, + {0xAC, _xDecodeOpEn_ZO}, + {0xAD, _xDecodeOpEn_ZO}, + {0xAE, _xDecodeOpEn_ZO}, + {0xAF, _xDecodeOpEn_ZO}, +#undef SAME_ITEM_LAZY +#define SAME_ITEM_LAZY 1, OpEn_OI, OpSz_0, ImmSz_8, _DecodeOpEn_OI + {0xB0, SAME_ITEM_LAZY}, + {0xB1, SAME_ITEM_LAZY}, + {0xB2, SAME_ITEM_LAZY}, + {0xB3, SAME_ITEM_LAZY}, + {0xB4, SAME_ITEM_LAZY}, + {0xB5, SAME_ITEM_LAZY}, + {0xB6, SAME_ITEM_LAZY}, + {0xB7, SAME_ITEM_LAZY}, +#undef SAME_ITEM_LAZY +#define SAME_ITEM_LAZY 1, OpEn_OI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_OI + {0xB8, SAME_ITEM_LAZY}, + {0xB9, SAME_ITEM_LAZY}, + {0xBA, SAME_ITEM_LAZY}, + {0xBB, SAME_ITEM_LAZY}, + {0xBC, SAME_ITEM_LAZY}, + {0xBD, SAME_ITEM_LAZY}, + {0xBE, SAME_ITEM_LAZY}, + {0xBF, SAME_ITEM_LAZY}, + {0xC0, 2, OpEn_MI, OpSz_8, ImmSz_8, _DecodeOpEn_MI}, + {0xC1, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_MI}, + {0xC2, 1, OpEn_I, OpSz_0, ImmSz_16, _DecodeOpEn_I}, + {0xC3, _xDecodeOpEn_ZO}, + {0xC4, _xInvalidOpHanlder}, + {0xC5, _xInvalidOpHanlder}, + {0xC6, _xUnknownOpHanlder}, + {0xC7, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_MI}, + {0xC8, _xDecodeOpC8}, + {0xC9, _xDecodeOpEn_ZO}, + {0xCA, 1, OpEn_I, OpSz_0, ImmSz_16, _DecodeOpEn_I}, + {0xCB, _xDecodeOpEn_ZO}, + {0xCC, _xDecodeOpEn_ZO}, + {0xCD, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0xCE, _xInvalidOpHanlder}, +#else + {0xCE, _xDecodeOpEn_ZO}, +#endif + {0xCF, _xDecodeOpEn_ZO}, + {0xD0, 1, OpEn_M1, OpSz_8, ImmSz_0, _DecodeOpEn_M1}, + {0xD1, 1, OpEn_M1, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_M1}, + {0xD2, 1, OpEn_MC, OpSz_8, ImmSz_0, _DecodeOpEn_MC}, + {0xD3, 1, OpEn_MC, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_MC}, +#if defined(_M_X64) || defined(__x86_64__) + {0xD4, _xInvalidOpHanlder}, + {0xD5, _xInvalidOpHanlder}, +#else + {0xD4, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xD5, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, +#endif + {0xD6, _xInvalidOpHanlder}, + {0xD7, _xDecodeOpEn_ZO}, + {0xD8, _xUnknownOpHanlder}, + {0xD9, _xUnknownOpHanlder}, + {0xDA, _xUnknownOpHanlder}, + {0xDB, _xUnknownOpHanlder}, + {0xDC, _xUnknownOpHanlder}, + {0xDD, _xUnknownOpHanlder}, + {0xDE, _xUnknownOpHanlder}, + {0xDF, _xUnknownOpHanlder}, + {0xE0, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0xE1, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0xE2, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0xE3, 1, OpEn_D, OpSz_0, ImmSz_8, _DecodeOpEn_D}, + {0xE4, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xE5, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xE6, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xE7, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xE8, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, + {0xE9, 1, OpEn_I, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_I}, +#if defined(_M_X64) || defined(__x86_64__) + {0xEA, _xInvalidOpHanlder}, +#else + {0xEA, _xUnknownOpHanlder}, +#endif + {0xEB, 1, OpEn_I, OpSz_0, ImmSz_8, _DecodeOpEn_I}, + {0xEC, _xDecodeOpEn_ZO}, + {0xED, _xDecodeOpEn_ZO}, + {0xEE, _xDecodeOpEn_ZO}, + {0xEF, _xDecodeOpEn_ZO}, + {0xF0, _xDecodePrefix}, + {0xF1, _xDecodeOpEn_ZO}, + {0xF2, _xDecodeOpEn_ZO}, +#ifdef DETOURS_X86 + {0xF3, _CopyF3}, +#else + {0xF3, _xDecodeOpEn_ZO}, +#endif + {0xF4, _xDecodeOpEn_ZO}, + {0xF5, _xDecodeOpEn_ZO}, + {0xF6, 2, OpEn_MI, OpSz_8, ImmSz_8, _DecodeOpEn_MI}, + {0xF7, 2, OpEn_MI, OpSz_16 | OpSz_32, ImmSz_16 | ImmSz_32, _DecodeOpEn_MI}, + {0xF8, _xDecodeOpEn_ZO}, + {0xF9, _xDecodeOpEn_ZO}, + {0xFA, _xDecodeOpEn_ZO}, + {0xFB, _xDecodeOpEn_ZO}, + {0xFC, _xDecodeOpEn_ZO}, + {0xFD, _xDecodeOpEn_ZO}, + {0xFE, 2, OpEn_M, OpSz_8, ImmSz_0, _DecodeOpEn_M}, + {0xFF, 2, OpEn_M, OpSz_16 | OpSz_32, ImmSz_0, _DecodeOpEn_M}, + {0, 0, 0, 0, 0}}; + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.h new file mode 100644 index 0000000..778fdb3 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/deprecated/X86OpcodoDecodeTable.h @@ -0,0 +1,142 @@ +#ifndef X86_OPCODE_DECODE_TABLE_H +#define X86_OPCODE_DECODE_TABLE_H + +#ifndef __addr_t_defined +#define __addr_t_defined +typedef unsigned long long addr_t; +#endif + +#ifndef __byte_defined +#define __byte_defined +typedef unsigned char byte_t; +#endif + +#ifndef __uint_defined +#define __uint_defined +typedef unsigned int uint; +#endif + +#ifndef __word_defined +#define __word_defined +typedef short word; +#endif + +#ifndef __dword_defined +#define __dword_defined +typedef int dword; +#endif + +enum OpcodeType { OpTy_Op1, OpTy_RegInOp1, OpTy_Op1ExtraOp }; + +struct Instr { + byte_t prefix; + + byte_t REX; + + union { + byte_t opcode[3]; + struct { + byte_t opcode1; + byte_t opcode2; + byte_t opcode3; + }; + }; + + union { + byte_t ModRM; + struct { + byte_t Mod : 2; + byte_t RegOpcode : 3; + byte_t RM : 3; + }; + }; + + union { + byte_t SIB; + struct { + byte_t base : 2; + byte_t index : 3; + byte_t scale : 3; + }; + }; + + byte_t Displacement[4]; + int DisplacementOffset; + + byte_t Immediate[4]; + int ImmediateOffset; +}; + +// clang-format off +enum OperandSize { + OpSz_0 = 0, + OpSz_8=1, + OpSz_16=2, + OpSz_32=4, + OpSz_64=8 +}; + +enum ImmediteSize { + ImmSz_0 = 0, + ImmSz_8=1, + ImmSz_16=2, + ImmSz_32=4, + ImmSz_64=8 +}; + +enum InstrFlag { + kNoFlag = 0, + kIPRelativeAddress = 1 +}; +// clang-format on + +struct InstrMnemonic { + uint len; + + int flag; + + OperandSize OperandSz; + + ImmediteSize ImmediteSz; + + struct Instr instr; +}; + +struct OpcodeDecodeItem { + unsigned char opcode; + + int FixedSize; + + int OpEn; + + int OperandSz; + + int ImmediteSz; + + void (*DecodeHandler)(InstrMnemonic *, addr_t); +}; + +// clang-format off +enum OperandEncodingType { + OpEn_NONE =0, + OpEn_ZO, + OpEn_M, + OpEn_I, + OpEn_D, + OpEn_O, + OpEn_RM, + OpEn_MR, + OpEn_MI, + OpEn_OI, + OpEn_M1, + OpEn_MC, + OpEn_RMI +}; + +// clang-format on + +extern OpcodeDecodeItem OpcodeDecodeTable[257]; + +void _DecodePrefix(InstrMnemonic *instr, addr_t p); + +#endif diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/build_config.h b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/build_config.h new file mode 100644 index 0000000..7c558c2 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/build_config.h @@ -0,0 +1,144 @@ +#ifndef BUILD_CONFIG_H +#define BUILD_CONFIG_H + +#if defined(__APPLE__) +// only include TargetConditions after testing ANDROID as some android builds +// on mac don't have this header available and it's not needed unless the target +// is really mac/ios. +#include +#define OS_MACOSX 1 +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#define OS_IOS 1 +#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#elif defined(__linux__) +#define OS_LINUX 1 +// include a system header to pull in features.h for glibc/uclibc macros. +#include +#if defined(__GLIBC__) && !defined(__UCLIBC__) +// we really are using glibc, not uClibc pretending to be glibc +#define LIBC_GLIBC 1 +#endif +#elif defined(_WIN32) +#define OS_WIN 1 +#elif defined(__Fuchsia__) +#define OS_FUCHSIA 1 +#elif defined(__FreeBSD__) +#define OS_FREEBSD 1 +#elif defined(__NetBSD__) +#define OS_NETBSD 1 +#elif defined(__OpenBSD__) +#define OS_OPENBSD 1 +#elif defined(__sun) +#define OS_SOLARIS 1 +#elif defined(__QNXNTO__) +#define OS_QNX 1 +#elif defined(_AIX) +#define OS_AIX 1 +#elif defined(__asmjs__) || defined(__wasm__) +#define OS_ASMJS +#else +#error Please add support for your platform in build/build_config.h +#endif +// NOTE: Adding a new port? Please follow +// https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md + +// For access to standard BSD features, use OS_BSD instead of a +// more specific macro. +#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD) +#define OS_BSD 1 +#endif + +// For access to standard POSIXish features, use OS_POSIX instead of a +// more specific macro. +#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || defined(OS_FREEBSD) || defined(OS_LINUX) || \ + defined(OS_MACOSX) || defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \ + defined(OS_SOLARIS) +#define OS_POSIX 1 +#endif + +// Compiler detection. Note: clang masquerades as GCC on POSIX and as MSVC on +// Windows. +#if defined(__GNUC__) +#define COMPILER_GCC 1 +#elif defined(_MSC_VER) +#define COMPILER_MSVC 1 +#else +#error Please add support for your compiler in build/build_config.h +#endif + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86_64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(_M_IX86) || defined(__i386__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__s390x__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390X 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__s390__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390 1 +#define ARCH_CPU_31_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__PPC64__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARMEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__aarch64__) || defined(_M_ARM64) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARM64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__) +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__MIPSEL__) +#if defined(__LP64__) +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS64EL 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#else +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPSEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#endif +#elif defined(__MIPSEB__) +#if defined(__LP64__) +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#else +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#endif +#else +#error Please add support for your architecture in build/build_config.h +#endif + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c new file mode 100644 index 0000000..1b5de10 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c @@ -0,0 +1,564 @@ +#include "platform_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 { + FATAL("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 + DLOG(0, "[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..bb395fd --- /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 "common_header.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..059e547 --- /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) { + DLOG(0, "[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) { + DLOG(0, "[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) { + DLOG(0, "[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) { + DLOG(0, "[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) { + DLOG(0, "[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..cf7184b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptEntry.cpp @@ -0,0 +1,18 @@ +#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; + } else { + this->thumb_mode = false; + } +#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..0fff858 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptEntry.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include "common_header.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..b2e7b6d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/InterceptRouting.cpp @@ -0,0 +1,98 @@ +#include "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)); + } + DLOG(0, "%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 + DLOG(0, "[insn relocate] origin %p - %d", origin_->addr, origin_->size); + log_hex_format((uint8_t *)origin_->addr, origin_->size); + + DLOG(0, "[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) { + DLOG(0, "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() { + MemoryOperationError err; + err = DobbyCodePatch((void *)entry_->patched_addr, trampoline_buffer_->GetBuffer(), + trampoline_buffer_->GetBufferSize()); + if (err != kMemoryOperationSuccess) { + ERROR_LOG("[intercept routing] active failed"); + return; + } + DLOG(0, "[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..e85fb92 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHook.cc @@ -0,0 +1,51 @@ +#include "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 RS_FAILED; + } + +#if defined(__APPLE__) && defined(__arm64__) +#if __has_feature(ptrauth_calls) + address = ptrauth_strip(address, ptrauth_key_asia); + replace_func = ptrauth_strip(replace_func, ptrauth_key_asia); +#endif +#endif + +#if defined(ANDROID) + void *page_align_address = (void *)ALIGN_FLOOR(address, OSMemory::PageSize()); + if (!OSMemory::SetPermission(page_align_address, OSMemory::PageSize(), kReadExecute)) { + return RS_FAILED; + } +#endif + + DLOG(0, "----- [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 RS_FAILED; + } + + 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; + } + + routing->Commit(); + + Interceptor::SharedInstance()->add(entry); + + return RS_SUCCESS; +} 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..d308ace --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHookRouting.h @@ -0,0 +1,22 @@ +#pragma once + +#include "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..015c946 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionInlineHook/RoutingImpl.cc @@ -0,0 +1,22 @@ +#include "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..18a6645 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/FunctionWrapperExport.cc @@ -0,0 +1,27 @@ +#include "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) { + DLOG(0, "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(); + + DLOG(0, "Finalize %p", function_address); + return RS_SUCCESS; +} 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..354beeb --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/function-wrapper.cc @@ -0,0 +1,38 @@ +#include "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; + + DLOG(0, "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); + DLOG(0, "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..38903ef --- /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_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..85dbd12 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/FunctionWrapper/intercept_routing_handler.cc @@ -0,0 +1,79 @@ + +#include "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) { + DLOG(0, "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) { + DLOG(0, "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..a7cbc48 --- /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_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..d96ed84 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrument.cc @@ -0,0 +1,44 @@ +#include "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 RS_FAILED; + } + +#if defined(__APPLE__) && defined(__arm64__) +#if __has_feature(ptrauth_calls) + address = ptrauth_strip(address, ptrauth_key_asia); +#endif +#endif + +#if defined(ANDROID) + void *page_align_address = (void *)ALIGN_FLOOR(address, OSMemory::PageSize()); + if (!OSMemory::SetPermission(page_align_address, OSMemory::PageSize(), kReadExecute)) { + return RS_FAILED; + } +#endif + + DLOG(0, "\n\n----- [DobbyInstrument:%p] -----", address); + + auto entry = Interceptor::SharedInstance()->find((addr_t)address); + if (entry) { + ERROR_LOG("%s already been instrumented.", address); + return RS_FAILED; + } + + 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 RS_SUCCESS; +} 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..e4eed62 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrumentRouting.h @@ -0,0 +1,30 @@ +#pragma once + +#include "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..1631431 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/RoutingImpl.cc @@ -0,0 +1,42 @@ + +#include "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__) +#if __has_feature(ptrauth_calls) + handler = ptrauth_strip(handler, ptrauth_key_asia); +#endif +#endif + auto closure_trampoline = ClosureTrampoline::CreateClosureTrampoline(entry_, handler); + this->SetTrampolineTarget((addr_t)closure_trampoline->address); + DLOG(0, "[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..baaa0cf --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.cc @@ -0,0 +1,21 @@ +#include "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..553f1eb --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.h @@ -0,0 +1,7 @@ +#pragma once + +#include "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..951a8ec --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.cc @@ -0,0 +1,47 @@ +#include "InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.h" + +#include "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) { + InterceptEntry *entry = routing->GetInterceptEntry(); + 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..8b8a948 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.h @@ -0,0 +1,15 @@ +#pragma once + +#include "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..f168889 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/near_trampoline_arm64.cc @@ -0,0 +1,82 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "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); + DLOG(0, "forward trampoline use [adrp, add, br]"); + } else { + // use mov + br == (4 * 5) trampoline size + _ Mov(TMP_REG_0, dst); + _ br(TMP_REG_0); + DLOG(0, "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..f945e4e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.cc @@ -0,0 +1,11 @@ +#include "InterceptRouting/RoutingPlugin/RoutingPlugin.h" + +std::vector RoutingPluginManager::plugins; + +RoutingPluginInterface *RoutingPluginManager::near_branch_trampoline = NULL; + +void RoutingPluginManager::registerPlugin(const char *name, RoutingPluginInterface *plugin) { + DLOG(0, "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..a692c22 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/InterceptRouting/RoutingPlugin/RoutingPlugin.h @@ -0,0 +1,30 @@ +#pragma once + +#include "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 std::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..0fa0c3d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/Interceptor.h @@ -0,0 +1,25 @@ +#pragma once + +#include "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..774411a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/AssemblyCodeBuilder.cc @@ -0,0 +1,34 @@ +#include "MemoryAllocator/AssemblyCodeBuilder.h" + +#include "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..8a2dc0c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/CodeBufferBase.h @@ -0,0 +1,40 @@ +#pragma once + +#include "common_header.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..827eca0 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.cc @@ -0,0 +1,18 @@ +#include "platform_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..a66d29e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code-buffer-x86.h @@ -0,0 +1,11 @@ +#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..c75bc2d --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/CodeBuffer/code_buffer_x86.h @@ -0,0 +1,13 @@ +#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..a4b865c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/MemoryAllocator.cc @@ -0,0 +1,106 @@ +#include "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); + } + + DLOG(0, "[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, kMemoryOperationSuccess); + 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); + } + + DLOG(0, "[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..800f740 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.cc @@ -0,0 +1,234 @@ +#include "NearMemoryAllocator.h" + +#include "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; + + DLOG(0, "[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; + + DLOG(0, "[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; + + DLOG(0, "[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, kMemoryOperationSuccess); + 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; + + DLOG(0, "[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..a1e39bc --- /dev/null +++ b/app/src/main/cpp/Dobby/source/MemoryAllocator/NearMemoryAllocator.h @@ -0,0 +1,30 @@ +#pragma once + +#include "PlatformUnifiedInterface/MemoryAllocator.h" + +#include "common_header.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..f6c1073 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/ExecMemory/CodePatchTool.h @@ -0,0 +1,3 @@ +#pragma once + +MemoryOperationError 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..b4b928c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/PlatformUnifiedInterface/MemoryAllocator.h @@ -0,0 +1,101 @@ +#pragma once + +#include "common_header.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; + + std::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: + std::vector code_arenas; + std::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..14db027 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/ClosureTrampoline.h @@ -0,0 +1,39 @@ +#pragma once + +#include "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 std::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..25720ad --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/ClosureTrampolineARM.cc @@ -0,0 +1,49 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM) + +#include "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; + AssemblerPseudoLabel forward_bridge_label; + + _ 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..1e0151e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/closure_bridge_arm.cc @@ -0,0 +1,90 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM) + +#include "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; + + DLOG(0, "[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..2186324 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/dummy/closure-trampoline-template-arm.S @@ -0,0 +1,40 @@ +// .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..cf93095 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm/helper_arm.cc @@ -0,0 +1,13 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM) + +#include "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..6dcb702 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/ClosureTrampolineARM64.cc @@ -0,0 +1,63 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "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; + AssemblerPseudoLabel forward_bridge_label; + + // 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..d9159af --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/closure_bridge_arm64.cc @@ -0,0 +1,159 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "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; + + DLOG(0, "[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..16116cf --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/closure-trampoline-template-arm64.S @@ -0,0 +1,47 @@ +// .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..593b57b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/dummy/dynamic-closure-trampoline-template-arm64.S @@ -0,0 +1,31 @@ +// .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..8a9fabb --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/arm64/helper_arm64.cc @@ -0,0 +1,17 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "core/assembler/assembler-arm64.h" + +#include "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..5fdf917 --- /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) { + DLOG(0, "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..52d8567 --- /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_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..2b8cc8c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/ClosureTrampolineX64.cc @@ -0,0 +1,45 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_X64) + +#include "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..dc82f99 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/closure_bridge_x64.cc @@ -0,0 +1,141 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_X64) + +#include "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; + + DLOG(0, "[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..b24b602 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/dummy/closure-trampoline-template-x64.S @@ -0,0 +1,23 @@ +#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..383651f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x64/helper_x64.cc @@ -0,0 +1,17 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_X64) + +#include "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..be86bbe --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/ClosureTrampolineX86.cc @@ -0,0 +1,44 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "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..3e03b57 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/closure_bridge_x86.cc @@ -0,0 +1,112 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "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; + + DLOG(0, "[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..5bf2529 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/ClosureTrampolineBridge/x86/helper_x86.cc @@ -0,0 +1,16 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "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..f291302 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm/trampoline_arm.cc @@ -0,0 +1,61 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM) + +#include "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..c921683 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/arm64/trampoline_arm64.cc @@ -0,0 +1,41 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "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); + DLOG(0, "[trampoline] use [adrp, add, br]"); + } else { + // ldr, br, branch-address + CodeGen codegen(&turbo_assembler_); + codegen.LiteralLdrBranch((uint64_t)to); + DLOG(0, "[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..92c7009 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x64/trampoline_x64.cc @@ -0,0 +1,54 @@ +#include "platform_macro.h" + +#if defined(TARGET_ARCH_X64) + +#include "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; + } + + DLOG(0, "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) { + DLOG(0, "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..289b013 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/TrampolineBridge/Trampoline/x86/trampoline_x86.cc @@ -0,0 +1,33 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_IA32) + +#include "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) { + DLOG(0, "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..b9a0736 --- /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 "common_header.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..5540e6b --- /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 "common_header.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..bd29a56 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/arch/x86/cpu-x86.cc @@ -0,0 +1,98 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) + +#include "cpu-x86.h" + +X86CpuInfo::X86CpuInfo() { + icache_line_size_ = 0; + dcache_line_size_ = 0; + has_fpu_ = false; + has_cmov_ = false; + has_sahf_ = false; + has_mmx_ = false; + has_sse_ = false; + has_sse2_ = false; + has_sse3_ = false; + has_ssse3_ = false; + has_sse41_ = false; + + has_sse42_ = false; + has_osxsave_ = false; + has_avx_ = false; + has_fma3_ = false; + has_bmi1_ = false; + has_bmi2_ = false; + has_lzcnt_ = false; + has_popcnt_ = false; + is_atom_ = false; + + memcpy(vendor_, (void *)"Unknown", 8); +#if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64 + int cpu_info[4]; + __cpuid(cpu_info, 0); + unsigned num_ids = cpu_info[0]; + std::swap(cpu_info[2], cpu_info[3]); + _memcpy(vendor_, cpu_info + 1, 12); + vendor_[12] = '\0'; + + // Interpret CPU feature information. + if (num_ids > 0) { + __cpuid(cpu_info, 1); + stepping_ = cpu_info[0] & 0xF; + model_ = ((cpu_info[0] >> 4) & 0xF) + ((cpu_info[0] >> 12) & 0xF0); + family_ = (cpu_info[0] >> 8) & 0xF; + type_ = (cpu_info[0] >> 12) & 0x3; + ext_model_ = (cpu_info[0] >> 16) & 0xF; + ext_family_ = (cpu_info[0] >> 20) & 0xFF; + has_fpu_ = (cpu_info[3] & 0x00000001) != 0; + has_cmov_ = (cpu_info[3] & 0x00008000) != 0; + has_mmx_ = (cpu_info[3] & 0x00800000) != 0; + has_sse_ = (cpu_info[3] & 0x02000000) != 0; + has_sse2_ = (cpu_info[3] & 0x04000000) != 0; + has_sse3_ = (cpu_info[2] & 0x00000001) != 0; + has_ssse3_ = (cpu_info[2] & 0x00000200) != 0; + has_sse41_ = (cpu_info[2] & 0x00080000) != 0; + has_sse42_ = (cpu_info[2] & 0x00100000) != 0; + has_popcnt_ = (cpu_info[2] & 0x00800000) != 0; + has_osxsave_ = (cpu_info[2] & 0x08000000) != 0; + has_avx_ = (cpu_info[2] & 0x10000000) != 0; + has_fma3_ = (cpu_info[2] & 0x00001000) != 0; + if (family_ == 0x6) { + switch (model_) { + case 0x1C: // SLT + case 0x26: + case 0x36: + case 0x27: + case 0x35: + case 0x37: // SLM + case 0x4A: + case 0x4D: + case 0x4C: // AMT + case 0x6E: + is_atom_ = true; + } + } + } + + // There are separate feature flags for VEX-encoded GPR instructions. + if (num_ids >= 7) { + __cpuid(cpu_info, 7); + has_bmi1_ = (cpu_info[1] & 0x00000008) != 0; + has_bmi2_ = (cpu_info[1] & 0x00000100) != 0; + } + + // Query extended IDs. + __cpuid(cpu_info, 0x80000000); + unsigned num_ext_ids = cpu_info[0]; + + // Interpret extended CPU feature information. + if (num_ext_ids > 0x80000000) { + __cpuid(cpu_info, 0x80000001); + has_lzcnt_ = (cpu_info[2] & 0x00000020) != 0; + // SAHF must be probed in long mode. + has_sahf_ = (cpu_info[2] & 0x00000001) != 0; + } +#endif +} + +#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..362d1e2 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/AssemblerPseudoLabel.h @@ -0,0 +1,94 @@ +#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 type_; + vmaddr_t vmaddr_; + off_t offset_; + } ref_label_inst_t; + +public: + AssemblerPseudoLabel() : AssemblerPseudoLabel(0) { + } + + AssemblerPseudoLabel(vmaddr_t addr) : Label(addr) { + ref_label_insts_.reserve(4); + + pos_ = addr; + relocated_pos_ = 0; + } + + ~AssemblerPseudoLabel(void) { + } + + void link_confused_instructions(CodeBufferBase *buffer); + + bool has_confused_instructions() { + return ref_label_insts_.size(); + } + + void link_confused_instructions(); + + void link_to(int type, vmaddr_t inst_vmaddr, off_t offset) { + ref_label_inst_t inst; + inst.type_ = type; + inst.vmaddr_ = inst_vmaddr; + inst.offset_ = offset; + ref_label_insts_.push_back(inst); + } + +private: + addr_t relocated_pos_; + +public: + addr_t relocated_pos() { + return relocated_pos_; + }; + + void bind_to(vmaddr_t addr) { + relocated_pos_ = addr; + } + + void relocate_to(vmaddr_t addr) { + bind_to(addr); + } + +protected: + std::vector ref_label_insts_; +}; + +// --- + +struct RelocLabel : public AssemblerPseudoLabel { +public: + RelocLabel() : AssemblerPseudoLabel(0) { + } + + template RelocLabel(T value) : AssemblerPseudoLabel(0) { + *(T *)data_ = value; + data_size_ = sizeof(value); + } + + template T data() { + return *(T *)data_; + } + + template void fixup_data(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..56aaab5 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.cc @@ -0,0 +1,41 @@ +#include "platform_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_inst : ref_label_insts_) { + arm_inst_t inst = _buffer->LoadARMInst(ref_label_inst.offset_); + if (ref_label_inst.type_ == kLdrLiteral) { + int64_t pc = ref_label_inst.offset_ + ARM_PC_OFFSET; + assert(pc % 4 == 0); + int32_t imm12 = relocated_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_inst.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..af181ac --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm.h @@ -0,0 +1,357 @@ +#ifndef CORE_ASSEMBLER_ARM_H +#define CORE_ASSEMBLER_ARM_H + +#include "common_header.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->relocated_pos()) { + int offset = label->relocated_pos() - buffer_->GetBufferSize(); + ldr(rt, MemOperand(pc, offset)); + } else { + // record this ldr, and fix later. + label->link_to(kLdrLiteral, 0, 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->fixup_data(iter->second); + } + } + } +}; + +} // namespace arm +} // namespace zz + +#endif 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..1cb7836 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.cc @@ -0,0 +1,25 @@ +#include "platform_macro.h" +#if TARGET_ARCH_ARM64 + +#include "core/assembler/assembler-arm64.h" + +void AssemblerPseudoLabel::link_confused_instructions(CodeBufferBase *buffer) { + CodeBuffer *_buffer = (CodeBuffer *)buffer; + + for (auto &ref_label_inst : ref_label_insts_) { + int64_t fixup_offset = relocated_pos() - ref_label_inst.offset_; + + arm64_inst_t inst = _buffer->LoadInst(ref_label_inst.offset_); + arm64_inst_t new_inst = 0; + + if (ref_label_inst.type_ == kLabelImm19) { + new_inst = encode_imm19_offset(inst, fixup_offset); + } + + _buffer->RewriteInst(ref_label_inst.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..53b8c2a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-arm64.h @@ -0,0 +1,563 @@ +#ifndef CORE_ASSEMBLER_ARM64_H +#define CORE_ASSEMBLER_ARM64_H + +#include "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->relocated_pos()) { + int offset = label->relocated_pos() - buffer_->GetBufferSize(); + ldr(rt, offset); + } else { + label->link_to(kLabelImm19, 0, 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 + +#endif 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..dd30472 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.cc @@ -0,0 +1,34 @@ +#include "platform_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_inst : ref_label_insts_) { + int64_t new_offset = relocated_pos() - ref_label_inst.offset_; + + if (ref_label_inst.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_inst.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..5530a55 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-ia32.h @@ -0,0 +1,470 @@ +#ifndef CORE_ASSEMBLER_X86_H +#define CORE_ASSEMBLER_X86_H + +#include "common_header.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)); + { + RelocLabel *addr_label = new RelocLabel(function.address()); + addr_label->link_to(kDisp32_off_7, 0, ip_offset()); + this->AppendRelocLabel(addr_label); + } + nop(); + } + + void MovRipToRegister(Register dst) { + call(Immediate(0, 32)); + pop(dst); + } +}; + +} // namespace x86 +} // namespace zz + +#endif 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..bb7910b --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.cc @@ -0,0 +1,34 @@ +#include "platform_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_inst : ref_label_insts_) { + int64_t new_offset = relocated_pos() - ref_label_inst.offset_; + + if (ref_label_inst.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_inst.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..38b3513 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x64.h @@ -0,0 +1,596 @@ +#pragma once + +#include "common_header.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)); + { + RelocLabel *addr_label = new RelocLabel((uint64_t)function.address()); + addr_label->link_to(kDisp32_off_9, 0, ip_offset()); + this->AppendRelocLabel(addr_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..12968ac --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.cc @@ -0,0 +1,17 @@ +#include "platform_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..d84af57 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler-x86-shared.h @@ -0,0 +1,710 @@ +#ifndef CORE_ASSEMBLER_X64_H +#define CORE_ASSEMBLER_X64_H + +#include "common_header.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 + +#endif 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..69c761f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler.cc @@ -0,0 +1,65 @@ +#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) { + off_t bound_offset = reinterpret_cast(buffer_)->GetBufferSize(); + label->bind_to(bound_offset); + // If some instructions had been written, before the label bound, we need link these `confused` instructions + 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..6e53f49 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/assembler/assembler.h @@ -0,0 +1,76 @@ +#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__ +#if __has_feature(ptrauth_calls) + address_ = ptrauth_strip(address, ptrauth_key_asia); +#endif +#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: + std::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..3d9922f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm.cc @@ -0,0 +1,19 @@ +#include "platform_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..41e209e --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-arm64.cc @@ -0,0 +1,26 @@ +#include "platform_macro.h" +#if defined(TARGET_ARCH_ARM64) + +#include "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 dst_label = new RelocLabel(address); + turbo_assembler_->AppendRelocLabel(dst_label); + + _ Ldr(TMP_REG_0, dst_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..a5eba09 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-ia32.cc @@ -0,0 +1,23 @@ +#include "platform_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..83c9777 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/core/codegen/codegen-x64.cc @@ -0,0 +1,25 @@ +#include "platform_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..2026830 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby.cpp @@ -0,0 +1,32 @@ +#include "dobby_internal.h" +#include "Interceptor.h" + +__attribute__((constructor)) static void ctor() { + DLOG(-1, "================================"); + DLOG(-1, "Dobby"); + DLOG(-1, "================================"); + + DLOG(-1, "dobby in debug log mode, disable with cmake flag \"-DDOBBY_DEBUG=OFF\""); +} + +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 RT_SUCCESS; + } + + return RT_FAILED; +} diff --git a/app/src/main/cpp/Dobby/source/dobby_internal.h b/app/src/main/cpp/Dobby/source/dobby_internal.h new file mode 100644 index 0000000..0dc0c13 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/dobby_internal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "common_header.h" + +#include "dobby.h" + +#include "logging/logging.h" +#include "logging/check_logging.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" diff --git a/app/src/main/cpp/Dobby/source/include/common_header.h b/app/src/main/cpp/Dobby/source/include/common_header.h new file mode 100644 index 0000000..324d144 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/include/common_header.h @@ -0,0 +1,9 @@ +#pragma once + +#include "include/type_header.h" +#include "include/platform_header.h" +#include "include/platform_macro.h" +#include "include/utility_macro.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/include/kernel_mode_header.h b/app/src/main/cpp/Dobby/source/include/kernel_mode_header.h new file mode 100644 index 0000000..66d7a1a --- /dev/null +++ b/app/src/main/cpp/Dobby/source/include/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/include/list_c.h b/app/src/main/cpp/Dobby/source/include/list_c.h new file mode 100644 index 0000000..2d179d2 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/include/list_c.h @@ -0,0 +1,52 @@ +#pragma once + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; +#define container_of(ptr, type, member) \ + ({ \ + const __typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }) + +#define INIT_LIST_HEAD(ptr) \ + do { \ + (ptr)->next = (ptr); \ + (ptr)->prev = (ptr); \ + } while (0) + +static inline int list_empty(struct list_head *head) { + return head->next == head; +} + +static void __list_add(struct list_head *new_node, struct list_head *prev, struct list_head *next) { + next->prev = new_node; + new_node->next = next; + new_node->prev = prev; + prev->next = new_node; +} + +static inline void list_add(struct list_head *new_node, struct list_head *head) { + __list_add(new_node, head, head->next); +} + +static inline void __list_del(struct list_head *prev, struct list_head *next) { + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) { + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +#define list_entry(ptr, type, member) container_of(ptr, type, member) + +#define list_first_entry(ptr, type, member) list_entry((ptr)->next, type, member) + +#define list_next_entry(pos, member) list_entry((pos)->member.next, typeof(*(pos)), member) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); &pos->member != (head); pos = list_next_entry(pos, member)) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby/source/include/platform_header.h b/app/src/main/cpp/Dobby/source/include/platform_header.h new file mode 100644 index 0000000..c9b8d7f --- /dev/null +++ b/app/src/main/cpp/Dobby/source/include/platform_header.h @@ -0,0 +1,52 @@ +#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 +#include +#include +#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/include/platform_macro.h b/app/src/main/cpp/Dobby/source/include/platform_macro.h new file mode 100644 index 0000000..300c759 --- /dev/null +++ b/app/src/main/cpp/Dobby/source/include/platform_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/include/type_header.h b/app/src/main/cpp/Dobby/source/include/type_header.h new file mode 100644 index 0000000..9c3952c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/include/type_header.h @@ -0,0 +1,14 @@ +#pragma once + +#include "platform_header.h" + +typedef uintptr_t vmaddr_t; +typedef uintptr_t addr_t; +typedef uint32_t addr32_t; +typedef uint64_t addr64_t; +typedef unsigned char byte_t; +typedef unsigned int uint; + +#ifndef NULL +#define NULL 0 +#endif diff --git a/app/src/main/cpp/Dobby/source/include/utility_macro.h b/app/src/main/cpp/Dobby/source/include/utility_macro.h new file mode 100644 index 0000000..a6c567c --- /dev/null +++ b/app/src/main/cpp/Dobby/source/include/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/arm64-v8a/libdobby.a b/app/src/main/cpp/dobby/arm64-v8a/libdobby.a deleted file mode 100644 index e7acac65e20cea13a31ec31e935932a4996a0230..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212502 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 diff --git a/app/src/main/cpp/dobby/armeabi-v7a/libdobby.a b/app/src/main/cpp/dobby/armeabi-v7a/libdobby.a deleted file mode 100644 index 069292cd1b9e028096bcd2eaeffd8cea61107a5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166506 zcmeFa4}6rxoj*R$?k3r60t5sNh`0%eA%(EJ2_aZpldxHXB8fmPwYJ$!varFBtoegr zy)!6U{L?n5wcxcDZNIIewV|zd+SWon+b{h?3s>92{<+KbysIR=yP)Vr+u!y3e$O-W z?6Xfce^C4S-uHe^GCR-AeCG3K{(fejnR$vXjdiso-tSxPPd5RZ*L=4B`u)LRXjO%T ziTZ>PzY!u>DMV$*+qdgB3!&|Ye<_61cKoFfQ?Y&52yy#Q&fY%skP!b(tTFR0F`->} zi7?K_UiK4V)IMq22;9kRYnd>nYWGHj@tIjE`_Q$*p#8!l!gy?R$_}`M;k4hrR~WR< zt)JK)FBJyu3zhGfb)6p)Z^>4y6RxT*)4u;);mTyM*eqOAwJpCDu1vNUbTZqfYu5#X ztCPKvc&w+lE8ZOptZt9Tx>mWH+*xB9FUb#Kq($m?sCJkWS zhNe}qc2J2Ss;+KTvbQ_Y)EsNMzHa@M8$worrRQF-YD2tdeLU9Qc2m5yMirbao(zQ( ztU@k1fyWak7--7 z5QJGt3YW(_f|YCA&`e_OYg@Z&yJ8(Ji8?!&ja;C;GuGPFjUtTKZC$mxv!k_*?5;zT zt6Cr5($)<$C{X%?fve)%8j?VZ+6d;&21T+dNSjPm4avHW7VwjnE)$}4R|Z3x7BeQa zrkiz0Rv~AmArF%~%`$M-&V-{zRwDc3T|IS;iN-4v8&(kA#%rAT$gndk7+@KwrrDT~ zh2Vr)g{o<9yv}&p!cBk;sOu)jPM{T}Q&pG9#6%_If|YeW<3^T;%tCcyl_@JL>UPSM zx~zAam9MS6HQp7fjI^N(1z(Y_&TSieyP7+#3Ru^eq1wxCG8jbpcXsWpLAM(Vmdmz} zkXm#B*DFp|CsaL60RxLp-ASPa3PawtS3%sZ0wlyoi(B{ zjb%sm$k1V-de~*m$U<{`@&yFTm>Cj3H?Jd_g#gU1< zJ#q9vDAcBwM66>=yfqD&nGg)3muc!wv~BKbYS{|YC_^h58sCvbht$-zZ5yHp8>&Ie zkb!HqZDU9%xFz1vL|C$b@d|`cebT4^%o^^3<-Hx9U1%rh!neheYuei#RY&(M>=&CV zlCicfz|@(#M8<&uItUqI%VK{StVnNj<>;pC(!1P=M|;P3@XmMQp>k$fPoj5Q^CX$3 zMOw9y!0B>9b!u$04UE{0Hp$bR&)^xO*TA!n!GqtRBnWq)O%C7k4rgqLSsK%l% zaMrjC&m9MhO5QQ6b0#)LILt>Pp1EaJh6_RcL$ ziTIAD&7ED_Vm*3XuUyjs<+>%F>{;L0i<#Dzm5p8TB-ACPB#ns=1yRmWzkA}kF}Jej zjtkCeA;C9x#kM6oF*nik&8>1Y3XY*rS%b;S_Hi**uI}81?rl7R!Ie;oEumz8E*=bivSS=>C!34mtN{&zWK2${a}?Rbb-MO~t0wHO+=i zsPWfXCYWrWeIFfMx!Nr5R80$&ukOM!UVKc1Z6WW^t|U)1s%L05&e+q#pn zo|c3?(Z*{gPR0I)I z`5c1`W-#>S?a+BHqdTi+OI<~(^l7o{+>t!qqixwbh_S68=vW3XvMRnx{m z6H%;?Ywnowv&Oacbgw~FU2sKf8)ue$Rn^pktjfH=k;3edKfR2DO@i$a?-N*GLYea< z$i-=$C!MXm?eVK(dhunP6qU{tZ095~ZVs+&YGU4%*RI?iTp56QE4f%#x7Ex+GZQBy zSdJpY?5Ib+pJ=+VttZhGzo9qQ-rWSHvpv4KXHrgV#g|^R6<8;!Lm`3-GJ;H=Nb>T~ z1dMdGZArXcPEZ5PMMbdGTvxYYRTKUv<0^y#L0^Oy9;h9%XyAoPo@|fz#Mh+D8%(Rp zb#={@ePz?eiRD%$UNLDtrxQzWH}(>rLZK^~D<%=2PZP6;I=8vKb0^-4zC%o!<#U#7PRJwbkz}qn`3~eCGUP4r zR?!^mjt5t4OklFtDis3imMigke_d=FHf@;0M`oy1t9!dJAoi?jHHYcUP?hF95)00J z86A$d$G33dyU|?tscP6@(&kE#B)0JyD~y?Ec~)S-9It`p#sLJHT0&JLwA<91*;IIq z`Kl5+Q?SwmZH#wqYlCJ0ZP7%lFiZq#@o2(UM7rYK$RgDwi`Ggkzdyj+5KLKXN2A0Jj&j-4Iuo7kTlsYv*-pLH zw%E4V%$Oh)im9f}1bmlfmJ(C3)Xy#v7V)Q?%=U)-EhVyORj%#af;l$9zElp;cI-M3?yk1Xot64A< zZ{OV1f|vMh9c?|0E0xb&rmeE#xc7)J^>=P-?rd-BVbf2+%Fa5p5_|SK+qcJ?m5MvlQ63<9mrpSrE> zW)HbRvcKNsvrCBO&aK6^oyD0RgTa-RW_Z+x&5dkJ+)h>2(o)vB+=t1gxk+&ub_8S! ztE?Ck+7>GhjS1Xg0diLZ-l^CS+Cs)a*^p_l6e^Am=JqA-$Wk=bYi|2iA{FE4@Kh+* zP1OE3^~MT=DE78Z5^ZssG5w~h%8Ak=+N{(Q0$C(>R8?}?)X~%$XC{=%qLJ#1?ZsSK zOESqKOD!aEH;E#U9%v!91%t*|*i=@l_a^D@OH_jN_ZK#*V^-Hw*_w#AW0M|XEoR1| zv7#~|vLYSnQi6<0g>28va!SszX)HRnf;Jzyn5^)rRmnBzfK&*|OE2qoskce$Et3Km zyAqp@%T!HNm{r)FXqjd?TS0PtONVA$e^N-2?Xe!d-CdUK>~7nk0$HJ!$(8G6J)NEH z>2XxKmPwV%jKtt%R=y5I*p@8Q%25t=a#1s-XDMQg%G?S~HBR-#s@K(>5c?&y>Dt6I z-f4--3iFJ_%2grN)kS@`)UAyehJiy9rZGf`Ncn`=V|ubGWORw}A-30*XaV9a#Q^2|2oi1XZq#$02b ztJqjgyK|d#yW;IKO?*dN>kf&jj9SJd z+uX~TjAW+*yr`!vV6r=<-(`Su+aL$={3V-TXDD4-<`a!se&#cORThaIcy_|+G=yV* z()7UQ!dAo59EbJ7GK~BXAN@73z?ZxgF3qK|4A(qsy6;1n9p6a>$FM9|jqd_HlRs_3 z`a!umZW{cck<`(IBW-Oo+zJF}nHbjx+X%~eEk^;Km)jc=T8rIlKYOIxR7J>aTYmrK zZR%;@hq2mJHmILf8*)w3hRiyDwzb{lY6`y>14fr8!TVrW@o<2#2N7Etzn9b# z$;%lyFfh=Ub;XhYXcX?qO<`|n^{D4PtKay9Z*DYsc|HMP2vhKsCnEe=Yy7cjVrSSJ zEQ+$<^+yVx6^B!BkM@Z+gda|&8|zQe9e64$BHHpJ#Pq@`W0pU9S8dkuC2ju5vo8-+ z6VJf2l78OqzLBSP4IDpZZ23;@^y8k$HKU#n`y)$a%7blLrwa@}>Hg_EwWm)Q10ymO z_)LSRsONo1v1$*vp{$TbM5Bd+ABkiE z`pd(4u3feH$5+o=IO2A@hwpU1hbjI7b!aD3T$FgSFiWObc>zi+uLkxz2sZ|veRtb)vd0^=v+w_0RsSipO%!s=soK`D}-aVcCzAz%GHE$WRaXvhGAApuP8D zpO7s+n--rf_QGFiAx(<}yieSb7UUBjgMYpS&qJgs;}+9JG7a8r+#13yW{G%O{45hut|NY~_&{3xTu1x`qA@L=qhiXqMUi+oExt%R0Dr&&DYBmU;70D>od&}? zn;rQ39Qc?IOwZt1u2S)1cvAjl;sr&IV$N~}#Di)6a`8EpUgu}NlFu*U&H7G#nQg{h zj31a%z_2F!7MbN>g(Fi*XTf4Z0S-!AnmM@DxD$FA`p&-Pg1_6 z_<<>%X^sOvWGX>@7{~)~qxj)iPnV|j93egm*N4f*&2+)vH&kJKbi6IyQ$~PAElp3)8OBmE zfw-pvw&+p3JIFn$ucI=F)Zu0c(I1`L5SjtHXyw~Jx z8VA9yZ`vGdYd?#&lyxo z&{LNzJ!QrNvY8IiW-gUUj$n*AHEpXRa^J#KmtLGN49@B_dsz}oShwBch~jqfTv1E0m; zb?}pp#;;{sgD^Y3K^s28Yka?l;CB965$A{1M&o-KaCUqz1787vC@(cJjqf`qK8wHI zh$B5yTT99NO$WYF8-LVmG`?5yZ0GMD__eHfwAM`i1J8E;xX4Pn>5G~Cg9dri}O1gU1r|FJ5==yDRxhAIRe#y};nV*Yb zwb67R0h~QwN`Oz-E6yl2zD*`RD_?$%IP#}wotnRm4t!NM{?51IyVsE~FCmWeB5gE( zpTvWmziJzQoY89jt~2pj{LMx_kv~1_)%mBJbABm54 zP|YD(4{5FgUCt-9QNiNZfTJ9`LHF6@Sd3sT$2}&#~w!T4&85? za2DO;imqP0(7ZkkI6K_|8(m!<su5RU0eQLvO3)l-@g z;A8)yjmEbaaCUr0flv2Cl>pZGl6W9K%V@+ZK*udbIR2A%xJxZh3P&I6DOj>hBNFw& z5|1C2bjio^U=`lzq|9XRc7m(Q)eFD&}QQ zzg$Dlul(NC*VO&f39ROR9HQVo!INseU9Yx#ibY#$#XT#-iaXo49s{}F?tEYHcGh;u z(|CsKxM6z%SvAQ_!MC}WrN!rnOW+S!2vL!2#x3&1iZu8<@go#A%Xep z#k2pP<;oGi(vff(Z-0+P|2a1N^gjqoob2Ofx$?w!;A+G8`LI4z{(9vyj_Ydd8+}+8 zTS^y7SpaSn>tk$RbZNNWMsf?VK1Rd#CU>Ezi>7E$FcLM231v`iBIHMVx3+f)%CbXy=W>$2ULU7--)1CSGr?Nb|# z`nv$Kj~jkOuuV}nB0t2(aj4D2XN_x|$1q&;tm(!b_yWL3IO4-lVc~nPiO(8O??)W_ z2W>RIl@5GWz~=`L@v&#p{JBkh7JolR9O>%bOXK4f8oRvIXElEe*Z9Z}P+I)GV0*@+ zHRko>*^V!2!^b6gjqf@<1E0m8*8kPAy@UgZ>_hGN5;lD7vNXPW6CaaE^OAm0E_tW9 z7#3xelpg#LF4H)^40P~s89x!g(qaCPF6+he^0OPq-`%5uOjAaeiOO25ZjIN5|8MQl zSYBz@&feXJqsx!3YEM>BAD-;`%l|WFTop_y<9M={_`7?pQsWv6`v2K74hGT+d9pGt z59x+K`JAhQgMw|Z)dg~-z})Dmdj|3&3)U>SLM+@F#!jms_ao)qJLi$Cgz;$e8$b1l z)A_Di?7TWBI-@xEanbMITM{w$9y^sgt6}8UnCsr@#hjm&$eCA>{LR~!Z<|r&4-BY1 zOyV&9VE*{0KXF)>h67y}EOmaGyxn=++f;Mc!1-78Mb57;`0QVVcZ5YkBy#%`k*0*_ zbb)(w;-y5(8=nGa1@6`0Fzd9}ee9I00yDv$W2ZzV`8hdS;13Vw1ih=hk9qqGA|Ax?jJhD3FN%mJ=d z>9KrZ;L*P7#F~V6-oTrEBh|SR>?6{{57~n8%ip&%|0F_I2kI?cC#O!sFzPE@m*&)p zx&y;F2gX(cxz5)gY;>`INLg_>C0;N~yV;JsoQ{5=*Cv+`=M z8=THfgP$g5!{4X@5Pyk^$21ah%oRUT%980Xv>^Gwy-73D@XZp3WI7@4fy+7ae?fT4 z-v?Xl^2%pCO2fQ5sh;W3f2G6!F(!r~{UX=7Vy^NtjPSn!e+;6|U+kJDUW2O*qEg^K^FjxbfI)_ri()CgX=~??uHwkjXmqig(K(Q)o^`tgt&$- z3b}(W(y&k2vG;_V zQ}%JTq>uLw{jBEi6W$@6J|i`!@DnC@+q3w|XRPDQ^T)fgNP=g2|H|bE+qGKbpdGtz z@s{ro%aob6`O*!q_Zd}mOk|>i$|9F%^W!qE{U=;#yxm(Tm z+ZEW;&?!p{XDllCqPrEZxOG_B+oWZ_EjQnjpWSlDd(hF?yT?b-?(eY$upgo+f#oxo zO=u#K-Ym;|t^2E=RRZ4s`Vrj<64?u}8l% z|2-k)%G<=6?mtzMa*rk)MeXXO>#AnIzgpxxo;aYO6b>QmN+&UMZ+~OyS)1lq4}duM7T`+ zwSo|aJee;Vj<6jjUEx-YaDOV{w9$08I_Ms-(WU;S=`KJ82dD@s1Sd1#BWy^)Y58Bu z07n$p!4jV~BX$o0wU_N0DB0|!A>5_f16RY9<3ZD8G}lP`5yp6y3wdKXE)ND**(bM? z&3oQmqe7-Bqw|_B?PPuL|Ft!$VEMactP9QI{*PIs3a&_7Y<{;LrwaOiP8sX-eP)k6 zX&J8ws5&wE8r4fwUTkYr??I0?fb)F?ey-7EMRMvz5zgZ++!6lXDc4MIpd?xnsUG!Q zXs$mEJWCyoW*~2O-#wAL3wPl(Tr*ds9Wz2xMhwN*cREb=hU89p5O{`n_Iz5a)XbEb=7pOHQ6%imku{$l>{TH{-hoZ+k8 z_u=eey%(!?9mhrsju`!5c>Os)RJ20G+3wi~h<;orQW#3i1@Wt%mh%0A!z5DLk3vljmz0dPNqb$K3i^?S# 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�sO?6ibI2<&cgG5oae=wE@pHg`i?e#9el!|6epyje95|huZyZFa8hD#G{iB%M zdItgtvCzO=7w4G&{e|m{a`6YF+Vh?_PKcrW9PjYQ#p=Y@qveNwoj4Qyhv=Ea6NkPO zJ!kNJk&6#)8oj7`_A|!&knb1axpBpzrID{B#IiR}d?|Vc`TqUU{J~A57gv7)`JNrU z@DS?dRmNri`8)FT-4m{~8{pjXMlv&xBlbN0stf0-gOBl-=hKvB-%DmPWb>SVV*DcDvjFJB&LU5Vvk}Ad|P5gn?)n`P^k2P`G zimJi2sUtrw`UY?|O|<39`UGuemOpwI&ZwDM9%pLlGZ@sNyxr=|;4bLaM)Zza(ZAw& zzM9inUnKmnKXRkg_ksyyNy&+-@QrARqTr&DKG(pg>%h_Tvn~`^dpvsB@YfCfchvq+^#r-kFz;pln|0e9N8jxe-t80q zTlNXElD@Sc^e$KdwTxdrz1{PQ#lW%SA`Y#;0{*jr&4 zwi}iUpLf819F}2s!rldYH|#yI4ErSPr(ii(-3!aG`(UY`^7#SSŝa3569pM}eP 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 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 "zygisk.hpp" #include "dobby.h" @@ -11,19 +10,15 @@ #define PIF_JSON "/data/adb/pif.json" -#define PIF_JSON_2 "/data/adb/modules/playintegrityfix/pif.json" - static std::string FIRST_API_LEVEL, SECURITY_PATCH, BUILD_ID; typedef void (*T_Callback)(void *, const char *, const char *, uint32_t); -static std::map callbacks; +static T_Callback o_callback = nullptr; static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) { - if (cookie == nullptr || name == nullptr || value == nullptr || - !callbacks.contains(cookie)) - return; + if (cookie == nullptr || name == nullptr || value == nullptr || o_callback == nullptr) return; std::string_view prop(name); @@ -59,17 +54,17 @@ static void modify_callback(void *cookie, const char *name, const char *value, u LOGD("[%s] -> %s", name, value); } - return callbacks[cookie](cookie, name, value, serial); + return o_callback(cookie, name, value, serial); } -static void (*o_system_property_read_callback)(const prop_info *, T_Callback, void *); +static void (*o_system_property_read_callback)(const void *, T_Callback, void *); static void -my_system_property_read_callback(const prop_info *pi, T_Callback callback, void *cookie) { +my_system_property_read_callback(const void *pi, T_Callback callback, void *cookie) { if (pi == nullptr || callback == nullptr || cookie == nullptr) { return o_system_property_read_callback(pi, callback, cookie); } - callbacks[cookie] = callback; + o_callback = callback; return o_system_property_read_callback(pi, modify_callback, cookie); } @@ -82,8 +77,8 @@ static void doHook() { LOGD("Found '__system_property_read_callback' handle at %p", handle); DobbyHook( handle, - reinterpret_cast(my_system_property_read_callback), - reinterpret_cast(&o_system_property_read_callback) + (dobby_dummy_func_t) my_system_property_read_callback, + (dobby_dummy_func_t *) &o_system_property_read_callback ); } @@ -98,48 +93,93 @@ public: auto name = env->GetStringUTFChars(args->nice_name, nullptr); - if (name == nullptr) { - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - return; - } - - if (strncmp(name, "com.google.android.gms", 22) != 0) { - env->ReleaseStringUTFChars(args->nice_name, name); - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - return; - } - - api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); - - if (strcmp(name, "com.google.android.gms.unstable") != 0) { - env->ReleaseStringUTFChars(args->nice_name, name); - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - return; - } + bool isGms = memcmp(name, "com.google.android.gms", 22) == 0; + bool isGmsUnstable = memcmp(name, "com.google.android.gms.unstable", 31) == 0; env->ReleaseStringUTFChars(args->nice_name, name); - long dexSize = 0, jsonSize = 0; + if (isGms) api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); - int fd = api->connectCompanion(); + if (isGmsUnstable) { + long dexSize = 0, jsonSize = 0; - read(fd, &dexSize, sizeof(long)); - read(fd, &jsonSize, sizeof(long)); + int fd = api->connectCompanion(); - LOGD("Dex file size: %ld", dexSize); - LOGD("Json file size: %ld", jsonSize); + read(fd, &dexSize, sizeof(long)); + read(fd, &jsonSize, sizeof(long)); - vector.resize(dexSize); - read(fd, vector.data(), dexSize); + LOGD("Dex file size: %ld", dexSize); + LOGD("Json file size: %ld", jsonSize); - std::vector jsonVector(jsonSize); - read(fd, jsonVector.data(), jsonSize); + vector.resize(dexSize); + read(fd, vector.data(), dexSize); - close(fd); + std::vector jsonVector(jsonSize); + read(fd, jsonVector.data(), jsonSize); - std::string_view jsonStr(jsonVector.cbegin(), jsonVector.cend()); - json = nlohmann::json::parse(jsonStr, nullptr, false, true); + close(fd); + std::string_view jsonStr(jsonVector.cbegin(), jsonVector.cend()); + json = nlohmann::json::parse(jsonStr, nullptr, false, true); + + parseJson(); + + return; // We can't dlclose lib because of the hook + } + + api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + } + + void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { + if (vector.empty() || json.empty()) return; + + doHook(); + + injectDex(); + + vector.clear(); + json.clear(); + } + + void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { + api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + } + +private: + zygisk::Api *api = nullptr; + JNIEnv *env = nullptr; + std::vector vector; + nlohmann::json json; + + void injectDex() { + LOGD("get system classloader"); + auto clClass = env->FindClass("java/lang/ClassLoader"); + auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader", + "()Ljava/lang/ClassLoader;"); + auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader); + + LOGD("create class loader"); + auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); + auto dexClInit = env->GetMethodID(dexClClass, "", + "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); + auto buffer = env->NewDirectByteBuffer(vector.data(), vector.size()); + auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader); + + LOGD("load class"); + auto loadClass = env->GetMethodID(clClass, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + auto entryClassName = env->NewStringUTF("es.chiteroman.playintegrityfix.EntryPoint"); + auto entryClassObj = env->CallObjectMethod(dexCl, loadClass, entryClassName); + + auto entryClass = (jclass) entryClassObj; + + LOGD("call init"); + auto entryInit = env->GetStaticMethodID(entryClass, "init", "(Ljava/lang/String;)V"); + auto str = env->NewStringUTF(json.dump().c_str()); + env->CallStaticVoidMethod(entryClass, entryInit, str); + } + + void parseJson() { if (json.contains("FIRST_API_LEVEL")) { if (json["FIRST_API_LEVEL"].is_number_integer()) { @@ -189,51 +229,6 @@ public: LOGD("JSON file doesn't contain ID/BUILD_ID keys :("); } } - - void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { - if (vector.empty() || json.empty()) return; - - doHook(); - - LOGD("get system classloader"); - auto clClass = env->FindClass("java/lang/ClassLoader"); - auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader", - "()Ljava/lang/ClassLoader;"); - auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader); - - LOGD("create class loader"); - auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); - auto dexClInit = env->GetMethodID(dexClClass, "", - "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); - auto buffer = env->NewDirectByteBuffer(vector.data(), vector.size()); - auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader); - - LOGD("load class"); - auto loadClass = env->GetMethodID(clClass, "loadClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); - auto entryClassName = env->NewStringUTF("es.chiteroman.playintegrityfix.EntryPoint"); - auto entryClassObj = env->CallObjectMethod(dexCl, loadClass, entryClassName); - - auto entryClass = (jclass) entryClassObj; - - LOGD("call init"); - auto entryInit = env->GetStaticMethodID(entryClass, "init", "(Ljava/lang/String;)V"); - auto str = env->NewStringUTF(json.dump().c_str()); - env->CallStaticVoidMethod(entryClass, entryInit, str); - - vector.clear(); - json.clear(); - } - - void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - } - -private: - zygisk::Api *api = nullptr; - JNIEnv *env = nullptr; - std::vector vector; - nlohmann::json json; }; static void companion(int fd) { @@ -254,8 +249,7 @@ static void companion(int fd) { fclose(dexFile); } - FILE *jsonFile = fopen(PIF_JSON, "rb"); - if (jsonFile == nullptr) jsonFile = fopen(PIF_JSON_2, "rb"); + FILE *jsonFile = fopen(PIF_JSON, "r"); if (jsonFile) { diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java deleted file mode 100644 index 72ec9a3..0000000 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java +++ /dev/null @@ -1,105 +0,0 @@ -package es.chiteroman.playintegrityfix; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.Key; -import java.security.KeyStoreException; -import java.security.KeyStoreSpi; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.util.Date; -import java.util.Enumeration; -import java.util.Locale; - -public final class CustomKeyStoreSpi extends KeyStoreSpi { - public static volatile KeyStoreSpi keyStoreSpi; - - @Override - public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { - return keyStoreSpi.engineGetKey(alias, password); - } - - @Override - public Certificate[] engineGetCertificateChain(String alias) { - for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) { - if (stackTraceElement.getClassName().toLowerCase(Locale.US).contains("droidguard")) { - EntryPoint.LOG("DroidGuard call certificate chain! Throw exception."); - throw new UnsupportedOperationException(); - } - } - return keyStoreSpi.engineGetCertificateChain(alias); - } - - @Override - public Certificate engineGetCertificate(String alias) { - return keyStoreSpi.engineGetCertificate(alias); - } - - @Override - public Date engineGetCreationDate(String alias) { - return keyStoreSpi.engineGetCreationDate(alias); - } - - @Override - public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException { - keyStoreSpi.engineSetKeyEntry(alias, key, password, chain); - } - - @Override - public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException { - keyStoreSpi.engineSetKeyEntry(alias, key, chain); - } - - @Override - public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { - keyStoreSpi.engineSetCertificateEntry(alias, cert); - } - - @Override - public void engineDeleteEntry(String alias) throws KeyStoreException { - keyStoreSpi.engineDeleteEntry(alias); - } - - @Override - public Enumeration engineAliases() { - return keyStoreSpi.engineAliases(); - } - - @Override - public boolean engineContainsAlias(String alias) { - return keyStoreSpi.engineContainsAlias(alias); - } - - @Override - public int engineSize() { - return keyStoreSpi.engineSize(); - } - - @Override - public boolean engineIsKeyEntry(String alias) { - return keyStoreSpi.engineIsKeyEntry(alias); - } - - @Override - public boolean engineIsCertificateEntry(String alias) { - return keyStoreSpi.engineIsCertificateEntry(alias); - } - - @Override - public String engineGetCertificateAlias(Certificate cert) { - return keyStoreSpi.engineGetCertificateAlias(cert); - } - - @Override - public void engineStore(OutputStream stream, char[] password) throws CertificateException, IOException, NoSuchAlgorithmException { - keyStoreSpi.engineStore(stream, password); - } - - @Override - public void engineLoad(InputStream stream, char[] password) throws CertificateException, IOException, NoSuchAlgorithmException { - keyStoreSpi.engineLoad(stream, password); - } -} diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java index 3ec3fb4..0382e3e 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java @@ -1,6 +1,7 @@ package es.chiteroman.playintegrityfix; import java.security.Provider; +import java.security.ProviderException; public final class CustomProvider extends Provider { @@ -8,15 +9,16 @@ public final class CustomProvider extends Provider { super(provider.getName(), provider.getVersion(), provider.getInfo()); putAll(provider); - - put("KeyStore.AndroidKeyStore", CustomKeyStoreSpi.class.getName()); } @Override public synchronized Service getService(String type, String algorithm) { EntryPoint.LOG(String.format("[Service] Type: '%s' | Algorithm: '%s'", type, algorithm)); - if ("KeyStore".equals(type)) EntryPoint.spoofDevice(); + if ("AndroidKeyStore".equals(algorithm)) { + EntryPoint.spoofFields(); + throw new ProviderException(); + } return super.getService(type, algorithm); } diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java index d3acf00..404fbd6 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java @@ -7,8 +7,6 @@ import org.json.JSONException; import org.json.JSONObject; import java.lang.reflect.Field; -import java.security.KeyStore; -import java.security.KeyStoreSpi; import java.security.Provider; import java.security.Security; @@ -16,22 +14,6 @@ public final class EntryPoint { private static JSONObject jsonObject = new JSONObject(); static { - try { - spoofProvider(); - } catch (Throwable t) { - LOG("spoofProvider exception: " + t); - } - } - - private static void spoofProvider() throws Exception { - KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); - keyStore.load(null); - - Field f = keyStore.getClass().getDeclaredField("keyStoreSpi"); - f.setAccessible(true); - CustomKeyStoreSpi.keyStoreSpi = (KeyStoreSpi) f.get(keyStore); - f.setAccessible(false); - Provider provider = Security.getProvider("AndroidKeyStore"); Provider customProvider = new CustomProvider(provider); @@ -39,23 +21,20 @@ public final class EntryPoint { Security.removeProvider("AndroidKeyStore"); Security.insertProviderAt(customProvider, 1); - LOG("Spoof KeyStoreSpi and Provider done!"); + LOG("Spoof Provider done!"); } public static void init(String json) { + try { jsonObject = new JSONObject(json); - spoofDevice(); + spoofFields(); } catch (JSONException e) { LOG("Couldn't parse JSON from Zygisk"); } } - static void LOG(String msg) { - Log.d("PIF/Java", msg); - } - - static void spoofDevice() { + public static void spoofFields() { jsonObject.keys().forEachRemaining(s -> { try { Object value = jsonObject.get(s); @@ -104,4 +83,8 @@ public final class EntryPoint { return field; } + + public static void LOG(String msg) { + Log.d("PIF/Java", msg); + } } diff --git a/changelog.md b/changelog.md index 072f65b..1f79d85 100644 --- a/changelog.md +++ b/changelog.md @@ -1,12 +1,7 @@ -We have a Telegram channel! +We have a Telegram group! If you want to share your knowledge join: https://t.me/playintegrityfix -# v15.1 +# v15.2 -Emergency update 🚨 - -- Fix few bugs. -- Using v14.6 fingerprint, so you can pass DEVICE. - -MODULE WON'T PROVIDE MORE FINGERPRINTS WHEN GOOGLE BAN THIS ONE. \ No newline at end of file +Fingerprint is not included. \ No newline at end of file diff --git a/module/customize.sh b/module/customize.sh index 8be9094..babb43a 100644 --- a/module/customize.sh +++ b/module/customize.sh @@ -50,9 +50,4 @@ if [ -d "/system/app/EliteDevelopmentModule" ]; then touch "$directory/.replace" ui_print "- EliteDevelopmentModule app removed." -fi - -if [ -f "/data/adb/pif.json" ]; then - mv -f "/data/adb/pif.json" "/data/adb/pif.json.old" - ui_print "- Backup pif.json" fi \ No newline at end of file diff --git a/module/pif.json b/module/pif.json deleted file mode 100644 index fba7312..0000000 --- a/module/pif.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "PRODUCT": "c01_ww", - "DEVICE": "acer_c01", - "MANUFACTURER": "Acer Inc.", - "BRAND": "acer", - "MODEL": "C01", - "FINGERPRINT": "acer/c01_ww/acer_c01:7.1.1/NMF26F/1521514970:user/release-keys", - "SECURITY_PATCH": "2018-04-01", - "FIRST_API_LEVEL": 24, - "ID": "NMF26F" -} \ No newline at end of file diff --git a/update.json b/update.json index 938f4af..d545798 100644 --- a/update.json +++ b/update.json @@ -1,6 +1,6 @@ { - "version": "v15.1", - "versionCode": 15100, - "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v15.1/PlayIntegrityFix.zip", + "version": "v15.2", + "versionCode": 15200, + "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v15.2/PlayIntegrityFix_v15.2.zip", "changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/changelog.md" } \ No newline at end of file