Prepare v15.7

This commit is contained in:
chiteroman 2024-02-07 18:03:56 +01:00
parent 92eee68b67
commit bdef42962f
350 changed files with 50901 additions and 19873 deletions

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/src/main/cpp/libcxx" vcs="Git" />
</component>
</project>

View File

@ -12,8 +12,8 @@ android {
applicationId = "es.chiteroman.playintegrityfix"
minSdk = 26
targetSdk = 34
versionCode = 15600
versionName = "v15.6"
versionCode = 15700
versionName = "v15.7"
multiDexEnabled = false
buildFeatures {
@ -24,16 +24,20 @@ android {
jniLibs {
excludes += "**/liblog.so"
excludes += "**/libdobby.so"
excludes += "**/libshadowhook.so"
}
}
externalNativeBuild {
ndk {
abiFilters += "arm64-v8a"
abiFilters += "armeabi-v7a"
cmake {
arguments += "-DANDROID_STL=none"
arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel"
arguments += "-DPlugin.Android.BionicLinkerUtil=ON"
jobs = Runtime.getRuntime().availableProcessors()
cppFlags += "-std=c++20"
cppFlags += "-fno-exceptions"
cppFlags += "-fno-rtti"
cppFlags += "-fvisibility=hidden"
cppFlags += "-fvisibility-inlines-hidden"
}
}
}
@ -53,12 +57,17 @@ android {
}
externalNativeBuild {
ndkBuild {
path = file("src/main/cpp/Android.mk")
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
}
dependencies {
implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0")
}
tasks.register("updateModuleProp") {
doLast {
val versionName = project.android.defaultConfig.versionName

View File

@ -1,30 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := playintegrityfix
LOCAL_SRC_FILES := $(LOCAL_PATH)/main.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/cJSON/cJSON.c)
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/*.c)
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/common/*.c)
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/third_party/xdl/*.c)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/cJSON
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/common
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/bsd
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/lss
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/xdl
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm/*.c)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm
else ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm64/*.c)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm64
endif
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

View File

@ -1,8 +0,0 @@
APP_STL := system
APP_ABI := armeabi-v7a arm64-v8a
APP_CFLAGS := -Oz -flto -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -fno-threadsafe-statics -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-stack-protector
APP_CPPFLAGS := -std=c++2b -fno-exceptions -fno-rtti
APP_CONLYFLAGS := -std=c2x
APP_LDFLAGS := -Oz -flto -Wl,--exclude-libs,ALL -Wl,--gc-sections
APP_PLATFORM := android-26
APP_THIN_ARCHIVE := true

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.22.1)
project(playintegrityfix)
find_package(cxx REQUIRED CONFIG)
link_libraries(cxx::cxx)
add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp)
add_subdirectory(Dobby)
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC log dobby_static)

View File

@ -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

80
app/src/main/cpp/Dobby/.gitignore vendored Normal file
View File

@ -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

View File

@ -0,0 +1,384 @@
cmake_minimum_required(VERSION 3.5)
project(Dobby)
enable_language(ASM)
include(cmake/Util.cmake)
include(cmake/Macros.cmake)
include(cmake/build_environment_check.cmake)
include(cmake/auto_source_group.cmake)
include(cmake/xcode_generator_helper.cmake)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 11)
auto_source_group("." "auto-source-group" "\\.(cc|cpp|c|h)$")
# --- options
option(DOBBY_DEBUG "Enable debug logging" OFF)
option(NearBranch "Enable near branch trampoline" ON)
option(FullFloatingPointRegisterPack "Save and pack all floating-point registers" OFF)
option(Plugin.SymbolResolver "Enable symbol resolver" ON)
option(Plugin.ImportTableReplace "Enable import table replace " OFF)
option(Plugin.Android.BionicLinkerUtil "Enable android bionic linker util" OFF)
option(DOBBY_BUILD_EXAMPLE "Build example" OFF)
option(DOBBY_BUILD_TEST "Build test" OFF)
# --- private
option(DOBBY_BUILD_KERNEL_MODE "Build xnu kernel mode" OFF)
option(Private.Obfuscation "Enable llvm obfuscation" OFF)
if ((NOT DEFINED CMAKE_BUILD_TYPE) OR (CMAKE_BUILD_TYPE STREQUAL "Debug"))
set(DOBBY_DEBUG ON)
endif ()
set(compile_definitions "")
# for arm64, allow access q8 - q31
if (FullFloatingPointRegisterPack)
set(compile_definitions "${compile_definitions} -DFULL_FLOATING_POINT_REGISTER_PACK")
endif ()
if (DOBBY_BUILD_KERNEL_MODE)
set(compile_definitions "${compile_definitions} -DBUILDING_KERNEL")
endif ()
if (DOBBY_DEBUG)
set(compile_definitions "${compile_definitions} -DDOBBY_DEBUG")
else ()
set(compile_definitions "${compile_definitions} -DDOBBY_LOGGING_DISABLE")
endif ()
if (CMAKE_GENERATOR STREQUAL Xcode)
endif ()
include(cmake/compiler_and_linker.cmake)
message(STATUS "[Dobby] CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
message(STATUS "[Dobby] DOBBY_DEBUG: ${DOBBY_DEBUG}")
message(STATUS "[Dobby] NearBranch: ${NearBranch}")
message(STATUS "[Dobby] FullFloatingPointRegisterPack: ${FullFloatingPointRegisterPack}")
message(STATUS "[Dobby] Plugin.SymbolResolver: ${Plugin.SymbolResolver}")
message(STATUS "[Dobby] Plugin.ImportTableReplace: ${Plugin.ImportTableReplace}")
message(STATUS "[Dobby] Plugin.Android.BionicLinkerUtil: ${Plugin.Android.BionicLinkerUtil}")
message(STATUS "[Dobby] DOBBY_BUILD_EXAMPLE: ${DOBBY_BUILD_EXAMPLE}")
message(STATUS "[Dobby] DOBBY_BUILD_TEST: ${DOBBY_BUILD_TEST}")
message(STATUS "[Dobby] DOBBY_BUILD_KERNEL_MODE: ${DOBBY_BUILD_KERNEL_MODE}")
message(STATUS "[Dobby] Private.Obfuscation: ${Private.Obfuscation}")
# ---
include_directories(
.
./include
./source
source/dobby
./external
./external/logging
./builtin-plugin
)
if (SYSTEM.Darwin AND (NOT DOBBY_BUILD_KERNEL_MODE))
include_directories(
source/Backend/UserMode
)
endif ()
# ---
set(DOBBY_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST}
# cpu
source/core/arch/CpuFeature.cc
source/core/arch/CpuRegister.cc
# assembler
source/core/assembler/assembler.cc
source/core/assembler/assembler-arm.cc
source/core/assembler/assembler-arm64.cc
source/core/assembler/assembler-ia32.cc
source/core/assembler/assembler-x64.cc
# codegen
source/core/codegen/codegen-arm.cc
source/core/codegen/codegen-arm64.cc
source/core/codegen/codegen-ia32.cc
source/core/codegen/codegen-x64.cc
# memory kit
source/MemoryAllocator/CodeBuffer/CodeBufferBase.cc
source/MemoryAllocator/AssemblyCodeBuilder.cc
source/MemoryAllocator/MemoryAllocator.cc
# instruction relocation
source/InstructionRelocation/arm/InstructionRelocationARM.cc
source/InstructionRelocation/arm64/InstructionRelocationARM64.cc
source/InstructionRelocation/x86/InstructionRelocationX86.cc
source/InstructionRelocation/x86/InstructionRelocationX86Shared.cc
source/InstructionRelocation/x64/InstructionRelocationX64.cc
source/InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.c
# intercept routing
source/InterceptRouting/InterceptRouting.cpp
# intercept routing trampoline
source/TrampolineBridge/Trampoline/arm/trampoline_arm.cc
source/TrampolineBridge/Trampoline/arm64/trampoline_arm64.cc
source/TrampolineBridge/Trampoline/x86/trampoline_x86.cc
source/TrampolineBridge/Trampoline/x64/trampoline_x64.cc
# closure trampoline bridge - arm
source/TrampolineBridge/ClosureTrampolineBridge/common_bridge_handler.cc
source/TrampolineBridge/ClosureTrampolineBridge/arm/helper_arm.cc
source/TrampolineBridge/ClosureTrampolineBridge/arm/closure_bridge_arm.cc
source/TrampolineBridge/ClosureTrampolineBridge/arm/ClosureTrampolineARM.cc
# closure trampoline bridge - arm64
source/TrampolineBridge/ClosureTrampolineBridge/arm64/helper_arm64.cc
source/TrampolineBridge/ClosureTrampolineBridge/arm64/closure_bridge_arm64.cc
source/TrampolineBridge/ClosureTrampolineBridge/arm64/ClosureTrampolineARM64.cc
# closure trampoline bridge - x86
source/TrampolineBridge/ClosureTrampolineBridge/x86/helper_x86.cc
source/TrampolineBridge/ClosureTrampolineBridge/x86/closure_bridge_x86.cc
source/TrampolineBridge/ClosureTrampolineBridge/x86/ClosureTrampolineX86.cc
# closure trampoline bridge - x64
source/TrampolineBridge/ClosureTrampolineBridge/x64/helper_x64.cc
source/TrampolineBridge/ClosureTrampolineBridge/x64/closure_bridge_x64.cc
source/TrampolineBridge/ClosureTrampolineBridge/x64/ClosureTrampolineX64.cc
source/InterceptRouting/Routing/InstructionInstrument/InstructionInstrument.cc
source/InterceptRouting/Routing/InstructionInstrument/RoutingImpl.cc
source/InterceptRouting/Routing/InstructionInstrument/instrument_routing_handler.cc
source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHook.cc
source/InterceptRouting/Routing/FunctionInlineHook/RoutingImpl.cc
# plugin register
source/InterceptRouting/RoutingPlugin/RoutingPlugin.cc
# main
source/dobby.cpp
source/Interceptor.cpp
source/InterceptEntry.cpp
)
if (SYSTEM.Darwin AND NOT DOBBY_BUILD_KERNEL_MODE)
set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST}
source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc
source/Backend/UserMode/UnifiedInterface/platform-posix.cc
source/Backend/UserMode/ExecMemory/code-patch-tool-darwin.cc
source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c
)
elseif (SYSTEM.Linux OR SYSTEM.Android)
set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST}
source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc
source/Backend/UserMode/UnifiedInterface/platform-posix.cc
source/Backend/UserMode/ExecMemory/code-patch-tool-posix.cc
source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c
)
elseif (SYSTEM.Windows)
set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST}
source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc
source/Backend/UserMode/UnifiedInterface/platform-windows.cc
source/Backend/UserMode/ExecMemory/code-patch-tool-windows.cc
source/Backend/UserMode/ExecMemory/clear-cache-tool-all.c
)
endif ()
if (PROCESSOR.X86_64 OR PROCESSOR.X86)
set(NearBranch ON)
endif ()
# ---
if (0 AND SYSTEM.iOS AND (NOT DOBBY_BUILD_KERNEL_MODE))
include_directories(
source/Backend/UserMode/ExecMemory/substrated
)
set(compile_definitions "${compile_definitions} -DCODE_PATCH_WITH_SUBSTRATED")
set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST}
source/Backend/UserMode/ExecMemory/substrated/mach_interface_support
)
endif ()
# ----- instrument -----
if (FunctionWrapper)
set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST}
# user mode - multi thread support
# source/UserMode/MultiThreadSupport/ThreadSupport.cpp
# source/UserMode/Thread/PlatformThread.cc
# source/UserMode/Thread/platform-thread-${platform1}.cc
)
message(FATAL_ERROR "[!] FunctionWrapper plugin is not supported")
endif ()
# ---
if (NearBranch)
set(dobby.SOURCE_FILE_LIST ${dobby.SOURCE_FILE_LIST}
source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/near_trampoline_arm64.cc
source/InterceptRouting/RoutingPlugin/NearBranchTrampoline/NearBranchTrampoline.cc
source/MemoryAllocator/NearMemoryAllocator.cc)
endif ()
# ---
# add logging library
add_subdirectory(external/logging)
get_target_property(logging.SOURCE_FILE_LIST logging SOURCES)
# add osbase library
add_subdirectory(external/osbase)
# ---
if (Plugin.SymbolResolver)
include_directories(builtin-plugin/SymbolResolver)
add_subdirectory(builtin-plugin/SymbolResolver)
get_target_property(symbol_resolver.SOURCE_FILE_LIST dobby_symbol_resolver SOURCES)
set(dobby.plugin.SOURCE_FILE_LIST ${dobby.plugin.SOURCE_FILE_LIST}
${symbol_resolver.SOURCE_FILE_LIST}
)
endif ()
# ---
set(dobby.HEADER_FILE_LIST
include/dobby.h
)
# ---
# add build version
string(TIMESTAMP TODAY "%Y%m%d")
set(VERSION_REVISION "-${TODAY}")
if (EXISTS "${CMAKE_SOURCE_DIR}/.git")
execute_process(
COMMAND git rev-parse --short --verify HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE VERSION_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (VERSION_COMMIT_HASH)
set(VERSION_REVISION "${VERSION_REVISION}-${VERSION_COMMIT_HASH}")
endif ()
endif ()
set(DOBBY_BUILD_VERSION "Dobby${VERSION_REVISION}")
set(compile_definitions "${compile_definitions} -D__DOBBY_BUILD_VERSION__=\"${DOBBY_BUILD_VERSION}\"")
message(STATUS "[Dobby] ${DOBBY_BUILD_VERSION}")
# ---
set(SOURCE_FILE_LIST
${dobby.HEADER_FILE_LIST}
${dobby.SOURCE_FILE_LIST}
${logging.SOURCE_FILE_LIST}
${dobby.plugin.SOURCE_FILE_LIST}
)
get_absolute_path_list(SOURCE_FILE_LIST SOURCE_FILE_LIST_)
set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST_})
add_library(dobby SHARED
${SOURCE_FILE_LIST}
)
target_include_directories(dobby PUBLIC
include
)
# ---
add_library(dobby_static STATIC
${SOURCE_FILE_LIST}
)
target_include_directories(dobby_static PUBLIC
include
)
set_target_properties(dobby_static
PROPERTIES OUTPUT_NAME "dobby"
)
# ---
set_target_properties(dobby
PROPERTIES
LINK_FLAGS "${linker_flags}"
COMPILE_FLAGS "${compiler_flags}"
)
set_target_properties(dobby_static
PROPERTIES
COMPILE_FLAGS "${compiler_flags}"
)
target_compile_definitions(dobby PRIVATE
"COMPILE_DEFINITIONS ${compile_definitions}"
)
target_compile_definitions(dobby_static PRIVATE
"COMPILE_DEFINITIONS ${compile_definitions}"
)
# ---
if (Private.Obfuscation)
set(linker_flags "${linker_flags} -Wl,-mllvm -Wl,-obfuscator-conf=all")
endif ()
# ---
if (SYSTEM.Android)
target_link_libraries(dobby log)
if (PROCESSOR.ARM)
set_target_properties(dobby
PROPERTIES
ANDROID_ARM_MODE arm
)
set_target_properties(dobby_static
PROPERTIES
ANDROID_ARM_MODE arm
)
endif ()
endif ()
if (SYSTEM.Linux)
target_link_libraries(dobby dl)
endif ()
# ---
if (DOBBY_BUILD_EXAMPLE AND (NOT DOBBY_BUILD_KERNEL_MODE))
add_subdirectory(examples)
endif ()
if (DOBBY_BUILD_TEST AND (NOT DOBBY_BUILD_KERNEL_MODE))
add_subdirectory(tests)
endif ()
# ---
if (SYSTEM.Darwin AND (NOT DOBBY_BUILD_KERNEL_MODE))
include(cmake/platform/platform-darwin.cmake)
endif ()

View File

@ -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.

View File

@ -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)

View File

@ -0,0 +1,3 @@
## Dobby
**待更新**

View File

@ -0,0 +1,46 @@
#include "./dobby_monitor.h"
#include <dlfcn.h>
#include <CoreFoundation/CoreFoundation.h>
#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

View File

@ -0,0 +1,94 @@
#include <stdlib.h> /* getenv */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <set>
#include <unordered_map>
#include <dlfcn.h>
#include <sys/param.h>
#include "dobby.h"
#include "dobby/common.h"
#define LOG_TAG "DynamicLoaderMonitor"
std::unordered_map<void *, const char *> traced_dlopen_handle_list;
static void *(*orig_dlopen)(const char *__file, int __mode);
static void *fake_dlopen(const char *__file, int __mode) {
void *result = orig_dlopen(__file, __mode);
if (result != NULL && __file) {
char *traced_filename = (char *)malloc(MAXPATHLEN);
// FIXME: strncpy
strcpy(traced_filename, __file);
INFO_LOG("[-] dlopen handle: %s", __file);
traced_dlopen_handle_list.insert(std::make_pair(result, (const char *)traced_filename));
}
return result;
}
static void *(*orig_loader_dlopen)(const char *filename, int flags, const void *caller_addr);
static void *fake_loader_dlopen(const char *filename, int flags, const void *caller_addr) {
void *result = orig_loader_dlopen(filename, flags, caller_addr);
if (result != NULL) {
char *traced_filename = (char *)malloc(MAXPATHLEN);
// FIXME: strncpy
strcpy(traced_filename, filename);
INFO_LOG("[-] dlopen handle: %s", filename);
traced_dlopen_handle_list.insert(std::make_pair(result, (const char *)traced_filename));
}
return result;
}
static const char *get_traced_filename(void *handle, bool removed) {
std::unordered_map<void *, const char *>::iterator it;
it = traced_dlopen_handle_list.find(handle);
if (it != traced_dlopen_handle_list.end()) {
if (removed)
traced_dlopen_handle_list.erase(it);
return it->second;
}
return NULL;
}
static void *(*orig_dlsym)(void *__handle, const char *__symbol);
static void *fake_dlsym(void *__handle, const char *__symbol) {
const char *traced_filename = get_traced_filename(__handle, false);
if (traced_filename) {
INFO_LOG("[-] dlsym: %s, symbol: %s", traced_filename, __symbol);
}
return orig_dlsym(__handle, __symbol);
}
static int (*orig_dlclose)(void *__handle);
static int fake_dlclose(void *__handle) {
const char *traced_filename = get_traced_filename(__handle, true);
if (traced_filename) {
INFO_LOG("[-] dlclose: %s", traced_filename);
free((void *)traced_filename);
}
return orig_dlclose(__handle);
}
#if 0
__attribute__((constructor)) static void ctor() {
#if defined(__ANDROID__)
#if 0
void *dl = dlopen("libdl.so", RTLD_LAZY);
void *__loader_dlopen = dlsym(dl, "__loader_dlopen");
#endif
DobbyHook((void *)DobbySymbolResolver(NULL, "__loader_dlopen"), (void *)fake_loader_dlopen,
(void **)&orig_loader_dlopen);
#else
DobbyHook((void *)DobbySymbolResolver(NULL, "dlopen"), (void *)fake_dlopen, (void **)&orig_dlopen);
#endif
DobbyHook((void *)dlsym, (void *)fake_dlsym, (void **)&orig_dlsym);
DobbyHook((void *)dlclose, (void *)fake_dlclose, (void **)&orig_dlclose);
}
#endif

View File

@ -0,0 +1,97 @@
#include <stdlib.h> /* getenv */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <set>
#include <unordered_map>
#include <sys/param.h>
#include "./dobby_monitor.h"
std::unordered_map<FILE *, const char *> *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<FILE *, const char *>::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<FILE *, const char *>();
#if defined(__APPLE__)
#include <TargetConditionals.h>
#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;
}

View File

@ -0,0 +1,58 @@
#include "./dobby_monitor.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
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;
}

View File

@ -0,0 +1,120 @@
#include <stdlib.h> /* getenv */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <dlfcn.h>
#include <iostream>
#include <fstream>
#include <set>
#include <unordered_map>
#include <sys/param.h>
#include "dobby.h"
#include "dobby/common.h"
#define LOG_TAG "PosixFileOperationMonitor"
std::unordered_map<int, const char *> *posix_file_descriptors;
int (*orig_open)(const char *pathname, int flags, ...);
int fake_open(const char *pathname, int flags, ...) {
mode_t mode = 0;
if (flags & O_CREAT) {
va_list args;
va_start(args, flags);
mode = (mode_t)va_arg(args, int);
va_end(args);
}
int result = orig_open(pathname, flags, mode);
if (result != -1) {
char *traced_filename = (char *)malloc(MAXPATHLEN);
// FIXME: strncpy
strcpy(traced_filename, pathname);
INFO_LOG("[-] trace open handle: %s", pathname);
if (posix_file_descriptors == NULL) {
posix_file_descriptors = new std::unordered_map<int, const char *>();
}
posix_file_descriptors->insert(std::make_pair(result, (const char *)traced_filename));
}
return result;
}
int (*orig___open)(const char *pathname, int flags, int mode);
int fake___open(const char *pathname, int flags, int mode) {
char *traced_filename = NULL;
if (pathname) {
traced_filename = (char *)malloc(MAXPATHLEN);
// FIXME: strncpy
strcpy(traced_filename, pathname);
INFO_LOG("[-] trace open handle: ", pathname);
}
int result = orig___open(pathname, flags, mode);
if (result != -1) {
if (posix_file_descriptors == NULL) {
posix_file_descriptors = new std::unordered_map<int, const char *>();
}
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<int, const char *>::iterator it;
it = posix_file_descriptors->find(fd);
if (it != posix_file_descriptors->end()) {
if (removed)
posix_file_descriptors->erase(it);
return it->second;
}
return NULL;
}
ssize_t (*orig_read)(int fd, void *buf, size_t count);
ssize_t fake_read(int fd, void *buf, size_t count) {
const char *traced_filename = get_traced_filename(fd, false);
if (traced_filename) {
INFO_LOG("[-] read: %s, buffer: %p, size: %zu", traced_filename, buf, count);
}
return orig_read(fd, buf, count);
}
ssize_t (*orig_write)(int fd, const void *buf, size_t count);
ssize_t fake_write(int fd, const void *buf, size_t count) {
const char *traced_filename = get_traced_filename(fd, false);
if (traced_filename) {
INFO_LOG("[-] write: %s, buffer: %p, size: %zu", traced_filename, buf, count);
}
return orig_write(fd, buf, count);
}
int (*orig_close)(int fd);
int fake_close(int fd) {
const char *traced_filename = get_traced_filename(fd, true);
if (traced_filename) {
INFO_LOG("[-] close: %s", traced_filename);
free((void *)traced_filename);
}
return orig_close(fd);
}
#if 0
__attribute__((constructor)) static void ctor() {
DobbyHook((void *)DobbySymbolResolver(NULL, "open"), (void *)fake_open, (void **)&orig_open);
DobbyHook((void *)DobbySymbolResolver(NULL, "write"), (void *)fake_write, (void **)&orig_write);
DobbyHook((void *)DobbySymbolResolver(NULL, "read"), (void *)fake_read, (void **)&orig_read);
DobbyHook((void *)DobbySymbolResolver(NULL, "close"), (void *)fake_close, (void **)&orig_close);
}
#endif

View File

@ -0,0 +1,57 @@
#include <stdlib.h> /* getenv */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <set>
#include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>
std::unordered_map<int, const char *> 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<int, const char *>::iterator it;
it = posix_socket_file_descriptors.find(fd);
if (it != posix_socket_file_descriptors.end()) {
if (removed)
posix_socket_file_descriptors.erase(it);
return it->second;
}
return NULL;
}
int (*orig_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int fake_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
const char *traced_socket = get_traced_socket(sockfd, false);
if (traced_socket) {
INFO_LOG("[-] connect: %s\n", traced_socket);
}
return orig_connect(sockfd, addr, addrlen);
}
ssize_t (*orig_send)(int sockfd, const void *buf, size_t len, int flags);
ssize_t fake_send(int sockfd, const void *buf, size_t len, int flags) {
const char *traced_socket = get_traced_socket(sockfd, false);
if (traced_socket) {
INFO_LOG("[-] send: %s, buf: %p, len: %zu\n", traced_socket, buf, len);
}
return orig_send(sockfd, buf, len, flags);
}
ssize_t (*orig_recv)(int sockfd, void *buf, size_t len, int flags);
ssize_t fake_recv(int sockfd, void *buf, size_t len, int flags) {
const char *traced_socket = get_traced_socket(sockfd, false);
if (traced_socket) {
INFO_LOG("[-] recv: %s, buf: %p, len: %zu\n", traced_socket, buf, len);
}
return orig_recv(sockfd, buf, len, flags);
}

View File

@ -0,0 +1,36 @@
#include "dobby.h"
#include "bionic_linker_util.h"
#include "logging/logging.h"
#include <dlfcn.h>
#define LOG_TAG "BionicLinkerUtil"
__attribute__((constructor)) static void ctor() {
const char *lib = NULL;
#if defined(__LP64__)
lib = "/system/lib64/libandroid_runtime.so";
#else
lib = "/system/lib/libandroid_runtime.so";
#endif
void *vm = NULL;
vm = DobbySymbolResolver(lib, "_ZN7android14AndroidRuntime7mJavaVME");
INFO_LOG("DobbySymbolResolver::vm %p", vm);
#if 0
linker_disable_namespace_restriction();
void *handle = NULL;
handle = dlopen(lib, RTLD_LAZY);
vm = dlsym(handle, "_ZN7android14AndroidRuntime7mJavaVME");
#else
void *handle = NULL;
handle = linker_dlopen(lib, RTLD_LAZY);
vm = dlsym(handle, "_ZN7android14AndroidRuntime7mJavaVME");
#endif
INFO_LOG("vm %p", vm);
}

View File

@ -0,0 +1,197 @@
#include "bionic_linker_util.h"
#include <elf.h>
#include <jni.h>
#include <string>
#include <dlfcn.h>
#include <link.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <unordered_map>
#include <vector>
#include <set>
#include "dobby.h"
#include "dobby_symbol_resolver.h"
#include "dobby/common.h"
#undef LOG_TAG
#define LOG_TAG "BionicLinkerUtil"
#undef Q
#define Q 29
// impl at "dobby_symbol_resolver.cc"
extern void *resolve_elf_internal_symbol(const char *library_name, const char *symbol_name);
#include <sys/system_properties.h>
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<soinfo_t> linker_solist;
std::vector<soinfo_t> linker_get_solist() {
if (!linker_solist.empty()) {
linker_solist.clear();
}
static soinfo_t (*solist_get_head)() = NULL;
if (!solist_get_head)
solist_get_head =
(soinfo_t(*)())resolve_elf_internal_symbol(get_android_linker_path(), "__dl__Z15solist_get_headv");
static soinfo_t (*solist_get_somain)() = NULL;
if (!solist_get_somain)
solist_get_somain =
(soinfo_t(*)())resolve_elf_internal_symbol(get_android_linker_path(), "__dl__Z17solist_get_somainv");
static addr_t *solist_head = NULL;
if (!solist_head)
solist_head = (addr_t *)solist_get_head();
static addr_t somain = 0;
if (!somain)
somain = (addr_t)solist_get_somain();
// Generate the name for an offset.
#define PARAM_OFFSET(type_, member_) __##type_##__##member_##__offset_
#define STRUCT_OFFSET PARAM_OFFSET
int STRUCT_OFFSET(solist, next) = 0;
for (size_t i = 0; i < 1024 / sizeof(void *); i++) {
if (*(addr_t *)((addr_t)solist_head + i * sizeof(void *)) == somain) {
STRUCT_OFFSET(solist, next) = i * sizeof(void *);
break;
}
}
linker_solist.push_back(solist_head);
addr_t sonext = 0;
sonext = *(addr_t *)((addr_t)solist_head + STRUCT_OFFSET(solist, next));
while (sonext) {
linker_solist.push_back((void *)sonext);
sonext = *(addr_t *)((addr_t)sonext + STRUCT_OFFSET(solist, next));
}
return linker_solist;
}
char *linker_soinfo_get_realpath(soinfo_t soinfo) {
static char *(*_get_realpath)(soinfo_t) = NULL;
if (!_get_realpath)
_get_realpath =
(char *(*)(soinfo_t))resolve_elf_internal_symbol(get_android_linker_path(), "__dl__ZNK6soinfo12get_realpathEv");
return _get_realpath(soinfo);
}
uintptr_t linker_soinfo_to_handle(soinfo_t soinfo) {
static uintptr_t (*_linker_soinfo_to_handle)(soinfo_t) = NULL;
if (!_linker_soinfo_to_handle)
_linker_soinfo_to_handle =
(uintptr_t(*)(soinfo_t))resolve_elf_internal_symbol(get_android_linker_path(), "__dl__ZN6soinfo9to_handleEv");
return _linker_soinfo_to_handle(soinfo);
}
typedef void *android_namespace_t;
android_namespace_t linker_soinfo_get_primary_namespace(soinfo_t soinfo) {
static android_namespace_t (*_get_primary_namespace)(soinfo_t) = NULL;
if (!_get_primary_namespace)
_get_primary_namespace = (android_namespace_t(*)(soinfo_t))resolve_elf_internal_symbol(
get_android_linker_path(), "__dl__ZN6soinfo21get_primary_namespaceEv");
return _get_primary_namespace(soinfo);
}
void linker_iterate_soinfo(int (*cb)(soinfo_t soinfo)) {
auto solist = linker_get_solist();
for (auto it = solist.begin(); it != solist.end(); it++) {
int ret = cb(*it);
if (ret != 0)
break;
}
}
static int iterate_soinfo_cb(soinfo_t soinfo) {
android_namespace_t ns = NULL;
ns = linker_soinfo_get_primary_namespace(soinfo);
INFO_LOG("lib: %s", linker_soinfo_get_realpath(soinfo));
// set is_isolated_ as false
// no need for this actually
int STRUCT_OFFSET(android_namespace_t, is_isolated_) = 0x8;
*(uint8_t *)((addr_t)ns + STRUCT_OFFSET(android_namespace_t, is_isolated_)) = false;
std::vector<std::string> 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<std::string> orig_ld_library_paths =
*(std::vector<std::string> *)((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<std::string> paths(orig_ld_library_paths.begin(), orig_ld_library_paths.end());
orig_ld_library_paths.assign(paths.begin(), paths.end());
}
} else {
*(std::vector<std::string> *)((addr_t)ns + STRUCT_OFFSET(android_namespace_t, ld_library_paths_)) =
std::move(ld_library_paths);
}
return 0;
}
bool (*orig_linker_namespace_is_is_accessible)(android_namespace_t ns, const std::string &file);
bool linker_namespace_is_is_accessible(android_namespace_t ns, const std::string &file) {
INFO_LOG("check %s", file.c_str());
return true;
return orig_linker_namespace_is_is_accessible(ns, file);
}
void linker_disable_namespace_restriction() {
linker_iterate_soinfo(iterate_soinfo_cb);
// no need for this actually
void *linker_namespace_is_is_accessible_ptr = resolve_elf_internal_symbol(
get_android_linker_path(), "__dl__ZN19android_namespace_t13is_accessibleERKNSt3__112basic_"
"stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE");
DobbyHook(linker_namespace_is_is_accessible_ptr, (void *)linker_namespace_is_is_accessible,
(void **)&orig_linker_namespace_is_is_accessible);
INFO_LOG("disable namespace restriction done");
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
#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

View File

@ -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 ()

View File

@ -0,0 +1,3 @@
add_library(dobby_import_replace INTERFACE
dobby_import_replace.cc
)

View File

@ -0,0 +1,192 @@
#include "dobby_import_replace.h"
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/dyld_images.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <mach/vm_map.h>
#include <mach/mach.h>
#include <sys/mman.h>
#include <vector>
#include "dobby/common.h"
#include "logging/logging.h"
#include "PlatformUtil/ProcessRuntimeUtility.h"
#if defined(__LP64__)
typedef struct mach_header_64 mach_header_t;
typedef struct segment_command_64 segment_command_t;
typedef struct section_64 section_t;
typedef struct nlist_64 nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
#else
typedef struct mach_header mach_header_t;
typedef struct segment_command segment_command_t;
typedef struct section section_t;
typedef struct nlist nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
#endif
static void *iterate_indirect_symtab(char *symbol_name, section_t *section, intptr_t slide, nlist_t *symtab,
char *strtab, uint32_t *indirect_symtab) {
const bool is_data_const = strcmp(section->segname, "__DATA_CONST") == 0;
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
vm_prot_t old_protection = VM_PROT_READ;
if (is_data_const) {
mprotect(indirect_symbol_bindings, section->size, PROT_READ | PROT_WRITE);
}
for (uint i = 0; i < section->size / sizeof(void *); i++) {
uint32_t symtab_index = indirect_symbol_indices[i];
if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
continue;
}
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
char *local_symbol_name = strtab + strtab_offset;
bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1];
if (strcmp(local_symbol_name, symbol_name) == 0) {
return &indirect_symbol_bindings[i];
}
if (local_symbol_name[0] == '_') {
if (strcmp(symbol_name, &local_symbol_name[1]) == 0) {
return &indirect_symbol_bindings[i];
}
}
}
if (is_data_const && 0) {
int protection = 0;
if (old_protection & VM_PROT_READ) {
protection |= PROT_READ;
}
if (old_protection & VM_PROT_WRITE) {
protection |= PROT_WRITE;
}
if (old_protection & VM_PROT_EXECUTE) {
protection |= PROT_EXEC;
}
mprotect(indirect_symbol_bindings, section->size, protection);
}
return NULL;
}
static void *get_global_offset_table_stub(mach_header_t *header, char *symbol_name) {
segment_command_t *curr_seg_cmd;
segment_command_t *text_segment, *data_segment, *linkedit_segment;
struct symtab_command *symtab_cmd = NULL;
struct dysymtab_command *dysymtab_cmd = NULL;
uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
for (uint i = 0; i < header->ncmds; i++, cur += curr_seg_cmd->cmdsize) {
curr_seg_cmd = (segment_command_t *)cur;
if (curr_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
if (strcmp(curr_seg_cmd->segname, "__LINKEDIT") == 0) {
linkedit_segment = curr_seg_cmd;
} else if (strcmp(curr_seg_cmd->segname, "__DATA") == 0) {
data_segment = curr_seg_cmd;
} else if (strcmp(curr_seg_cmd->segname, "__TEXT") == 0) {
text_segment = curr_seg_cmd;
}
} else if (curr_seg_cmd->cmd == LC_SYMTAB) {
symtab_cmd = (struct symtab_command *)curr_seg_cmd;
} else if (curr_seg_cmd->cmd == LC_DYSYMTAB) {
dysymtab_cmd = (struct dysymtab_command *)curr_seg_cmd;
}
}
if (!symtab_cmd || !linkedit_segment || !linkedit_segment) {
return NULL;
}
uintptr_t slide = (uintptr_t)header - (uintptr_t)text_segment->vmaddr;
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
uint32_t symtab_count = symtab_cmd->nsyms;
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
cur = (uintptr_t)header + sizeof(mach_header_t);
for (uint i = 0; i < header->ncmds; i++, cur += curr_seg_cmd->cmdsize) {
curr_seg_cmd = (segment_command_t *)cur;
if (curr_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
if (strcmp(curr_seg_cmd->segname, "__DATA") != 0 && strcmp(curr_seg_cmd->segname, "__DATA_CONST") != 0) {
continue;
}
for (uint j = 0; j < curr_seg_cmd->nsects; j++) {
section_t *sect = (section_t *)(cur + sizeof(segment_command_t)) + j;
if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
void *stub = iterate_indirect_symtab(symbol_name, sect, slide, symtab, strtab, indirect_symtab);
if (stub)
return stub;
}
if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
void *stub = iterate_indirect_symtab(symbol_name, sect, slide, symtab, strtab, indirect_symtab);
if (stub)
return stub;
}
}
}
}
return NULL;
}
PUBLIC int DobbyImportTableReplace(char *image_name, char *symbol_name, void *fake_func, void **orig_func_ptr) {
std::vector<RuntimeModule> ProcessModuleMap = ProcessRuntimeUtility::GetProcessModuleMap();
for (auto module : ProcessModuleMap) {
if (image_name != NULL && strstr(module.path, image_name) == NULL)
continue;
addr_t header = (addr_t)module.load_address;
size_t slide = 0;
#if 0
if (header) {
if (((struct mach_header *)header)->magic == MH_MAGIC_64)
slide = macho_kit_get_slide64(header);
}
#endif
#if 0
INFO_LOG("resolve image: %s", module.path);
#endif
uint32_t nlist_count = 0;
nlist_t *nlist_array = 0;
char *string_pool = 0;
void *stub = get_global_offset_table_stub((mach_header_t *)header, symbol_name);
if (stub) {
void *orig_func;
orig_func = *(void **)stub;
#if __has_feature(ptrauth_calls)
orig_func = ptrauth_strip(orig_func, ptrauth_key_asia);
orig_func = ptrauth_sign_unauthenticated(orig_func, ptrauth_key_asia, 0);
#endif
*orig_func_ptr = orig_func;
#if __has_feature(ptrauth_calls)
fake_func = (void *)ptrauth_strip(fake_func, ptrauth_key_asia);
fake_func = ptrauth_sign_unauthenticated(fake_func, ptrauth_key_asia, stub);
#endif
*(void **)stub = fake_func;
}
if (image_name)
return 0;
}
return -1;
}

View File

@ -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

View File

@ -0,0 +1,7 @@
add_library(objc_runtime_replace
dobby_objc_runtime_replace.mm
)
target_link_libraries(objc_runtime_replace
"-framework Foundation"
)

View File

@ -0,0 +1,18 @@
#pragma once
#include <stdio.h>
#include <objc/runtime.h>
#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

View File

@ -0,0 +1,54 @@
#include "dobby_objc_runtime_repalce.h"
#include <stdio.h>
#include <objc/runtime.h>
/* clang -rewrite-objc main.m */
IMP DobbyObjcReplace(Class class_, SEL sel_, IMP fake_impl) {
Method method_ = class_getInstanceMethod(class_, sel_);
if (!method_)
method_ = class_getClassMethod(class_, sel_);
if (!method_) {
// DEBUG_LOG("Not found class: %s, selector: %s method\n", class_getName(class_), sel_getName(sel_));
return NULL;
}
return method_setImplementation(method_, (IMP)fake_impl);
}
void DobbyObjcReplaceEx(const char *class_name, const char *selector_name, void *fake_impl, void **out_orig_impl) {
Class class_ = objc_getClass(class_name);
SEL sel_ = sel_registerName(selector_name);
Method method_ = class_getInstanceMethod(class_, sel_);
if (!method_) {
method_ = class_getClassMethod(class_, sel_);
if (!method_) {
// ERROR_LOG("Not found class: %s, selector: %s method\n", class_name, selector_name);
return;
}
}
auto orig_impl = (void *)method_setImplementation(method_, (IMP)fake_impl);
if (out_orig_impl) {
*out_orig_impl = orig_impl;
}
return;
}
void *DobbyObjcResolveMethodImp(const char *class_name, const char *selector_name) {
Class class_ = objc_getClass(class_name);
SEL sel_ = sel_registerName(selector_name);
Method method_ = class_getInstanceMethod(class_, sel_);
if (!method_)
method_ = class_getClassMethod(class_, sel_);
if (!method_) {
// DEBUG_LOG("Not found class: %s, selector: %s method\n", class_name, selector_name);
return NULL;
}
return (void *)method_getImplementation(method_);
}

View File

@ -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(
.
)

View File

@ -0,0 +1 @@
Monitor all supervisor call

View File

@ -0,0 +1,193 @@
#include "dobby/dobby_internal.h"
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <mach/mach.h>
#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);
}

View File

@ -0,0 +1,44 @@
#include "misc_utility.h"
#include <string.h>
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;
}

View File

@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
typedef uintptr_t addr_t;
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.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
// 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);

View File

@ -0,0 +1,95 @@
#include "dobby/dobby_internal.h"
#include <sys/param.h>
#include <mach/mach.h>
#include <sys/syscall.h>
#include "SupervisorCallMonitor/supervisor_call_monitor.h"
#include "misc-helper/async_logger.h"
#define PT_DENY_ATTACH 31
static void sensitive_api_handler(DobbyRegisterContext *ctx, const InterceptEntry *info) {
char buffer[256] = {0};
int syscall_rum = ctx->general.regs.x16;
if (syscall_rum == 0) {
syscall_rum = (int)ctx->general.x[0];
if (syscall_rum == SYS_ptrace) {
int request = ctx->general.x[1];
if (request == PT_DENY_ATTACH) {
ctx->general.x[1] = 0;
// INFO_LOG("syscall svc ptrace deny");
}
}
if (syscall_rum == SYS_exit) {
// INFO_LOG("syscall svc exit");
}
} else if (syscall_rum > 0) {
if (syscall_rum == SYS_ptrace) {
int request = ctx->general.x[0];
if (request == PT_DENY_ATTACH) {
ctx->general.x[0] = 0;
// INFO_LOG("svc ptrace deny");
}
}
if (syscall_rum == SYS_exit) {
// INFO_LOG("svc exit");
}
}
async_logger_print(buffer);
}
static int get_func_svc_offset(addr_t func_addr) {
typedef int32_t arm64_instr_t;
for (int i = 0; i < 8; i++) {
arm64_instr_t *insn = (arm64_instr_t *)func_addr + i;
if (*insn == 0xd4001001) {
return i * sizeof(arm64_instr_t);
}
}
return 0;
}
#include <sys/sysctl.h>
__typeof(sysctl) *orig_sysctl;
int fake_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
struct kinfo_proc *info = NULL;
int ret = orig_sysctl(name, namelen, oldp, oldlenp, newp, newlen);
if (name[0] == CTL_KERN && name[1] == KERN_PROC && name[2] == KERN_PROC_PID) {
info = (struct kinfo_proc *)oldp;
info->kp_proc.p_flag &= ~(P_TRACED);
}
return ret;
}
void supervisor_call_monitor_register_sensitive_api_handler() {
char *sensitive_func_array[] = {"ptrace", "exit"};
size_t count = sizeof(sensitive_func_array) / sizeof(char *);
for (size_t i = 0; i < count; i++) {
addr_t func_addr = 0;
char func_name[64] = {0};
sprintf(func_name, "__%s", sensitive_func_array[i]);
func_addr = (addr_t)DobbySymbolResolver("libsystem_kernel.dylib", func_name);
if (func_addr == 0) {
func_addr = (addr_t)DobbySymbolResolver("libsystem_kernel.dylib", sensitive_func_array[i]);
}
if (func_addr == 0) {
INFO_LOG("not found func %s", sensitive_func_array[i]);
continue;
}
int func_svc_offset = get_func_svc_offset(func_addr);
if (func_svc_offset == 0) {
INFO_LOG("not found svc %s", sensitive_func_array[i]);
continue;
}
addr_t func_svc_addr = func_addr + func_svc_offset;
supervisor_call_monitor_register_svc(func_svc_addr);
}
// ===============
DobbyHook((void *)sysctl, (void *)fake_sysctl, (void **)&orig_sysctl);
supervisor_call_monitor_register_handler(sensitive_api_handler);
}

View File

@ -0,0 +1,138 @@
#include "SupervisorCallMonitor/misc_utility.h"
#include "dobby/dobby_internal.h"
#include "PlatformUtil/ProcessRuntimeUtility.h"
#include "misc-helper/async_logger.h"
#include <vector>
std::vector<DBICallTy> *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<DBICallTy>();
}
g_supervisor_call_handlers->push_back(handler);
}
std::vector<addr_t> *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<addr_t>();
}
if (g_svc_addr_array) {
auto iter = g_svc_addr_array->begin();
for (; iter != g_svc_addr_array->end(); iter++) {
if (*iter == svc_addr)
return;
}
}
g_svc_addr_array->push_back(svc_addr);
DobbyInstrument((void *)svc_addr, common_supervisor_call_monitor_handler);
DLOG(2, "register supervisor_call_monitor at %p", svc_addr);
}
void supervisor_call_monitor_register_image(void *header) {
auto text_section = macho_kit_get_section_by_name((mach_header_t *)header, "__TEXT", "__text");
addr_t insn_addr = (addr_t)header + (addr_t)text_section->offset;
addr_t insn_addr_end = insn_addr + text_section->size;
for (; insn_addr < insn_addr_end; insn_addr += sizeof(uint32_t)) {
if (*(uint32_t *)insn_addr == 0xd4001001) {
supervisor_call_monitor_register_svc((addr_t)insn_addr);
}
}
}
void supervisor_call_monitor_register_main_app() {
const char *main_bundle_udid = fast_get_main_app_bundle_udid();
auto module_map = ProcessRuntimeUtility::GetProcessModuleMap();
for (auto module : module_map) {
if (strstr(module.path, main_bundle_udid)) {
INFO_LOG("[supervisor_call_monitor] %s", module.path);
supervisor_call_monitor_register_image((void *)module.load_address);
}
}
}
extern "C" int __shared_region_check_np(uint64_t *startaddress);
struct dyld_cache_header *shared_cache_get_load_addr() {
static struct dyld_cache_header *shared_cache_load_addr = 0;
if (shared_cache_load_addr)
return shared_cache_load_addr;
#if 0
if (syscall(294, &shared_cache_load_addr) == 0) {
#else
// FIXME:
if (__shared_region_check_np((uint64_t *)&shared_cache_load_addr) != 0) {
#endif
shared_cache_load_addr = 0;
}
return shared_cache_load_addr;
}
void supervisor_call_monitor_register_system_kernel() {
auto libsystem = ProcessRuntimeUtility::GetProcessModule("libsystem_kernel.dylib");
addr_t libsystem_header = (addr_t)libsystem.load_address;
auto text_section = macho_kit_get_section_by_name((mach_header_t *)libsystem_header, "__TEXT", "__text");
addr_t shared_cache_load_addr = (addr_t)shared_cache_get_load_addr();
addr_t insn_addr = shared_cache_load_addr + (addr_t)text_section->offset;
addr_t insn_addr_end = insn_addr + text_section->size;
addr_t write_svc_addr = (addr_t)DobbySymbolResolver("libsystem_kernel.dylib", "write");
write_svc_addr += 4;
addr_t __psynch_mutexwait_svc_addr = (addr_t)DobbySymbolResolver("libsystem_kernel.dylib", "__psynch_mutexwait");
__psynch_mutexwait_svc_addr += 4;
for (; insn_addr < insn_addr_end; insn_addr += sizeof(uint32_t)) {
if (*(uint32_t *)insn_addr == 0xd4001001) {
if (insn_addr == write_svc_addr)
continue;
if (insn_addr == __psynch_mutexwait_svc_addr)
continue;
supervisor_call_monitor_register_svc((addr_t)insn_addr);
}
}
}
void supervisor_call_monitor_init() {
// create logger file
char logger_path[1024] = {0};
sprintf(logger_path, "%s%s", getenv("HOME"), "/Documents/svc_monitor.txt");
INFO_LOG("HOME: %s", logger_path);
async_logger_init(logger_path);
dobby_enable_near_branch_trampoline();
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
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();

View File

@ -0,0 +1,98 @@
#include "dobby/dobby_internal.h"
#include <mach/mach.h>
#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);
}

View File

@ -0,0 +1,16 @@
#include "dobby/dobby_internal.h"
#include "SupervisorCallMonitor/supervisor_call_monitor.h"
#if 1
__attribute__((constructor)) static void ctor() {
log_set_level(2);
log_switch_to_syslog();
supervisor_call_monitor_init();
supervisor_call_monitor_register_main_app();
supervisor_call_monitor_register_syscall_call_log_handler();
supervisor_call_monitor_register_mach_syscall_call_log_handler();
}
#endif

View File

@ -0,0 +1,55 @@
set(SOURCE_FILE_LIST)
include_directories(
.
)
if (NOT DEFINED DOBBY_DIR)
message(FATAL_ERROR "DOBBY_DIR must be set!")
endif ()
if (SYSTEM.Darwin)
add_library(macho_ctx_kit
${CMAKE_CURRENT_SOURCE_DIR}/macho/macho_ctx.h
${CMAKE_CURRENT_SOURCE_DIR}/macho/macho_ctx.cc
)
add_library(shared_cache_ctx_kit
${CMAKE_CURRENT_SOURCE_DIR}/macho/shared_cache_ctx.h
${CMAKE_CURRENT_SOURCE_DIR}/macho/shared_cache_ctx.cpp
)
set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST}
macho/macho_ctx.cc
macho/dobby_symbol_resolver.cc
)
if (NOT DOBBY_BUILD_KERNEL_MODE)
set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST}
macho/macho_file_symbol_resolver.cpp
macho/shared_cache_ctx.cpp
${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Darwin/ProcessRuntimeUtility.cc
)
endif ()
elseif (SYSTEM.Linux OR SYSTEM.Android)
set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST}
elf/dobby_symbol_resolver.cc
${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Linux/ProcessRuntimeUtility.cc
)
elseif (SYSTEM.Windows)
set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST}
pe/dobby_symbol_resolver.cc
${DOBBY_DIR}/source/Backend/UserMode/PlatformUtil/Windows/ProcessRuntimeUtility.cc
)
endif ()
get_absolute_path_list(SOURCE_FILE_LIST SOURCE_FILE_LIST_)
set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST_})
add_library(dobby_symbol_resolver
${SOURCE_FILE_LIST}
)

View File

@ -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

View File

@ -0,0 +1,238 @@
#include "SymbolResolver/dobby_symbol_resolver.h"
#include "dobby/common.h"
#include <elf.h>
#include <dlfcn.h>
#include <link.h>
#include <string.h>
#include "mmap_file_util.h"
#include "PlatformUtil/ProcessRuntimeUtility.h"
#include <vector>
#undef LOG_TAG
#define LOG_TAG "DobbySymbolResolver"
typedef struct elf_ctx {
void *header;
uintptr_t load_bias;
ElfW(Shdr) * sym_sh_;
ElfW(Shdr) * dynsym_sh_;
const char *strtab_;
ElfW(Sym) * symtab_;
const char *dynstrtab_;
ElfW(Sym) * dynsymtab_;
size_t nbucket_;
size_t nchain_;
uint32_t *bucket_;
uint32_t *chain_;
size_t gnu_nbucket_;
uint32_t *gnu_bucket_;
uint32_t *gnu_chain_;
uint32_t gnu_maskwords_;
uint32_t gnu_shift2_;
ElfW(Addr) * gnu_bloom_filter_;
} elf_ctx_t;
static void get_syms(ElfW(Ehdr) * header, ElfW(Sym) * *symtab_ptr, char **strtab_ptr, int *count_ptr) {
ElfW(Shdr) *section_header = NULL;
section_header = (ElfW(Shdr) *)((addr_t)header + header->e_shoff);
ElfW(Shdr) *section_strtab_section_header = NULL;
section_strtab_section_header = (ElfW(Shdr) *)((addr_t)section_header + header->e_shstrndx * header->e_shentsize);
char *section_strtab = NULL;
section_strtab = (char *)((addr_t)header + section_strtab_section_header->sh_offset);
for (int i = 0; i < header->e_shnum; ++i) {
const char *section_name = (const char *)(section_strtab + section_header->sh_name);
if (section_header->sh_type == SHT_SYMTAB && strcmp(section_name, ".symtab") == 0) {
*symtab_ptr = (ElfW(Sym) *)((addr_t)header + section_header->sh_offset);
*count_ptr = section_header->sh_size / sizeof(ElfW(Sym));
}
if (section_header->sh_type == SHT_STRTAB && strcmp(section_name, ".strtab") == 0) {
*strtab_ptr = (char *)((addr_t)header + section_header->sh_offset);
}
section_header = (ElfW(Shdr) *)((addr_t)section_header + header->e_shentsize);
}
}
int elf_ctx_init(elf_ctx_t *ctx, void *header_) {
ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)header_;
ctx->header = ehdr;
ElfW(Addr) ehdr_addr = (ElfW(Addr))ehdr;
// Handle dynamic segment
{
ElfW(Addr) addr = 0;
ElfW(Dyn) *dyn = NULL;
ElfW(Phdr) *phdr = reinterpret_cast<ElfW(Phdr) *>(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<ElfW(Dyn) *>(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<const char *>(addr + d->d_un.d_ptr);
} else if (d->d_tag == DT_SYMTAB) {
symtab = reinterpret_cast<ElfW(Sym) *>(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<ElfW(Shdr) *>(ehdr_addr + ehdr->e_shoff);
ElfW(Shdr) *shstr_sh = NULL;
shstr_sh = &shdr[ehdr->e_shstrndx];
char *shstrtab = NULL;
shstrtab = (char *)((addr_t)ehdr_addr + shstr_sh->sh_offset);
for (size_t i = 0; i < ehdr->e_shnum; i++) {
if (shdr[i].sh_type == SHT_SYMTAB) {
sym_sh = &shdr[i];
ctx->sym_sh_ = sym_sh;
ctx->symtab_ = (ElfW(Sym) *)(ehdr_addr + shdr[i].sh_offset);
} else if (shdr[i].sh_type == SHT_STRTAB && strcmp(shstrtab + shdr[i].sh_name, ".strtab") == 0) {
str_sh = &shdr[i];
ctx->strtab_ = (const char *)(ehdr_addr + shdr[i].sh_offset);
} else if (shdr[i].sh_type == SHT_DYNSYM) {
dynsym_sh = &shdr[i];
ctx->dynsym_sh_ = dynsym_sh;
ctx->dynsymtab_ = (ElfW(Sym) *)(ehdr_addr + shdr[i].sh_offset);
} else if (shdr[i].sh_type == SHT_STRTAB && strcmp(shstrtab + shdr[i].sh_name, ".dynstr") == 0) {
dynstr_sh = &shdr[i];
ctx->dynstrtab_ = (const char *)(ehdr_addr + shdr[i].sh_offset);
}
}
}
return 0;
}
static void *iterate_symbol_table_impl(const char *symbol_name, ElfW(Sym) * symtab, const char *strtab, int count) {
for (int i = 0; i < count; ++i) {
ElfW(Sym) *sym = symtab + i;
const char *symbol_name_ = strtab + sym->st_name;
if (strcmp(symbol_name_, symbol_name) == 0) {
return (void *)sym->st_value;
}
}
return NULL;
}
void *elf_ctx_iterate_symbol_table(elf_ctx_t *ctx, const char *symbol_name) {
void *result = NULL;
if (ctx->symtab_ && ctx->strtab_) {
size_t count = ctx->sym_sh_->sh_size / sizeof(ElfW(Sym));
result = iterate_symbol_table_impl(symbol_name, ctx->symtab_, ctx->strtab_, count);
if (result)
return result;
}
if (ctx->dynsymtab_ && ctx->dynstrtab_) {
size_t count = ctx->dynsym_sh_->sh_size / sizeof(ElfW(Sym));
result = iterate_symbol_table_impl(symbol_name, ctx->dynsymtab_, ctx->dynstrtab_, count);
if (result)
return result;
}
return NULL;
}
void *resolve_elf_internal_symbol(const char *library_name, const char *symbol_name) {
void *result = NULL;
if (library_name) {
RuntimeModule module = ProcessRuntimeUtility::GetProcessModule(library_name);
if (module.load_address) {
auto mmapFileMng = MmapFileManager(module.path);
auto file_mem = mmapFileMng.map();
elf_ctx_t ctx;
memset(&ctx, 0, sizeof(elf_ctx_t));
if (file_mem) {
elf_ctx_init(&ctx, file_mem);
result = elf_ctx_iterate_symbol_table(&ctx, symbol_name);
}
if (result)
result = (void *)((addr_t)result + (addr_t)module.load_address - ((addr_t)file_mem - (addr_t)ctx.load_bias));
}
}
if (!result) {
auto ProcessModuleMap = ProcessRuntimeUtility::GetProcessModuleMap();
for (auto module : ProcessModuleMap) {
if (module.load_address) {
auto mmapFileMng = MmapFileManager(module.path);
auto file_mem = mmapFileMng.map();
elf_ctx_t ctx;
memset(&ctx, 0, sizeof(elf_ctx_t));
if (file_mem) {
elf_ctx_init(&ctx, file_mem);
result = elf_ctx_iterate_symbol_table(&ctx, symbol_name);
}
if (result)
result = (void *)((addr_t)result + (addr_t)module.load_address - ((addr_t)file_mem - (addr_t)ctx.load_bias));
}
if (result)
break;
}
}
return result;
}
// impl at "android_restriction.cc"
extern std::vector<void *> 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;
}

View File

@ -0,0 +1,114 @@
#include "dobby_symbol_resolver.h"
#include "macho/dobby_symbol_resolver_priv.h"
#include "macho_file_symbol_resolver.h"
#include "dobby/common.h"
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include "PlatformUtil/ProcessRuntimeUtility.h"
#include "macho_ctx.h"
#include "shared_cache_ctx.h"
#if !defined(BUILDING_KERNEL)
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
#endif
#undef LOG_TAG
#define LOG_TAG "DobbySymbolResolver"
PUBLIC void *DobbySymbolResolver(const char *image_name, const char *symbol_name_pattern) {
uintptr_t result = 0;
auto modules = ProcessRuntimeUtility::GetProcessModuleMap();
for (auto iter = modules.begin(); iter != modules.end(); iter++) {
auto module = *iter;
// image filter
if (image_name && !strstr(module.path, image_name))
continue;
// dyld in shared cached at new os version
// ignore dyld, as some functions as own implementation in dyld
if (!image_name && strstr(module.path, "dyld"))
continue;
auto header = (mach_header_t *)module.load_address;
if (header == nullptr)
continue;
#if 0
DEBUG_LOG("resolve image: %s", module.path);
#endif
nlist_t *symtab = NULL;
uint32_t symtab_count = 0;
char *strtab = NULL;
#if !defined(BUILDING_KERNEL)
#if defined(__arm__) || defined(__aarch64__)
static int shared_cache_ctx_init_once = 0;
static shared_cache_ctx_t shared_cache_ctx;
if (shared_cache_ctx_init_once == 0) {
shared_cache_ctx_init_once = 1;
shared_cache_ctx_init(&shared_cache_ctx);
shared_cache_load_symbols(&shared_cache_ctx);
}
if (shared_cache_ctx.mmap_shared_cache) {
// shared cache library
if (shared_cache_is_contain(&shared_cache_ctx, (addr_t)header, 0)) {
shared_cache_get_symbol_table(&shared_cache_ctx, header, &symtab, &symtab_count, &strtab);
}
}
if (symtab && strtab) {
result = macho_iterate_symbol_table((char *)symbol_name_pattern, symtab, symtab_count, strtab);
}
if (result) {
result = result + shared_cache_ctx.runtime_slide;
return (void *)result;
}
#endif
#endif
macho_ctx_t macho_ctx(header);
result = macho_ctx.symbol_resolve(symbol_name_pattern);
if (result) {
return (void *)result;
}
}
#if !defined(BUILDING_KERNEL)
mach_header_t *dyld_header = NULL;
if (image_name != NULL && strcmp(image_name, "dyld") == 0) {
// task info
task_dyld_info_data_t task_dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
if (task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count)) {
return NULL;
}
// get dyld load address
const struct dyld_all_image_infos *infos =
(struct dyld_all_image_infos *)(uintptr_t)task_dyld_info.all_image_info_addr;
dyld_header = (mach_header_t *)infos->dyldImageLoadAddress;
macho_ctx_t dyld_ctx(dyld_header);
result = dyld_ctx.symbol_resolve(symbol_name_pattern);
bool is_dyld_in_cache = ((mach_header_t *)dyld_header)->flags & MH_DYLIB_IN_CACHE;
if (!is_dyld_in_cache && result == 0) {
result = macho_file_symbol_resolve(dyld_header->cputype, dyld_header->cpusubtype, "/usr/lib/dyld",
(char *)symbol_name_pattern);
result += (uintptr_t)dyld_header;
}
}
#endif
if (result == 0) {
DEBUG_LOG("symbol resolver failed: %s", symbol_name_pattern);
}
return (void *)result;
}

View File

@ -0,0 +1,6 @@
#include <stdint.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include "macho_ctx.h"

View File

@ -0,0 +1,358 @@
#include "macho_ctx.h"
#include <mach-o/dyld.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <string.h>
#define ASSERT(x)
uintptr_t macho_iterate_symbol_table(char *symbol_name_pattern, nlist_t *symtab, uint32_t symtab_count, char *strtab) {
for (uint32_t i = 0; i < symtab_count; i++) {
if (symtab[i].n_value) {
uint32_t strtab_offset = symtab[i].n_un.n_strx;
char *symbol_name = strtab + strtab_offset;
#if 0
printf("> %s", symbol_name);
#endif
if (strcmp(symbol_name_pattern, symbol_name) == 0) {
return symtab[i].n_value;
}
if (symbol_name[0] == '_') {
if (strcmp(symbol_name_pattern, &symbol_name[1]) == 0) {
return symtab[i].n_value;
}
}
}
}
return 0;
}
// ---
void macho_ctx_t::init(mach_header_t *header, bool is_runtime_mode) {
memset(this, 0, sizeof(macho_ctx_t));
this->is_runtime_mode = is_runtime_mode;
this->header = header;
segment_command_t *curr_seg_cmd;
segment_command_t *text_segment = 0, *text_exec_segment = 0, *data_segment = 0, *data_const_segment = 0,
*linkedit_segment = 0;
struct symtab_command *symtab_cmd = 0;
struct dysymtab_command *dysymtab_cmd = 0;
struct dyld_info_command *dyld_info_cmd = 0;
struct linkedit_data_command *exports_trie_cmd = 0;
struct linkedit_data_command *chained_fixups_cmd = NULL;
curr_seg_cmd = (segment_command_t *)((uintptr_t)header + sizeof(mach_header_t));
for (int i = 0; i < header->ncmds; i++) {
if (curr_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
// BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB and REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
this->segments[this->segments_count++] = curr_seg_cmd;
if (strcmp(curr_seg_cmd->segname, "__LINKEDIT") == 0) {
linkedit_segment = curr_seg_cmd;
} else if (strcmp(curr_seg_cmd->segname, "__DATA") == 0) {
data_segment = curr_seg_cmd;
} else if (strcmp(curr_seg_cmd->segname, "__DATA_CONST") == 0) {
data_const_segment = curr_seg_cmd;
} else if (strcmp(curr_seg_cmd->segname, "__TEXT") == 0) {
text_segment = curr_seg_cmd;
} else if (strcmp(curr_seg_cmd->segname, "__TEXT_EXEC") == 0) {
text_exec_segment = curr_seg_cmd;
}
} else if (curr_seg_cmd->cmd == LC_SYMTAB) {
symtab_cmd = (struct symtab_command *)curr_seg_cmd;
} else if (curr_seg_cmd->cmd == LC_DYSYMTAB) {
dysymtab_cmd = (struct dysymtab_command *)curr_seg_cmd;
} else if (curr_seg_cmd->cmd == LC_DYLD_INFO || curr_seg_cmd->cmd == LC_DYLD_INFO_ONLY) {
dyld_info_cmd = (struct dyld_info_command *)curr_seg_cmd;
} else if (curr_seg_cmd->cmd == LC_DYLD_EXPORTS_TRIE) {
exports_trie_cmd = (struct linkedit_data_command *)curr_seg_cmd;
} else if (curr_seg_cmd->cmd == LC_DYLD_CHAINED_FIXUPS) {
chained_fixups_cmd = (struct linkedit_data_command *)curr_seg_cmd;
}
curr_seg_cmd = (segment_command_t *)((uintptr_t)curr_seg_cmd + curr_seg_cmd->cmdsize);
}
uintptr_t slide = (uintptr_t)header - (uintptr_t)text_segment->vmaddr;
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
if (!is_runtime_mode) {
// as mmap, all segment is close
uintptr_t linkedit_segment_vmaddr = linkedit_segment->fileoff;
linkedit_base = (uintptr_t)slide + linkedit_segment_vmaddr - linkedit_segment->fileoff;
}
vm_region_start = segments[0]->vmaddr;
// skip __LINKEDIT
if (strcmp(segments[0]->segname, "__LINKEDIT") == 0) {
vm_region_start = segments[1]->vmaddr;
}
vm_region_end = segments[segments_count - 1]->vmaddr + segments[segments_count - 1]->vmsize;
vmaddr = vm_region_start;
vmsize = vm_region_end - vm_region_start;
this->text_seg = text_segment;
this->text_exec_seg = text_exec_segment;
this->data_seg = data_segment;
this->data_const_seg = data_const_segment;
this->linkedit_seg = linkedit_segment;
this->symtab_cmd = symtab_cmd;
this->dysymtab_cmd = dysymtab_cmd;
this->dyld_info_cmd = dyld_info_cmd;
this->exports_trie_cmd = exports_trie_cmd;
this->chained_fixups_cmd = chained_fixups_cmd;
this->slide = slide;
this->linkedit_base = linkedit_base;
this->symtab = (nlist_t *)(this->linkedit_base + this->symtab_cmd->symoff);
this->strtab = (char *)(this->linkedit_base + this->symtab_cmd->stroff);
this->indirect_symtab = (uint32_t *)(this->linkedit_base + this->dysymtab_cmd->indirectsymoff);
}
uintptr_t macho_ctx_t::iterate_symbol_table(const char *symbol_name_pattern) {
nlist_t *symtab = this->symtab;
uint32_t symtab_count = this->symtab_cmd->nsyms;
char *strtab = this->strtab;
for (uint32_t i = 0; i < symtab_count; i++) {
if (symtab[i].n_value) {
uint32_t strtab_offset = symtab[i].n_un.n_strx;
char *symbol_name = strtab + strtab_offset;
#if 0
printf("> %s", symbol_name);
#endif
if (strcmp(symbol_name_pattern, symbol_name) == 0) {
return symtab[i].n_value;
}
if (symbol_name[0] == '_') {
if (strcmp(symbol_name_pattern, &symbol_name[1]) == 0) {
return symtab[i].n_value;
}
}
}
}
return 0;
}
uintptr_t read_uleb128(const uint8_t **pp, const uint8_t *end) {
uint8_t *p = (uint8_t *)*pp;
uint64_t result = 0;
int bit = 0;
do {
if (p == end)
ASSERT(p == end);
uint64_t slice = *p & 0x7f;
if (bit > 63)
ASSERT(bit > 63);
else {
result |= (slice << bit);
bit += 7;
}
} while (*p++ & 0x80);
*pp = p;
return (uintptr_t)result;
}
intptr_t read_sleb128(const uint8_t **pp, const uint8_t *end) {
uint8_t *p = (uint8_t *)*pp;
int64_t result = 0;
int bit = 0;
uint8_t byte;
do {
if (p == end)
ASSERT(p == end);
byte = *p++;
result |= (((int64_t)(byte & 0x7f)) << bit);
bit += 7;
} while (byte & 0x80);
// sign extend negative numbers
if ((byte & 0x40) != 0)
result |= (~0ULL) << bit;
*pp = p;
return (intptr_t)result;
}
// dyld
// bool MachOLoaded::findExportedSymbol
// MachOLoaded::trieWalk
uint8_t *tail_walk(const uint8_t *start, const uint8_t *end, const char *symbol) {
uint32_t visitedNodeOffsets[128];
int visitedNodeOffsetCount = 0;
visitedNodeOffsets[visitedNodeOffsetCount++] = 0;
const uint8_t *p = start;
while (p < end) {
uint64_t terminalSize = *p++;
if (terminalSize > 127) {
// except for re-export-with-rename, all terminal sizes fit in one byte
--p;
terminalSize = read_uleb128(&p, end);
}
if ((*symbol == '\0') && (terminalSize != 0)) {
return (uint8_t *)p;
}
const uint8_t *children = p + terminalSize;
if (children > end) {
// diag.error("malformed trie node, terminalSize=0x%llX extends past end of trie\n", terminalSize);
return NULL;
}
uint8_t childrenRemaining = *children++;
p = children;
uint64_t nodeOffset = 0;
for (; childrenRemaining > 0; --childrenRemaining) {
const char *ss = symbol;
bool wrongEdge = false;
// scan whole edge to get to next edge
// if edge is longer than target symbol name, don't read past end of symbol name
char c = *p;
while (c != '\0') {
if (!wrongEdge) {
if (c != *ss)
wrongEdge = true;
++ss;
}
++p;
c = *p;
}
if (wrongEdge) {
// advance to next child
++p; // skip over zero terminator
// skip over uleb128 until last byte is found
while ((*p & 0x80) != 0)
++p;
++p; // skip over last byte of uleb128
if (p > end) {
// diag.error("malformed trie node, child node extends past end of trie\n");
return nullptr;
}
} else {
// the symbol so far matches this edge (child)
// so advance to the child's node
++p;
nodeOffset = read_uleb128(&p, end);
if ((nodeOffset == 0) || (&start[nodeOffset] > end)) {
// diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset);
return nullptr;
}
symbol = ss;
break;
}
}
if (nodeOffset != 0) {
if (nodeOffset > (uint64_t)(end - start)) {
// diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset);
return NULL;
}
for (int i = 0; i < visitedNodeOffsetCount; ++i) {
if (visitedNodeOffsets[i] == nodeOffset) {
// diag.error("malformed trie child, cycle to nodeOffset=0x%llX\n", nodeOffset);
return NULL;
}
}
visitedNodeOffsets[visitedNodeOffsetCount++] = (uint32_t)nodeOffset;
p = &start[nodeOffset];
} else
p = end;
}
return NULL;
}
uintptr_t macho_ctx_t::iterate_exported_symbol(const char *symbol_name, uint64_t *out_flags) {
if (this->text_seg == NULL || this->linkedit_seg == NULL) {
return 0;
}
struct dyld_info_command *dyld_info_cmd = this->dyld_info_cmd;
struct linkedit_data_command *exports_trie_cmd = this->exports_trie_cmd;
if (exports_trie_cmd == NULL && dyld_info_cmd == NULL)
return 0;
uint32_t trieFileOffset = dyld_info_cmd ? dyld_info_cmd->export_off : exports_trie_cmd->dataoff;
uint32_t trieFileSize = dyld_info_cmd ? dyld_info_cmd->export_size : exports_trie_cmd->datasize;
void *exports = (void *)(this->linkedit_base + trieFileOffset);
if (exports == NULL)
return 0;
uint8_t *exports_start = (uint8_t *)exports;
uint8_t *exports_end = exports_start + trieFileSize;
uint8_t *node = (uint8_t *)tail_walk(exports_start, exports_end, symbol_name);
if (node == NULL)
return 0;
const uint8_t *p = node;
const uintptr_t flags = read_uleb128(&p, exports_end);
if (out_flags)
*out_flags = flags;
if (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
const uint64_t ordinal = read_uleb128(&p, exports_end);
const char *importedName = (const char *)p;
if (importedName[0] == '\0') {
importedName = symbol_name;
return 0;
}
// trick
// printf("reexported symbol: %s\n", importedName);
return (uintptr_t)importedName;
}
uint64_t trieValue = read_uleb128(&p, exports_end);
return trieValue;
#if 0
if (off == (void *)0) {
if (symbol_name[0] != '_' && strlen(&symbol_name[1]) >= 1) {
char _symbol_name[1024] = {0};
_symbol_name[0] = '_';
strcpy(&_symbol_name[1], symbol_name);
off = (void *)walk_exported_trie((const uint8_t *)exports, (const uint8_t *)exports + trieFileSize, _symbol_name);
}
}
#endif
}
uintptr_t macho_ctx_t::symbol_resolve_options(const char *symbol_name_pattern, resolve_symbol_type_t type) {
if (type & RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE) {
uintptr_t result = iterate_symbol_table(symbol_name_pattern);
if (result) {
result = result + (this->is_runtime_mode ? this->slide : 0);
return result;
}
}
if (type & RESOLVE_SYMBOL_TYPE_EXPORTED) {
// binary exported table(uleb128)
uint64_t flags;
uintptr_t result = iterate_exported_symbol(symbol_name_pattern, &flags);
if (result) {
switch (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) {
case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: {
result += (uintptr_t)this->header;
} break;
case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: {
result += (uintptr_t)this->header;
} break;
case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: {
} break;
default:
break;
}
return result;
}
}
return 0;
}
uintptr_t macho_ctx_t::symbol_resolve(const char *symbol_name_pattern) {
return symbol_resolve_options(symbol_name_pattern, RESOLVE_SYMBOL_TYPE_ALL);
}

View File

@ -0,0 +1,86 @@
#pragma once
#include <sys/types.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.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
intptr_t read_sleb128(const uint8_t **pp, const uint8_t *end);
uintptr_t read_uleb128(const uint8_t **pp, const uint8_t *end);
typedef enum {
RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE = 1 << 0,
RESOLVE_SYMBOL_TYPE_EXPORTED = 1 << 1,
RESOLVE_SYMBOL_TYPE_ALL = RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE | RESOLVE_SYMBOL_TYPE_EXPORTED
} resolve_symbol_type_t;
struct macho_ctx_t {
bool is_runtime_mode;
mach_header_t *header;
uintptr_t vmaddr;
size_t vmsize;
uintptr_t vm_region_start;
uintptr_t vm_region_end;
uintptr_t slide;
uintptr_t linkedit_base;
segment_command_t *segments[64];
int segments_count;
segment_command_t *text_seg;
segment_command_t *data_seg;
segment_command_t *text_exec_seg;
segment_command_t *data_const_seg;
segment_command_t *linkedit_seg;
struct symtab_command *symtab_cmd;
struct dysymtab_command *dysymtab_cmd;
struct dyld_info_command *dyld_info_cmd;
struct linkedit_data_command *exports_trie_cmd;
struct linkedit_data_command *chained_fixups_cmd;
nlist_t *symtab;
char *strtab;
uint32_t *indirect_symtab;
explicit macho_ctx_t(mach_header_t *header, bool is_runtime_mode = true) {
init(header, is_runtime_mode);
}
void init(mach_header_t *header, bool is_runtime_mode);
uintptr_t iterate_symbol_table(const char *symbol_name_pattern);
uintptr_t iterate_exported_symbol(const char *symbol_name, uint64_t *out_flags);
uintptr_t symbol_resolve_options(const char *symbol_name_pattern, resolve_symbol_type_t type);
uintptr_t symbol_resolve(const char *symbol_name_pattern);
};
#ifdef __cplusplus
extern "C" {
#endif
uintptr_t macho_iterate_symbol_table(char *name_pattern, nlist_t *symtab, uint32_t symtab_count, char *strtab);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,68 @@
#include "macho_file_symbol_resolver.h"
#include "SymbolResolver/mmap_file_util.h"
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
uintptr_t macho_file_memory_symbol_resolve(cpu_type_t in_cputype, cpu_subtype_t in_cpusubtype, const uint8_t *file_mem,
char *symbol_name_pattern) {
mach_header_t *header = (mach_header_t *)file_mem;
struct fat_header *fh = (struct fat_header *)file_mem;
if (fh->magic == OSSwapBigToHostInt32(FAT_MAGIC)) {
const struct fat_arch *archs = (struct fat_arch *)(((uintptr_t)fh) + sizeof(fat_header));
mach_header_t *header_arm64 = NULL;
mach_header_t *header_arm64e = NULL;
mach_header_t *header_x64 = NULL;
for (size_t i = 0; i < OSSwapBigToHostInt32(fh->nfat_arch); i++) {
uint64_t offset;
uint64_t len;
cpu_type_t cputype = (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype);
cpu_subtype_t cpusubtype = (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype);
offset = OSSwapBigToHostInt32(archs[i].offset);
len = OSSwapBigToHostInt32(archs[i].size);
if (cputype == CPU_TYPE_X86_64) {
header_x64 = (mach_header_t *)&file_mem[offset];
} else if (cputype == CPU_TYPE_ARM64 && (cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) {
header_arm64e = (mach_header_t *)&file_mem[offset];
} else if (cputype == CPU_TYPE_ARM64) {
header_arm64 = (mach_header_t *)&file_mem[offset];
}
if ((cputype == in_cputype) && ((cpusubtype & in_cpusubtype) == in_cpusubtype)) {
header = (mach_header_t *)&file_mem[offset];
break;
}
}
if (header == (mach_header_t *)file_mem) {
if (in_cputype == 0 && in_cpusubtype == 0) {
#if defined(__arm64__) || defined(__aarch64__)
header = header_arm64e ? header_arm64e : header_arm64;
#else
header = header_x64;
#endif
}
}
}
macho_ctx_t ctx(header, false);
return ctx.symbol_resolve_options(symbol_name_pattern, RESOLVE_SYMBOL_TYPE_SYMBOL_TABLE);
}
uintptr_t macho_file_symbol_resolve(cpu_type_t cpu, cpu_subtype_t subtype, const char *file,
char *symbol_name_pattern) {
#if defined(COMPILE_WITH_NO_STDLIB)
return 0;
#endif
MmapFileManager mng(file);
auto mmap_buffer = mng.map();
if (!mmap_buffer) {
return 0;
}
return macho_file_memory_symbol_resolve(cpu, subtype, mmap_buffer, symbol_name_pattern);
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "macho_ctx.h"
uintptr_t macho_file_memory_symbol_resolve(cpu_type_t cpu, cpu_subtype_t subtype, const uint8_t *file_mem,
char *symbol_name_pattern);
uintptr_t macho_file_symbol_resolve(cpu_type_t cpu, cpu_subtype_t subtype, const char *file, char *symbol_name_pattern);

View File

@ -0,0 +1,560 @@
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
* Copyright (c) 2006-2015 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#ifndef __DYLD_CACHE_FORMAT__
#define __DYLD_CACHE_FORMAT__
#include <stdint.h>
#include <uuid/uuid.h>
#include <TargetConditionals.h>
struct dyld_cache_header
{
char magic[16]; // e.g. "dyld_v0 i386"
uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
uint32_t mappingCount; // number of dyld_cache_mapping_info entries
uint32_t imagesOffsetOld; // UNUSED: moved to imagesOffset to prevent older dsc_extarctors from crashing
uint32_t imagesCountOld; // UNUSED: moved to imagesCount to prevent older dsc_extarctors from crashing
uint64_t dyldBaseAddress; // base address of dyld when cache was built
uint64_t codeSignatureOffset; // file offset of code signature blob
uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file)
uint64_t slideInfoOffsetUnused; // unused. Used to be file offset of kernel slid info
uint64_t slideInfoSizeUnused; // unused. Used to be size of kernel slid info
uint64_t localSymbolsOffset; // file offset of where local symbols are stored
uint64_t localSymbolsSize; // size of local symbols information
uint8_t uuid[16]; // unique value for each shared cache file
uint64_t cacheType; // 0 for development, 1 for production, 2 for multi-cache
uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses
uint32_t branchPoolsCount; // number of uint64_t entries
uint64_t dyldInCacheMH; // (unslid) address of mach_header of dyld in cache
uint64_t dyldInCacheEntry; // (unslid) address of entry point (_dyld_start) of dyld in cache
uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info
uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries
uint64_t patchInfoAddr; // (unslid) address of dyld_cache_patch_info
uint64_t patchInfoSize; // Size of all of the patch information pointed to via the dyld_cache_patch_info
uint64_t otherImageGroupAddrUnused; // unused
uint64_t otherImageGroupSizeUnused; // unused
uint64_t progClosuresAddr; // (unslid) address of list of program launch closures
uint64_t progClosuresSize; // size of list of program launch closures
uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures
uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures
uint32_t platform; // platform number (macOS=1, etc)
uint32_t formatVersion : 8, // dyld3::closure::kFormatVersion
dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid
simulator : 1, // for simulator of specified platform
locallyBuiltCache : 1, // 0 for B&I built cache, 1 for locally built cache
builtFromChainedFixups : 1, // some dylib in cache was built using chained fixups, so patch tables must be used for overrides
padding : 20; // TBD
uint64_t sharedRegionStart; // base load address of cache if not slid
uint64_t sharedRegionSize; // overall size required to map the cache and all subCaches, if any
uint64_t maxSlide; // runtime slide of cache can be between zero and this value
uint64_t dylibsImageArrayAddr; // (unslid) address of ImageArray for dylibs in this cache
uint64_t dylibsImageArraySize; // size of ImageArray for dylibs in this cache
uint64_t dylibsTrieAddr; // (unslid) address of trie of indexes of all cached dylibs
uint64_t dylibsTrieSize; // size of trie of cached dylib paths
uint64_t otherImageArrayAddr; // (unslid) address of ImageArray for dylibs and bundles with dlopen closures
uint64_t otherImageArraySize; // size of ImageArray for dylibs and bundles with dlopen closures
uint64_t otherTrieAddr; // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures
uint64_t otherTrieSize; // size of trie of dylibs and bundles with dlopen closures
uint32_t mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info
uint32_t mappingWithSlideCount; // number of dyld_cache_mapping_and_slide_info entries
uint64_t dylibsPBLStateArrayAddrUnused; // unused
uint64_t dylibsPBLSetAddr; // (unslid) address of PrebuiltLoaderSet of all cached dylibs
uint64_t programsPBLSetPoolAddr; // (unslid) address of pool of PrebuiltLoaderSet for each program
uint64_t programsPBLSetPoolSize; // size of pool of PrebuiltLoaderSet for each program
uint64_t programTrieAddr; // (unslid) address of trie mapping program path to PrebuiltLoaderSet
uint32_t programTrieSize;
uint32_t osVersion; // OS Version of dylibs in this cache for the main platform
uint32_t altPlatform; // e.g. iOSMac on macOS
uint32_t altOsVersion; // e.g. 14.0 for iOSMac
uint64_t swiftOptsOffset; // VM offset from cache_header* to Swift optimizations header
uint64_t swiftOptsSize; // size of Swift optimizations header
uint32_t subCacheArrayOffset; // file offset to first dyld_subcache_entry
uint32_t subCacheArrayCount; // number of subCache entries
uint8_t symbolFileUUID[16]; // unique value for the shared cache file containing unmapped local symbols
uint64_t rosettaReadOnlyAddr; // (unslid) address of the start of where Rosetta can add read-only/executable data
uint64_t rosettaReadOnlySize; // maximum size of the Rosetta read-only/executable region
uint64_t rosettaReadWriteAddr; // (unslid) address of the start of where Rosetta can add read-write data
uint64_t rosettaReadWriteSize; // maximum size of the Rosetta read-write region
uint32_t imagesOffset; // file offset to first dyld_cache_image_info
uint32_t imagesCount; // number of dyld_cache_image_info entries
uint32_t cacheSubType; // 0 for development, 1 for production, when cacheType is multi-cache(2)
uint64_t objcOptsOffset; // VM offset from cache_header* to ObjC optimizations header
uint64_t objcOptsSize; // size of ObjC optimizations header
uint64_t cacheAtlasOffset; // VM offset from cache_header* to embedded cache atlas for process introspection
uint64_t cacheAtlasSize; // size of embedded cache atlas
uint64_t dynamicDataOffset; // VM offset from cache_header* to the location of dyld_cache_dynamic_data_header
uint64_t dynamicDataMaxSize; // maximum size of space reserved from dynamic data
};
// Uncomment this and check the build errors for the current mapping offset to check against when adding new fields.
// template<size_t size> class A { int x[-size]; }; A<sizeof(dyld_cache_header)> a;
struct dyld_cache_mapping_info {
uint64_t address;
uint64_t size;
uint64_t fileOffset;
uint32_t maxProt;
uint32_t initProt;
};
// Contains the flags for the dyld_cache_mapping_and_slide_info flgs field
enum {
DYLD_CACHE_MAPPING_AUTH_DATA = 1 << 0U,
DYLD_CACHE_MAPPING_DIRTY_DATA = 1 << 1U,
DYLD_CACHE_MAPPING_CONST_DATA = 1 << 2U,
DYLD_CACHE_MAPPING_TEXT_STUBS = 1 << 3U,
DYLD_CACHE_DYNAMIC_CONFIG_DATA = 1 << 4U,
};
struct dyld_cache_mapping_and_slide_info {
uint64_t address;
uint64_t size;
uint64_t fileOffset;
uint64_t slideInfoFileOffset;
uint64_t slideInfoFileSize;
uint64_t flags;
uint32_t maxProt;
uint32_t initProt;
};
struct dyld_cache_image_info
{
uint64_t address;
uint64_t modTime;
uint64_t inode;
uint32_t pathFileOffset;
uint32_t pad;
};
struct dyld_cache_image_info_extra
{
uint64_t exportsTrieAddr; // address of trie in unslid cache
uint64_t weakBindingsAddr;
uint32_t exportsTrieSize;
uint32_t weakBindingsSize;
uint32_t dependentsStartArrayIndex;
uint32_t reExportsStartArrayIndex;
};
struct dyld_cache_accelerator_info
{
uint32_t version; // currently 1
uint32_t imageExtrasCount; // does not include aliases
uint32_t imagesExtrasOffset; // offset into this chunk of first dyld_cache_image_info_extra
uint32_t bottomUpListOffset; // offset into this chunk to start of 16-bit array of sorted image indexes
uint32_t dylibTrieOffset; // offset into this chunk to start of trie containing all dylib paths
uint32_t dylibTrieSize; // size of trie containing all dylib paths
uint32_t initializersOffset; // offset into this chunk to start of initializers list
uint32_t initializersCount; // size of initializers list
uint32_t dofSectionsOffset; // offset into this chunk to start of DOF sections list
uint32_t dofSectionsCount; // size of initializers list
uint32_t reExportListOffset; // offset into this chunk to start of 16-bit array of re-exports
uint32_t reExportCount; // size of re-exports
uint32_t depListOffset; // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward)
uint32_t depListCount; // size of dependencies
uint32_t rangeTableOffset; // offset into this chunk to start of ss
uint32_t rangeTableCount; // size of dependencies
uint64_t dyldSectionAddr; // address of libdyld's __dyld section in unslid cache
};
struct dyld_cache_accelerator_initializer
{
uint32_t functionOffset; // address offset from start of cache mapping
uint32_t imageIndex;
};
struct dyld_cache_range_entry
{
uint64_t startAddress; // unslid address of start of region
uint32_t size;
uint32_t imageIndex;
};
struct dyld_cache_accelerator_dof
{
uint64_t sectionAddress; // unslid address of start of region
uint32_t sectionSize;
uint32_t imageIndex;
};
struct dyld_cache_image_text_info
{
uuid_t uuid;
uint64_t loadAddress; // unslid address of start of __TEXT
uint32_t textSegmentSize;
uint32_t pathOffset; // offset from start of cache file
};
// The rebasing info is to allow the kernel to lazily rebase DATA pages of the
// dyld shared cache. Rebasing is adding the slide to interior pointers.
struct dyld_cache_slide_info
{
uint32_t version; // currently 1
uint32_t toc_offset;
uint32_t toc_count;
uint32_t entries_offset;
uint32_t entries_count;
uint32_t entries_size; // currently 128
// uint16_t toc[toc_count];
// entrybitmap entries[entries_count];
};
struct dyld_cache_slide_info_entry {
uint8_t bits[4096/(8*4)]; // 128-byte bitmap
};
// The version 2 of the slide info uses a different compression scheme. Since
// only interior pointers (pointers that point within the cache) are rebased
// (slid), we know the possible range of the pointers and thus know there are
// unused bits in each pointer. We use those bits to form a linked list of
// locations needing rebasing in each page.
//
// Definitions:
//
// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size
// pageStarts[] = info + info->page_starts_offset
// pageExtras[] = info + info->page_extras_offset
// valueMask = ~(info->delta_mask)
// deltaShift = __builtin_ctzll(info->delta_mask) - 2
//
// There are three cases:
//
// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE
// The page contains no values that need rebasing.
//
// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0
// All rebase locations are in one linked list. The offset of the first
// rebase location in the page is pageStarts[pageIndex] * 4.
//
// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
// Multiple linked lists are needed for all rebase locations in a page.
// The pagesExtras array contains 2 or more entries each of which is the
// start of a new linked list in the page. The first is at:
// extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF)
// The next is at extrasStartIndex+1. The last is denoted by
// having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[]
// set.
//
// For 64-bit architectures, there is always enough free bits to encode all
// possible deltas. The info->delta_mask field shows where the delta is located
// in the pointer. That value must be masked off (valueMask) before the slide
// is added to the pointer.
//
// For 32-bit architectures, there are only three bits free (the three most
// significant bits). To extract the delta, you must first subtract value_add
// from the pointer value, then AND with delta_mask, then shift by deltaShift.
// That still leaves a maximum delta to the next rebase location of 28 bytes.
// To reduce the number or chains needed, an optimization was added. Turns
// out zero is common in the DATA region. A zero can be turned into a
// non-rebasing entry in the linked list. The can be done because nothing
// in the shared cache should point out of its dylib to the start of the shared
// cache.
//
// The code for processing a linked list (chain) is:
//
// uint32_t delta = 1;
// while ( delta != 0 ) {
// uint8_t* loc = pageStart + pageOffset;
// uintptr_t rawValue = *((uintptr_t*)loc);
// delta = ((rawValue & deltaMask) >> deltaShift);
// uintptr_t newValue = (rawValue & valueMask);
// if ( newValue != 0 ) {
// newValue += valueAdd;
// newValue += slideAmount;
// }
// *((uintptr_t*)loc) = newValue;
// pageOffset += delta;
// }
//
//
struct dyld_cache_slide_info2
{
uint32_t version; // currently 2
uint32_t page_size; // currently 4096 (may also be 16384)
uint32_t page_starts_offset;
uint32_t page_starts_count;
uint32_t page_extras_offset;
uint32_t page_extras_count;
uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location
uint64_t value_add;
//uint16_t page_starts[page_starts_count];
//uint16_t page_extras[page_extras_count];
};
#define DYLD_CACHE_SLIDE_PAGE_ATTRS 0xC000 // high bits of uint16_t are flags
#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array)
#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing
#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page
// The version 3 of the slide info uses a different compression scheme. Since
// only interior pointers (pointers that point within the cache) are rebased
// (slid), we know the possible range of the pointers and thus know there are
// unused bits in each pointer. We use those bits to form a linked list of
// locations needing rebasing in each page.
//
// Definitions:
//
// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size
// pageStarts[] = info + info->page_starts_offset
//
// There are two cases:
//
// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
// The page contains no values that need rebasing.
//
// 2) otherwise...
// All rebase locations are in one linked list. The offset of the first
// rebase location in the page is pageStarts[pageIndex].
//
// A pointer is one of of the variants in dyld_cache_slide_pointer3
//
// The code for processing a linked list (chain) is:
//
// uint32_t delta = pageStarts[pageIndex];
// dyld_cache_slide_pointer3* loc = pageStart;
// do {
// loc += delta;
// delta = loc->offsetToNextPointer;
// if ( loc->auth.authenticated ) {
// newValue = loc->offsetFromSharedCacheBase + results->slide + auth_value_add;
// newValue = sign_using_the_various_bits(newValue);
// }
// else {
// uint64_t value51 = loc->pointerValue;
// uint64_t top8Bits = value51 & 0x0007F80000000000ULL;
// uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFFULL;
// uint64_t targetValue = ( top8Bits << 13 ) | bottom43Bits;
// newValue = targetValue + results->slide;
// }
// loc->raw = newValue;
// } while (delta != 0);
//
//
struct dyld_cache_slide_info3
{
uint32_t version; // currently 3
uint32_t page_size; // currently 4096 (may also be 16384)
uint32_t page_starts_count;
uint64_t auth_value_add;
uint16_t page_starts[/* page_starts_count */];
};
#define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE 0xFFFF // page has no rebasing
union dyld_cache_slide_pointer3
{
uint64_t raw;
struct {
uint64_t pointerValue : 51,
offsetToNextPointer : 11,
unused : 2;
} plain;
struct {
uint64_t offsetFromSharedCacheBase : 32,
diversityData : 16,
hasAddressDiversity : 1,
key : 2,
offsetToNextPointer : 11,
unused : 1,
authenticated : 1; // = 1;
} auth;
};
// The version 4 of the slide info is optimized for 32-bit caches up to 1GB.
// Since only interior pointers (pointers that point within the cache) are rebased
// (slid), we know the possible range of the pointers takes 30 bits. That
// gives us two bits to use to chain to the next rebase.
//
// Definitions:
//
// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size
// pageStarts[] = info + info->page_starts_offset
// pageExtras[] = info + info->page_extras_offset
// valueMask = ~(info->delta_mask)
// deltaShift = __builtin_ctzll(info->delta_mask) - 2
//
// There are three cases:
//
// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE
// The page contains no values that need rebasing.
//
// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0
// All rebase locations are in one linked list. The offset of the first
// rebase location in the page is pageStarts[pageIndex] * 4.
//
// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA
// Multiple chains are needed for all rebase locations in a page.
// The pagesExtras array contains 2 or more entries each of which is the
// start of a new chain in the page. The first is at:
// extrasStartIndex = (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_INDEX)
// The next is at extrasStartIndex+1. The last is denoted by
// having the high bit (DYLD_CACHE_SLIDE4_PAGE_EXTRA_END) of the pageExtras[].
//
// For 32-bit architectures, there are only two bits free (the two most
// significant bits). To extract the delta, you must first subtract value_add
// from the pointer value, then AND with delta_mask, then shift by deltaShift.
// That still leaves a maximum delta to the next rebase location of 12 bytes.
// To reduce the number or chains needed, an optimization was added. Turns
// most of the non-rebased data are small values and can be co-opt'ed into
// being used in the chain. The can be done because nothing
// in the shared cache should point to the first 64KB which are in the shared
// cache header information. So if the resulting pointer points to the
// start of the cache +/-32KB, then it is actually a small number that should
// not be rebased, but just reconstituted.
//
// The code for processing a linked list (chain) is:
//
// uint32_t delta = 1;
// while ( delta != 0 ) {
// uint8_t* loc = pageStart + pageOffset;
// uint32_t rawValue = *((uint32_t*)loc);
// delta = ((rawValue & deltaMask) >> deltaShift);
// uintptr_t newValue = (rawValue & valueMask);
// if ( (newValue & 0xFFFF8000) == 0 ) {
// // small positive non-pointer, use as-is
// }
// else if ( (newValue & 0x3FFF8000) == 0x3FFF8000 ) {
// // small negative non-pointer
// newValue |= 0xC0000000;
// }
// else {
// // pointer that needs rebasing
// newValue += valueAdd;
// newValue += slideAmount;
// }
// *((uint32_t*)loc) = newValue;
// pageOffset += delta;
// }
//
//
struct dyld_cache_slide_info4
{
uint32_t version; // currently 4
uint32_t page_size; // currently 4096 (may also be 16384)
uint32_t page_starts_offset;
uint32_t page_starts_count;
uint32_t page_extras_offset;
uint32_t page_extras_count;
uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location (0xC0000000)
uint64_t value_add; // base address of cache
//uint16_t page_starts[page_starts_count];
//uint16_t page_extras[page_extras_count];
};
#define DYLD_CACHE_SLIDE4_PAGE_NO_REBASE 0xFFFF // page has no rebasing
#define DYLD_CACHE_SLIDE4_PAGE_INDEX 0x7FFF // mask of page_starts[] values
#define DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA 0x8000 // index is into extras array (not a chain start offset)
#define DYLD_CACHE_SLIDE4_PAGE_EXTRA_END 0x8000 // last chain entry for page
struct dyld_cache_local_symbols_info
{
uint32_t nlistOffset; // offset into this chunk of nlist entries
uint32_t nlistCount; // count of nlist entries
uint32_t stringsOffset; // offset into this chunk of string pool
uint32_t stringsSize; // byte count of string pool
uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry
uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array
};
struct dyld_cache_local_symbols_entry
{
uint32_t dylibOffset; // offset in cache file of start of dylib
uint32_t nlistStartIndex; // start index of locals for this dylib
uint32_t nlistCount; // number of local symbols for this dylib
};
struct dyld_cache_local_symbols_entry_64
{
uint64_t dylibOffset; // offset in cache buffer of start of dylib
uint32_t nlistStartIndex; // start index of locals for this dylib
uint32_t nlistCount; // number of local symbols for this dylib
};
struct dyld_subcache_entry_v1
{
uint8_t uuid[16]; // The UUID of the subCache file
uint64_t cacheVMOffset; // The offset of this subcache from the main cache base address
};
struct dyld_subcache_entry
{
uint8_t uuid[16]; // The UUID of the subCache file
uint64_t cacheVMOffset; // The offset of this subcache from the main cache base address
char fileSuffix[32]; // The file name suffix of the subCache file e.g. ".25.data", ".03.development"
};
// This struct is a small piece of dynamic data that can be included in the shared region, and contains configuration
// data about the shared cache in use by the process. It is located
struct dyld_cache_dynamic_data_header
{
char magic[16]; // e.g. "dyld_data v0"
uint64_t fsId; // The fsid_t of the shared cache being used by a process
uint64_t fsObjId; // The fs_obj_id_t of the shared cache being used by a process
};
// This is the location of the macOS shared cache on macOS 11.0 and later
#define MACOSX_MRM_DYLD_SHARED_CACHE_DIR "/System/Library/dyld/"
// This is old define for the old location of the dyld cache
#define MACOSX_DYLD_SHARED_CACHE_DIR MACOSX_MRM_DYLD_SHARED_CACHE_DIR
#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/"
#define DRIVERKIT_DYLD_SHARED_CACHE_DIR "/System/DriverKit/System/Library/dyld/"
#if !TARGET_OS_SIMULATOR
#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_"
#else
#define DYLD_SHARED_CACHE_BASE_NAME "dyld_sim_shared_cache_"
#endif
#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development"
#define DYLD_SHARED_CACHE_DYNAMIC_DATA_MAGIC "dyld_data v0"
static const char* cryptexPrefixes[] = {
"/System/Volumes/Preboot/Cryptexes/OS/",
"/private/preboot/Cryptexes/OS/",
"/System/Cryptexes/OS"
};
static const uint64_t kDyldSharedCacheTypeDevelopment = 0;
static const uint64_t kDyldSharedCacheTypeProduction = 1;
static const uint64_t kDyldSharedCacheTypeUniversal = 2;
#endif // __DYLD_CACHE_FORMAT__

View File

@ -0,0 +1,183 @@
#include "shared_cache_ctx.h"
#include <mach/mach.h>
#include <mach/task.h>
#include <mach-o/dyld_images.h>
#include "logging/logging.h"
#include "mmap_file_util.h"
typedef uintptr_t addr_t;
extern "C" {
extern const char *dyld_shared_cache_file_path();
extern int __shared_region_check_np(uint64_t *startaddress);
}
const char *shared_cache_get_file_path() {
return dyld_shared_cache_file_path();
}
struct dyld_cache_header *shared_cache_get_load_addr() {
addr_t shared_cache_base = 0;
if (__shared_region_check_np((uint64_t *)&shared_cache_base) != 0) {
WARN_LOG("__shared_region_check_np failed");
}
if (shared_cache_base)
return (struct dyld_cache_header *)shared_cache_base;
// task info
task_dyld_info_data_t task_dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
kern_return_t ret = task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count);
if (ret != KERN_SUCCESS) {
ERROR_LOG("task_info failed, ret: %d", ret);
return NULL;
}
// get dyld load address
auto *infos = (struct dyld_all_image_infos *)(uintptr_t)task_dyld_info.all_image_info_addr;
auto *shared_cache = (struct dyld_cache_header *)infos->sharedCacheBaseAddress;
return shared_cache;
}
int shared_cache_load_symbols(shared_cache_ctx_t *ctx) {
uint64_t localSymbolsOffset = 0;
bool latest_shared_cache_format = true;
const char *shared_cache_path = shared_cache_get_file_path();
char shared_cache_symbols_path[4096] = {0};
{
strcat(shared_cache_symbols_path, shared_cache_path);
strcat(shared_cache_symbols_path, ".symbols");
}
auto mmapSharedCacheSymbolsMng = new MmapFileManager(shared_cache_symbols_path);
auto mmap_buffer = mmapSharedCacheSymbolsMng->map();
if (mmap_buffer) { // iphoneos >= 15.0, which has .symbols file
ctx->mmap_shared_cache = (struct dyld_cache_header *)mmap_buffer;
localSymbolsOffset = ctx->mmap_shared_cache->localSymbolsOffset;
} else {
// iphoneos < 15.0, which has no .symbols file
auto mmapSharedCacheMng = new MmapFileManager(shared_cache_symbols_path);
auto runtime_shared_cache = ctx->runtime_shared_cache;
uint64_t mmap_length = runtime_shared_cache->localSymbolsSize;
uint64_t mmap_offset = runtime_shared_cache->localSymbolsOffset;
if (mmap_length == 0)
return -1;
auto mmap_buffer = mmapSharedCacheMng->map_options(mmap_length, mmap_offset);
if (!mmap_buffer) {
return -1;
}
// fake shared cache header
auto mmap_shared_cache =
(struct dyld_cache_header *)((addr_t)mmap_buffer - runtime_shared_cache->localSymbolsOffset);
ctx->mmap_shared_cache = mmap_shared_cache;
localSymbolsOffset = runtime_shared_cache->localSymbolsOffset;
latest_shared_cache_format = false;
}
ctx->latest_shared_cache_format = latest_shared_cache_format;
{
auto mmap_shared_cache = ctx->mmap_shared_cache;
auto localInfo = (struct dyld_cache_local_symbols_info *)((char *)mmap_shared_cache + localSymbolsOffset);
ctx->local_symbols_info = localInfo;
if (ctx->latest_shared_cache_format) {
auto localEntries_64 = (struct dyld_cache_local_symbols_entry_64 *)((char *)localInfo + localInfo->entriesOffset);
ctx->local_symbols_entries_64 = localEntries_64;
} else {
auto localEntries = (struct dyld_cache_local_symbols_entry *)((char *)localInfo + localInfo->entriesOffset);
ctx->local_symbols_entries = localEntries;
}
ctx->symtab = (nlist_t *)((char *)localInfo + localInfo->nlistOffset);
ctx->strtab = ((char *)localInfo) + localInfo->stringsOffset;
}
return 0;
}
int shared_cache_ctx_init(shared_cache_ctx_t *ctx) {
memset(ctx, 0, sizeof(shared_cache_ctx_t));
auto runtime_shared_cache = shared_cache_get_load_addr();
if (!runtime_shared_cache) {
return -1;
}
ctx->runtime_shared_cache = runtime_shared_cache;
// shared cache slide
auto mappings =
(struct dyld_cache_mapping_info *)((char *)runtime_shared_cache + runtime_shared_cache->mappingOffset);
uintptr_t slide = (uintptr_t)runtime_shared_cache - (uintptr_t)(mappings[0].address);
ctx->runtime_slide = slide;
return 0;
}
// refer: dyld
bool shared_cache_is_contain(shared_cache_ctx_t *ctx, addr_t addr, size_t length) {
struct dyld_cache_header *runtime_shared_cache;
if (ctx) {
runtime_shared_cache = ctx->runtime_shared_cache;
} else {
runtime_shared_cache = shared_cache_get_load_addr();
}
addr_t region_start = runtime_shared_cache->sharedRegionStart + ctx->runtime_slide;
addr_t region_end = region_start + runtime_shared_cache->sharedRegionSize;
if (addr >= region_start && addr < region_end)
return true;
return false;
}
int shared_cache_get_symbol_table(shared_cache_ctx_t *ctx, mach_header_t *image_header, nlist_t **out_symtab,
uint32_t *out_symtab_count, char **out_strtab) {
uint64_t textOffsetInCache = (uint64_t)image_header - (uint64_t)ctx->runtime_shared_cache;
nlist_t *localNlists = NULL;
uint32_t localNlistCount = 0;
const char *localStrings = NULL;
const uint32_t entriesCount = ctx->local_symbols_info->entriesCount;
for (uint32_t i = 0; i < entriesCount; ++i) {
if (ctx->latest_shared_cache_format) {
if (ctx->local_symbols_entries_64[i].dylibOffset == textOffsetInCache) {
uint32_t localNlistStart = ctx->local_symbols_entries_64[i].nlistStartIndex;
localNlistCount = ctx->local_symbols_entries_64[i].nlistCount;
localNlists = &ctx->symtab[localNlistStart];
break;
}
} else {
if (ctx->local_symbols_entries[i].dylibOffset == textOffsetInCache) {
uint32_t localNlistStart = ctx->local_symbols_entries[i].nlistStartIndex;
localNlistCount = ctx->local_symbols_entries[i].nlistCount;
localNlists = &ctx->symtab[localNlistStart];
break;
}
}
#if 0
static struct dyld_cache_image_info *imageInfos = NULL;
imageInfos = (struct dyld_cache_image_info *)((addr_t)g_mmap_shared_cache + g_mmap_shared_cache->imagesOffset);
char *image_name = (char *)g_mmap_shared_cache + imageInfos[i].pathFileOffset;
INFO_LOG("dyld image: %s", image_name);
#endif
}
*out_symtab = localNlists;
*out_symtab_count = (uint32_t)localNlistCount;
*out_strtab = (char *)ctx->strtab;
return 0;
}

View File

@ -0,0 +1,45 @@
#include <sys/types.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include "shared-cache/dyld_cache_format.h"
#if defined(__LP64__)
typedef struct mach_header_64 mach_header_t;
typedef struct segment_command_64 segment_command_t;
typedef struct section_64 section_t;
typedef struct nlist_64 nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
#else
typedef struct mach_header mach_header_t;
typedef struct segment_command segment_command_t;
typedef struct section section_t;
typedef struct nlist nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
#endif
typedef uintptr_t addr_t;
typedef struct shared_cache_ctx {
struct dyld_cache_header *runtime_shared_cache;
struct dyld_cache_header *mmap_shared_cache;
uintptr_t runtime_slide;
bool latest_shared_cache_format;
struct dyld_cache_local_symbols_info *local_symbols_info;
struct dyld_cache_local_symbols_entry *local_symbols_entries;
struct dyld_cache_local_symbols_entry_64 *local_symbols_entries_64;
nlist_t *symtab;
char *strtab;
} shared_cache_ctx_t;
int shared_cache_ctx_init(shared_cache_ctx_t *ctx);
int shared_cache_load_symbols(shared_cache_ctx_t *ctx);
bool shared_cache_is_contain(shared_cache_ctx_t *ctx, addr_t addr, size_t length);
int shared_cache_get_symbol_table(shared_cache_ctx_t *ctx, mach_header_t *image_header, nlist_t **out_symtab,
uint32_t *out_symtab_count, char **out_strtab);

View File

@ -0,0 +1,63 @@
#pragma once
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/mman.h>
struct MmapFileManager {
const char *file_;
uint8_t *mmap_buffer;
size_t mmap_buffer_size;
explicit MmapFileManager(const char *file) : file_(file), mmap_buffer(nullptr) {
}
~MmapFileManager() {
if (mmap_buffer) {
munmap((void *)mmap_buffer, mmap_buffer_size);
}
}
uint8_t *map() {
size_t file_size = 0;
{
struct stat s;
int rt = stat(file_, &s);
if (rt != 0) {
// printf("mmap %s failed\n", file_);
return NULL;
}
file_size = s.st_size;
}
return map_options(file_size, 0);
}
uint8_t *map_options(size_t in_map_size, off_t in_map_off) {
if (!mmap_buffer) {
int fd = open(file_, O_RDONLY, 0);
if (fd < 0) {
// printf("%s open failed\n", file_);
return NULL;
}
// auto align
auto mmap_buffer = (uint8_t *)mmap(0, in_map_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, in_map_off);
if (mmap_buffer == MAP_FAILED) {
// printf("mmap %s failed\n", file_);
return NULL;
}
close(fd);
this->mmap_buffer = mmap_buffer;
this->mmap_buffer_size = in_map_size;
}
return mmap_buffer;
}
};

View File

@ -0,0 +1,26 @@
#include "SymbolResolver/dobby_symbol_resolver.h"
#include "dobby/common.h"
#include <windows.h>
#include <string>
#include <string.h>
#include "PlatformUtil/ProcessRuntimeUtility.h"
#include <vector>
#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;
}

View File

@ -0,0 +1,3 @@
macro(SET_OPTION option value)
set(${option} ${value} CACHE INTERNAL "" FORCE)
endmacro()

View File

@ -0,0 +1,29 @@
# Check files list exist
function(check_files_exist CHECK_FILES)
foreach (file ${CHECK_FILES})
if (NOT EXISTS "${file}")
message(FATAL_ERROR "${file} NOT EXISTS!")
endif ()
endforeach ()
endfunction(check_files_exist CHECK_FILES)
# Search suffix files
function(search_suffix_files suffix INPUT_VARIABLE OUTPUT_VARIABLE)
set(ResultFiles)
foreach (filePath ${${INPUT_VARIABLE}})
# message(STATUS "[*] searching *.${suffix} from ${filePath}")
file(GLOB files ${filePath}/*.${suffix})
set(ResultFiles ${ResultFiles} ${files})
endforeach ()
set(${OUTPUT_VARIABLE} ${ResultFiles} PARENT_SCOPE)
endfunction()
function(get_absolute_path_list input_list output_list)
set(absolute_list)
foreach (file ${${input_list}})
get_filename_component(absolute_file ${file} ABSOLUTE)
list(APPEND absolute_list ${absolute_file})
endforeach ()
set(${output_list} ${absolute_list} PARENT_SCOPE)
endfunction()

View File

@ -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 ()

View File

@ -0,0 +1,86 @@
if(__BUILD_ENVIRONMENT_CHECK)
return()
endif()
set(__BUILD_ENVIRONMENT_CHECK TRUE)
message(STATUS "")
message(STATUS "********* build environment check ***********")
# The Compiler ID
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
set(COMPILER.Clang 1)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(COMPILER.Gcc 1)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(COMPILER.MSVC 1)
else()
message (FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER_ID} not configured")
endif()
message(STATUS "\tCompiler: \t ${CMAKE_CXX_COMPILER_ID}")
if(MSVC)
string(TOLOWER ${MSVC_CXX_ARCHITECTURE_ID} CMAKE_SYSTEM_PROCESSOR)
set(CMAKE_SYSTEM_PROCESSOR ${MSVC_CXX_ARCHITECTURE_ID})
endif()
if(DOBBY_BUILD_SILICON)
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_OSX_ARCHITECTURES})
endif()
string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} CMAKE_SYSTEM_PROCESSOR)
# The Processor
if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*|x64.*")
set(PROCESSOR.X86_64 1)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*|amd64.*|AMD64.*")
set(PROCESSOR.X86 1)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm64.*")
set(PROCESSOR.AARCH64 1)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)")
set(PROCESSOR.AARCH64 1)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)")
set(PROCESSOR.ARM 1)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le")
message(STATUS "NOT SUPPORT ${CMAKE_SYSTEM_PROCESSOR}")
set(PROCESSOR.PPC64LE 1)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
message(STATUS "NOT SUPPORT ${CMAKE_SYSTEM_PROCESSOR}")
set(PROCESSOR.PPC64 1)
else()
message (FATAL_ERROR "Processor ${CMAKE_SYSTEM_PROCESSOR} not configured")
endif()
message(STATUS "\tProcessor:\t ${CMAKE_SYSTEM_PROCESSOR}")
# The System
if(CMAKE_SYSTEM_NAME MATCHES "^Android")
set(SYSTEM.Android 1)
elseif(CMAKE_SYSTEM_NAME MATCHES "^Windows")
set(SYSTEM.Windows 1)
elseif(CMAKE_SYSTEM_NAME MATCHES "^Linux")
set(SYSTEM.Linux 1)
elseif(CMAKE_SYSTEM_NAME MATCHES "^iOS")
set(SYSTEM.iOS 1)
set(SYSTEM.Darwin 1)
elseif(CMAKE_SYSTEM_NAME MATCHES "^macOS")
set(SYSTEM.macOS 1)
set(SYSTEM.Darwin 1)
elseif(CMAKE_SYSTEM_NAME MATCHES "^Darwin")
if(PROCESSOR.AARCH64 OR PROCESSOR.ARM)
set(CMAKE_SYSTEM_NAME "iOS or Silicon")
set(SYSTEM.iOS 1)
set(SYSTEM.Silicon 1)
elseif(PROCESSOR.X86 OR PROCESSOR.X86_64)
set(CMAKE_SYSTEM_NAME "macOS")
set(SYSTEM.macOS 1)
endif()
set(SYSTEM.Darwin 1)
else()
message (FATAL_ERROR "System ${CMAKE_SYSTEM_NAME} not configured")
endif()
message(STATUS "\tSystem: \t ${CMAKE_SYSTEM_NAME}")
message(STATUS "***************************************")
message(STATUS "")

View File

@ -0,0 +1,55 @@
# :< You Shall Not Pass!
if (0)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror")
endif ()
set(linker_flags "")
if (NOT DOBBY_DEBUG)
set(linker_flags "${linker_flags} -Wl,-x -Wl,-S")
endif ()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-stack-protector")
if (SYSTEM.Darwin)
# set(compiler_flags "${compiler_flags} -nostdinc++")
elseif (SYSTEM.Android)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer")
if (NOT DOBBY_DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
set(linker_flags "${linker_flags} -Wl,--gc-sections -Wl,--exclude-libs,ALL")
endif ()
elseif (SYSTEM.Linux)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
if (COMPILER.Clang)
if (PROCESSOR.ARM)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=armv7-unknown-linux-gnueabihf")
elseif (PROCESSOR.ARM64)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=aarch64-unknown-linux-gnu")
elseif (PROCESSOR.X86)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=i686-unknown-linux-gnu")
elseif (PROCESSOR.X86_64)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=x86_64-unknown-linux-gnu")
endif ()
endif ()
elseif (SYSTEM.Windows)
endif ()
if (NOT DOBBY_DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden")
endif ()
if (PROCESSOR.ARM)
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -arch armv7 -x assembler-with-cpp")
elseif (PROCESSOR.AARCH64)
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -arch arm64 -x assembler-with-cpp")
endif ()
# sync cxx with c flags
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS}")
message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")
message(STATUS "CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}")
message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}")
message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
message(STATUS "CMAKE_SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}")

View File

@ -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()

View File

@ -0,0 +1,30 @@
# set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE)
set(CMAKE_INSTALL_NAME_DIR "@rpath")
set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
add_library(DobbyX ${DOBBY_LIBRARY_TYPE} ${dobby.HEADER_FILE_LIST} ${dobby.SOURCE_FILE_LIST} ${logging.SOURCE_FILE_LIST} ${misc_helper.SOURCE_FILE_LIST} ${dobby.plugin.SOURCE_FILE_LIST})
set_target_properties(DobbyX
PROPERTIES
LINK_FLAGS "${linker_flags}"
COMPILE_FLAGS "${compiler_flags}"
)
# set framework property
set_target_properties(DobbyX PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER "com.dobby.dobby"
# MACOSX_FRAMEWORK_INFO_PLIST Info.plist
VERSION 1.0.0 # current version
SOVERSION 1.0.0 # compatibility version
PUBLIC_HEADER include/dobby.h
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Development"
)
if ((SYSTEM.Darwin AND BUILDING_PLUGIN) AND (NOT DOBBY_BUILD_KERNEL_MODE))
add_subdirectory(builtin-plugin/Dyld2HideLibrary)
add_subdirectory(builtin-plugin/ObjcRuntimeHook)
if (PROCESSOR.AARCH64)
add_subdirectory(builtin-plugin/SupervisorCallMonitor)
endif ()
endif()

View File

@ -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()

View File

@ -0,0 +1,89 @@
# Build
## CMake build options
```
option(DOBBY_GENERATE_SHARED "Build shared library" ON)
option(DOBBY_DEBUG "Enable debug logging" OFF)
option(NearBranch "Enable near branch trampoline" ON)
option(FullFloatingPointRegisterPack "Save and pack all floating-point registers" OFF)
option(Plugin.SymbolResolver "Enable symbol resolver" ON)
option(Plugin.ImportTableReplace "Enable import table replace " OFF)
option(Plugin.Android.BionicLinkerUtil "Enable android bionic linker util" OFF)
option(DOBBY_BUILD_EXAMPLE "Build example" OFF)
option(DOBBY_BUILD_TEST "Build test" OFF)
```
## Build with `scripts/platform_builder.py`
#### Build for iphoneos
```shell
python3 scripts/platform_builder.py --platform=iphoneos --arch=all
```
#### Build for macos
```
python3 scripts/platform_builder.py --platform=macos --arch=all
```
#### Build for linux
```
# prepare and download cmake/llvm
sh scripts/setup_linux_cross_compile.sh
python3 scripts/platform_builder.py --platform=linux --arch=all --cmake_dir=$HOME/opt/cmake-3.25.2 --llvm_dir=$HOME/opt/llvm-15.0.6
```
#### Build for android
```
# prepare and download cmake/llvm/ndk
sh scripts/setup_linux_cross_compile.sh
python3 scripts/platform_builder.py --platform=android --arch=all --cmake_dir=$HOME/opt/cmake-3.25.2 --llvm_dir=$HOME/opt/llvm-15.0.6 --android_ndk_dir=$HOME/opt/ndk-r25b
```
## Build with CMake
#### Build for host
```shell
cd Dobby && mkdir cmake-build-host && cd cmake-build-host
cmake ..
make -j4
```
## Build with Android Studio CMake
```
if(NOT TARGET dobby)
set(DOBBY_DIR /Users/jmpews/Workspace/Project.wrk/Dobby)
macro(SET_OPTION option value)
set(${option} ${value} CACHE INTERNAL "" FORCE)
endmacro()
SET_OPTION(DOBBY_DEBUG OFF)
SET_OPTION(DOBBY_GENERATE_SHARED OFF)
add_subdirectory(${DOBBY_DIR} dobby)
get_property(DOBBY_INCLUDE_DIRECTORIES
TARGET dobby
PROPERTY INCLUDE_DIRECTORIES)
include_directories(
.
${DOBBY_INCLUDE_DIRECTORIES}
$<TARGET_PROPERTY:dobby,INCLUDE_DIRECTORIES>
)
endif()
add_library(native-lib SHARED
${DOBBY_DIR}/examples/socket_example.cc
native-lib.cpp)
```

View File

@ -0,0 +1,18 @@
add_executable(socket_example
main.cc
socket_example.cc
)
target_link_libraries(socket_example
dobby
logging
)
add_library(socket_example_lib SHARED
socket_example.cc
)
target_link_libraries(socket_example_lib
dobby
)

View File

@ -0,0 +1,14 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <iostream>
int main(int argc, char const *argv[]) {
std::cout << "Start..." << std::endl;
sleep(100);
return 0;
}

View File

@ -0,0 +1,212 @@
#include "dobby.h"
#include "logging/logging.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <iostream>
#include <map>
#include <vector>
std::map<void *, const char *> *func_map;
// clang-format off
const char *func_array[] = {
// "__loader_dlopen",
"dlsym",
"dlclose",
"open",
"write",
"read",
"close",
"socket",
"connect",
"bind",
"listen",
"accept",
"send",
"recv",
// "pthread_create"
};
const char *func_short_array[] = {
"accept",
};
// clang-format on
#define pac_strip(symbol)
#if defined(__APPLE__) && __arm64e__
#if __has_feature(ptrauth_calls)
#define pac_strip(symbol)
//#define pac_strip(symbol) *(void **)&symbol = (void *)ptrauth_sign_unauthenticated((void *)symbol, ptrauth_key_asia, 0)
#endif
#endif
#define install_hook(name, fn_ret_t, fn_args_t...) \
fn_ret_t (*orig_##name)(fn_args_t); \
fn_ret_t fake_##name(fn_args_t); \
/* __attribute__((constructor)) */ static void install_hook_##name() { \
void *sym_addr = DobbySymbolResolver(NULL, #name); \
DobbyHook(sym_addr, (dobby_dummy_func_t)fake_##name, (dobby_dummy_func_t *)&orig_##name); \
pac_strip(orig_##name); \
printf("install hook %s:%p:%p\n", #name, sym_addr, orig_##name); \
} \
fn_ret_t fake_##name(fn_args_t)
install_hook(pthread_create, int, pthread_t *thread, const pthread_attr_t *attrs, void *(*start_routine)(void *),
void *arg, unsigned int create_flags) {
INFO_LOG("pthread_create: %p", start_routine);
return orig_pthread_create(thread, attrs, start_routine, arg, create_flags);
}
void common_handler(void *address, DobbyRegisterContext *ctx) {
auto iter = func_map->find(address);
if (iter != func_map->end()) {
INFO_LOG("func %s:%p invoke", iter->second, iter->first);
}
}
uint64_t socket_demo_server(void *ctx);
uint64_t socket_demo_client(void *ctx);
#if 1
__attribute__((constructor)) static void ctor() {
logger_set_options(0, 0, 0, LOG_LEVEL_DEBUG, false, false);
void *func = NULL;
func_map = new std::map<void *, const char *>();
for (int i = 0; i < sizeof(func_array) / sizeof(char *); ++i) {
func = DobbySymbolResolver(NULL, func_array[i]);
if (func == NULL) {
INFO_LOG("func %s not resolve", func_array[i]);
continue;
}
func_map->insert(std::pair<void *, const char *>(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 <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 49494
uint64_t socket_demo_server(void *ctx) {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
char *hello = "Hello from server";
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
ERROR_LOG("socket failed: %s", strerror(errno));
return -1;
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
ERROR_LOG("setsockopt: %s", strerror(errno));
return -1;
}
address.sin_family = AF_INET;
address.sin_port = htons(PORT);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
ERROR_LOG("bind failed: %s", strerror(errno));
return -1;
}
if (listen(server_fd, 3) < 0) {
ERROR_LOG("listen failed: %s", strerror(errno));
return -1;
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
ERROR_LOG("accept failed: %s", strerror(errno));
return -1;
}
int ret = recv(new_socket, buffer, 1024, 0);
INFO_LOG("[server] %s", buffer);
send(new_socket, hello, strlen(hello), 0);
INFO_LOG("[server] Hello message sent");
return 0;
}
uint64_t socket_demo_client(void *ctx) {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
ERROR_LOG("socket failed");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 and IPv6 addresses from text to binary form
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
ERROR_LOG("inet_pton failed");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
ERROR_LOG("connect failed");
return -1;
}
send(sock, hello, strlen(hello), 0);
INFO_LOG("[client] Hello message sent");
int ret = recv(sock, buffer, 1024, 0);
INFO_LOG("[client] %s", buffer);
return 0;
}
#endif

View File

@ -0,0 +1 @@
ref: https://github.com/mendsley/tinystl

View File

@ -0,0 +1 @@
#pragma once

View File

@ -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 <TINYSTL/stddef.h>
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

View File

@ -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 <TINYSTL/allocator.h>
#include <TINYSTL/new.h>
#include <TINYSTL/traits.h>
namespace tinystl {
template<typename T, typename Alloc = TINYSTL_ALLOCATOR>
struct buffer {
T* first;
T* last;
T* capacity;
};
template<typename T>
static inline void buffer_destroy_range_traits(T* first, T* last, pod_traits<T, false>) {
for (; first < last; ++first)
first->~T();
}
template<typename T>
static inline void buffer_destroy_range_traits(T*, T*, pod_traits<T, true>) {
}
template<typename T>
static inline void buffer_destroy_range(T* first, T* last) {
buffer_destroy_range_traits(first, last, pod_traits<T>());
}
template<typename T>
static inline void buffer_fill_urange_traits(T* first, T* last, pod_traits<T, false>) {
for (; first < last; ++first)
new(placeholder(), first) T();
}
template<typename T>
static inline void buffer_fill_urange_traits(T* first, T* last, pod_traits<T, true>) {
for (; first < last; ++first)
*first = T();
}
template<typename T>
static inline void buffer_fill_urange_traits(T* first, T* last, const T& value, pod_traits<T, false>) {
for (; first < last; ++first)
new(placeholder(), first) T(value);
}
template<typename T>
static inline void buffer_fill_urange_traits(T* first, T* last, const T& value, pod_traits<T, true>) {
for (; first < last; ++first)
*first = value;
}
template<typename T>
static inline void buffer_move_urange_traits(T* dest, T* first, T* last, pod_traits<T, false>) {
for (T* it = first; it != last; ++it, ++dest)
move_construct(dest, *it);
buffer_destroy_range(first, last);
}
template<typename T>
static inline void buffer_move_urange_traits(T* dest, T* first, T* last, pod_traits<T, true>) {
for (; first != last; ++first, ++dest)
*dest = *first;
}
template<typename T>
static inline void buffer_bmove_urange_traits(T* dest, T* first, T* last, pod_traits<T, false>) {
dest += (last - first);
for (T* it = last; it != first; --it, --dest) {
move_construct(dest - 1, *(it - 1));
buffer_destroy_range(it - 1, it);
}
}
template<typename T>
static inline void buffer_bmove_urange_traits(T* dest, T* first, T* last, pod_traits<T, true>) {
dest += (last - first);
for (T* it = last; it != first; --it, --dest)
*(dest - 1) = *(it - 1);
}
template<typename T>
static inline void buffer_move_urange(T* dest, T* first, T* last) {
buffer_move_urange_traits(dest, first, last, pod_traits<T>());
}
template<typename T>
static inline void buffer_bmove_urange(T* dest, T* first, T* last) {
buffer_bmove_urange_traits(dest, first, last, pod_traits<T>());
}
template<typename T>
static inline void buffer_fill_urange(T* first, T* last) {
buffer_fill_urange_traits(first, last, pod_traits<T>());
}
template<typename T>
static inline void buffer_fill_urange(T* first, T* last, const T& value) {
buffer_fill_urange_traits(first, last, value, pod_traits<T>());
}
template<typename T, typename Alloc>
static inline void buffer_init(buffer<T, Alloc>* b) {
b->first = b->last = b->capacity = 0;
}
template<typename T, typename Alloc>
static inline void buffer_destroy(buffer<T, Alloc>* b) {
buffer_destroy_range(b->first, b->last);
Alloc::static_deallocate(b->first, (size_t)((char*)b->capacity - (char*)b->first));
}
template<typename T, typename Alloc>
static inline void buffer_reserve(buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline void buffer_resize(buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline void buffer_resize(buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline void buffer_shrink_to_fit(buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline void buffer_clear(buffer<T, Alloc>* b) {
buffer_destroy_range(b->first, b->last);
b->last = b->first;
}
template<typename T, typename Alloc>
static inline T* buffer_insert_common(buffer<T, Alloc>* 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<typename T, typename Alloc, typename Param>
static inline void buffer_insert(buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline void buffer_insert(buffer<T, Alloc>* 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<typename T, typename Alloc, typename Param>
static inline void buffer_append(buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline void buffer_append(buffer<T, Alloc>* b) {
if (b->capacity != b->last) {
new(placeholder(), b->last) T();
++b->last;
} else {
buffer_insert(b, b->last, 1);
}
}
template<typename T, typename Alloc>
static inline T* buffer_erase(buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline T* buffer_erase_unordered(buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline void buffer_swap(buffer<T, Alloc>* b, buffer<T, Alloc>* 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<typename T, typename Alloc>
static inline void buffer_move(buffer<T, Alloc>* dst, buffer<T, Alloc>* src) {
dst->first = src->first, dst->last = src->last, dst->capacity = src->capacity;
src->first = src->last = src->capacity = nullptr;
}
}
#endif //TINYSTL_BUFFER_H

View File

@ -0,0 +1,48 @@
#pragma once
namespace tinystl {
template <typename T> class function;
template <typename Return, typename... Args> class function<Return(Args...)> {
public:
function() {
}
template <typename T> function(T functor) {
m_func = [](const void *user, Args... args) -> Return {
const T &func = *static_cast<const T *>(user);
return func(static_cast<Args &&>(args)...);
};
m_dtor = [](void *user) {
T &func = *static_cast<T *>(user);
func.~T();
};
new (tinystl::placeholder(), m_storage) T(static_cast<T &&>(functor));
}
~function() {
if (m_dtor)
m_dtor(m_storage);
}
Return operator()(Args... args) const {
return m_func(m_storage, static_cast<Args &&>(args)...);
}
explicit operator bool() {
return m_func != nullptr;
}
using Func = Return (*)(const void *, Args...);
Func m_func = nullptr;
using Dtor = void (*)(void *);
Dtor m_dtor = nullptr;
union {
void *m_storage[8];
};
};
} // namespace tinystl

View File

@ -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 <TINYSTL/stddef.h>
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<typename T>
inline size_t hash(const T& value) {
const size_t asint = (size_t)value;
return hash_string((const char*)&asint, sizeof(asint));
}
}
#endif

View File

@ -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 <TINYSTL/stddef.h>
#include <TINYSTL/traits.h>
namespace tinystl {
template<typename Key, typename Value>
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<typename Key, typename Value>
inline pair<Key, Value>::pair() {
}
template<typename Key, typename Value>
inline pair<Key, Value>::pair(const pair& other)
: first(other.first)
, second(other.second)
{
}
template<typename Key, typename Value>
inline pair<Key, Value>::pair(pair&& other)
: first(static_cast<Key&&>(other.first))
, second(static_cast<Value&&>(other.second))
{
}
template<typename Key, typename Value>
inline pair<Key, Value>::pair(const Key& key, const Value& value)
: first(key)
, second(value)
{
}
template<typename Key, typename Value>
inline pair<Key, Value>::pair(Key&& key, Value&& value)
: first(static_cast<Key&&>(key))
, second(static_cast<Value&&>(value))
{
}
template<typename Key, typename Value>
inline pair<Key, Value>& pair<Key, Value>::operator=(const pair& other) {
first = other.first;
second = other.second;
return *this;
}
template<typename Key, typename Value>
inline pair<Key, Value>& pair<Key, Value>::operator=(pair&& other) {
first = static_cast<Key&&>(other.first);
second = static_cast<Value&&>(other.second);
return *this;
}
template<typename Key, typename Value>
static inline pair<typename remove_reference<Key>::type, typename remove_reference<Value>::type>
make_pair(Key&& key, Value&& value) {
return pair<typename remove_reference<Key>::type, typename remove_reference<Value>::type>(
static_cast<Key&&>(key)
, static_cast<Value&&>(value)
);
}
template<typename Key, typename Value>
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<typename Key, typename Value>
inline unordered_hash_node<Key, Value>::unordered_hash_node(const Key& key, const Value& value)
: first(key)
, second(value)
{
}
template<typename Key, typename Value>
inline unordered_hash_node<Key, Value>::unordered_hash_node(Key&& key, Value&& value)
: first(static_cast<Key&&>(key))
, second(static_cast<Value&&>(value))
{
}
template <typename Key>
struct unordered_hash_node<Key, void> {
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<typename Key>
inline unordered_hash_node<Key, void>::unordered_hash_node(const Key& key)
: first(key)
{
}
template<typename Key>
inline unordered_hash_node<Key, void>::unordered_hash_node(Key&& key)
: first(static_cast<Key&&>(key))
{
}
template<typename Key, typename Value>
static inline void unordered_hash_node_insert(unordered_hash_node<Key, Value>* node, size_t hash, unordered_hash_node<Key, Value>** buckets, size_t nbuckets) {
size_t bucket = hash & (nbuckets - 1);
unordered_hash_node<Key, Value>* 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<Key, Value>* 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<typename Key, typename Value>
static inline void unordered_hash_node_erase(const unordered_hash_node<Key, Value>* where, size_t hash, unordered_hash_node<Key, Value>** buckets, size_t nbuckets) {
size_t bucket = hash & (nbuckets - 1);
unordered_hash_node<Key, Value>* 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<typename Node>
struct unordered_hash_iterator {
Node* operator->() const;
Node& operator*() const;
Node* node;
};
template<typename Node>
struct unordered_hash_iterator<const Node> {
unordered_hash_iterator() {}
unordered_hash_iterator(unordered_hash_iterator<Node> other)
: node(other.node)
{
}
const Node* operator->() const;
const Node& operator*() const;
const Node* node;
};
template<typename Key>
struct unordered_hash_iterator<const unordered_hash_node<Key, void> > {
const Key* operator->() const;
const Key& operator*() const;
unordered_hash_node<Key, void>* node;
};
template<typename LNode, typename RNode>
static inline bool operator==(const unordered_hash_iterator<LNode>& lhs, const unordered_hash_iterator<RNode>& rhs) {
return lhs.node == rhs.node;
}
template<typename LNode, typename RNode>
static inline bool operator!=(const unordered_hash_iterator<LNode>& lhs, const unordered_hash_iterator<RNode>& rhs) {
return lhs.node != rhs.node;
}
template<typename Node>
static inline void operator++(unordered_hash_iterator<Node>& lhs) {
lhs.node = lhs.node->next;
}
template<typename Node>
inline Node* unordered_hash_iterator<Node>::operator->() const {
return node;
}
template<typename Node>
inline Node& unordered_hash_iterator<Node>::operator*() const {
return *node;
}
template<typename Node>
inline const Node* unordered_hash_iterator<const Node>::operator->() const {
return node;
}
template<typename Node>
inline const Node& unordered_hash_iterator<const Node>::operator*() const {
return *node;
}
template<typename Key>
inline const Key* unordered_hash_iterator<const unordered_hash_node<Key, void> >::operator->() const {
return &node->first;
}
template<typename Key>
inline const Key& unordered_hash_iterator<const unordered_hash_node<Key, void> >::operator*() const {
return node->first;
}
template<typename Node, typename Key>
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

View File

@ -0,0 +1,44 @@
/*-
* Copyright 2012-2018 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TINYSTL_NEW_H
#define TINYSTL_NEW_H
#include <TINYSTL/stddef.h>
namespace tinystl {
struct placeholder {};
} // namespace tinystl
inline void *operator new(size_t, tinystl::placeholder, void *ptr) {
return ptr;
}
inline void operator delete(void *, tinystl::placeholder, void *) throw() {
}
#endif

View File

@ -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 <stddef.h>
#endif
#endif

View File

@ -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 <TINYSTL/allocator.h>
#include <TINYSTL/stddef.h>
#include <TINYSTL/hash.h>
namespace tinystl {
template<typename Allocator>
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<typename allocator>
inline basic_string<allocator>::basic_string()
: m_first(m_buffer)
, m_last(m_buffer)
, m_capacity(m_buffer + c_nbuffer)
{
resize(0);
}
template<typename allocator>
inline basic_string<allocator>::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<typename allocator>
inline basic_string<allocator>::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<typename allocator>
inline basic_string<allocator>::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<typename allocator>
inline basic_string<allocator>::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<typename allocator>
inline basic_string<allocator>::~basic_string() {
if (m_first != m_buffer)
allocator::static_deallocate(m_first, m_capacity - m_first);
}
template<typename allocator>
inline basic_string<allocator>& basic_string<allocator>::operator=(const basic_string& other) {
basic_string(other).swap(*this);
return *this;
}
template<typename allocator>
inline basic_string<allocator>& basic_string<allocator>::operator=(basic_string&& other) {
basic_string(static_cast<basic_string&&>(other)).swap(*this);
return *this;
}
template<typename allocator>
inline const char* basic_string<allocator>::c_str() const {
return m_first;
}
template<typename allocator>
inline size_t basic_string<allocator>::size() const
{
return (size_t)(m_last - m_first);
}
template<typename allocator>
inline void basic_string<allocator>::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<typename allocator>
inline void basic_string<allocator>::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<typename allocator>
inline void basic_string<allocator>::clear() {
resize(0);
}
template<typename allocator>
inline void basic_string<allocator>::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<typename allocator>
inline void basic_string<allocator>::assign(const char* sz, size_t n) {
clear();
append(sz, sz+n);
}
template<typename allocator>
inline void basic_string<allocator>::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<typename allocator>
inline void basic_string<allocator>::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<typename allocatorl, typename allocatorr>
inline bool operator==(const basic_string<allocatorl>& lhs, const basic_string<allocatorr>& 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<typename allocator>
static inline size_t hash(const basic_string<allocator>& value) {
return hash_string(value.c_str(), value.size());
}
typedef basic_string<TINYSTL_ALLOCATOR> string;
}
#endif

View File

@ -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 <TINYSTL/stddef.h>
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

View File

@ -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 <TINYSTL/new.h>
#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<typename T, bool pod = TINYSTL_TRY_POD_OPTIMIZATION(T)> struct pod_traits {};
template<typename T, T t> struct swap_holder;
template<typename T>
static inline void move_impl(T& a, T& b, ...) {
a = b;
}
template<typename T>
static inline void move_impl(T& a, T& b, T*, swap_holder<void (T::*)(T&), &T::swap>* = 0) {
a.swap(b);
}
template<typename T>
static inline void move(T& a, T&b) {
move_impl(a, b, (T*)0);
}
template<typename T>
static inline void move_construct_impl(T* a, T& b, ...) {
new(placeholder(), a) T(b);
}
template<typename T>
static inline void move_construct_impl(T* a, T& b, void*, swap_holder<void (T::*)(T&), &T::swap>* = 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<typename T>
static inline void move_construct_impl(T* a, T& b, T*, typename T::tinystl_nomove_construct* = 0) {
new(placeholder(), a) T(b);
}
template<typename T>
static inline void move_construct(T* a, T& b) {
move_construct_impl(a, b, (T*)0);
}
template<typename T>
struct remove_reference {
typedef T type;
};
template<typename T>
struct remove_reference<T&> {
typedef T type;
};
template<typename T>
struct remove_reference<T&&> {
typedef T type;
};
}
#endif

View File

@ -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 <TINYSTL/allocator.h>
#include <TINYSTL/buffer.h>
#include <TINYSTL/hash.h>
#include <TINYSTL/hash_base.h>
namespace tinystl {
template<typename Key, typename Value, typename Alloc = TINYSTL_ALLOCATOR>
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<Key, Value> value_type;
typedef unordered_hash_iterator<const unordered_hash_node<Key, Value> > const_iterator;
typedef unordered_hash_iterator<unordered_hash_node<Key, Value> > 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<iterator, bool> insert(const pair<Key, Value>& p);
pair<iterator, bool> emplace(pair<Key, Value>&& 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<Key, Value>* pointer;
size_t m_size;
tinystl::buffer<pointer, Alloc> m_buckets;
};
template<typename Key, typename Value, typename Alloc>
inline unordered_map<Key, Value, Alloc>::unordered_map()
: m_size(0)
{
buffer_init<pointer, Alloc>(&m_buckets);
buffer_resize<pointer, Alloc>(&m_buckets, 9, 0);
}
template<typename Key, typename Value, typename Alloc>
inline unordered_map<Key, Value, Alloc>::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<pointer, Alloc>(&m_buckets);
buffer_resize<pointer, Alloc>(&m_buckets, nbuckets, 0);
for (pointer it = *other.m_buckets.first; it; it = it->next) {
unordered_hash_node<Key, Value>* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node<Key, Value>))) unordered_hash_node<Key, Value>(it->first, it->second);
newnode->next = newnode->prev = 0;
unordered_hash_node_insert(newnode, hash(it->first), m_buckets.first, nbuckets - 1);
}
}
template<typename Key, typename Value, typename Alloc>
inline unordered_map<Key, Value, Alloc>::unordered_map(unordered_map&& other)
: m_size(other.m_size)
{
buffer_move(&m_buckets, &other.m_buckets);
other.m_size = 0;
}
template<typename Key, typename Value, typename Alloc>
inline unordered_map<Key, Value, Alloc>::~unordered_map() {
if (m_buckets.first != m_buckets.last)
clear();
buffer_destroy<pointer, Alloc>(&m_buckets);
}
template<typename Key, typename Value, typename Alloc>
inline unordered_map<Key, Value, Alloc>& unordered_map<Key, Value, Alloc>::operator=(const unordered_map<Key, Value, Alloc>& other) {
unordered_map<Key, Value, Alloc>(other).swap(*this);
return *this;
}
template<typename Key, typename Value, typename Alloc>
inline unordered_map<Key, Value, Alloc>& unordered_map<Key, Value, Alloc>::operator=(unordered_map&& other) {
unordered_map(static_cast<unordered_map&&>(other)).swap(*this);
return *this;
}
template<typename Key, typename Value, typename Alloc>
inline typename unordered_map<Key, Value, Alloc>::iterator unordered_map<Key, Value, Alloc>::begin() {
iterator it;
it.node = *m_buckets.first;
return it;
}
template<typename Key, typename Value, typename Alloc>
inline typename unordered_map<Key, Value, Alloc>::iterator unordered_map<Key, Value, Alloc>::end() {
iterator it;
it.node = 0;
return it;
}
template<typename Key, typename Value, typename Alloc>
inline typename unordered_map<Key, Value, Alloc>::const_iterator unordered_map<Key, Value, Alloc>::begin() const {
const_iterator cit;
cit.node = *m_buckets.first;
return cit;
}
template<typename Key, typename Value, typename Alloc>
inline typename unordered_map<Key, Value, Alloc>::const_iterator unordered_map<Key, Value, Alloc>::end() const {
const_iterator cit;
cit.node = 0;
return cit;
}
template<typename Key, typename Value, typename Alloc>
inline bool unordered_map<Key, Value, Alloc>::empty() const {
return m_size == 0;
}
template<typename Key, typename Value, typename Alloc>
inline size_t unordered_map<Key, Value, Alloc>::size() const {
return m_size;
}
template<typename Key, typename Value, typename Alloc>
inline void unordered_map<Key, Value, Alloc>::clear() {
pointer it = *m_buckets.first;
while (it) {
const pointer next = it->next;
it->~unordered_hash_node<Key, Value>();
Alloc::static_deallocate(it, sizeof(unordered_hash_node<Key, Value>));
it = next;
}
m_buckets.last = m_buckets.first;
buffer_resize<pointer, Alloc>(&m_buckets, 9, 0);
m_size = 0;
}
template<typename Key, typename Value, typename Alloc>
inline typename unordered_map<Key, Value, Alloc>::iterator unordered_map<Key, Value, Alloc>::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<typename Key, typename Value, typename Alloc>
inline typename unordered_map<Key, Value, Alloc>::const_iterator unordered_map<Key, Value, Alloc>::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<typename Key, typename Value, typename Alloc>
inline void unordered_map<Key, Value, Alloc>::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<pointer, Alloc>(&m_buckets, newnbuckets + 1, 0);
unordered_hash_node<Key, Value>** 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<typename Key, typename Value, typename Alloc>
inline pair<typename unordered_map<Key, Value, Alloc>::iterator, bool> unordered_map<Key, Value, Alloc>::insert(const pair<Key, Value>& p) {
pair<iterator, bool> result;
result.second = false;
result.first = find(p.first);
if (result.first.node != 0)
return result;
unordered_hash_node<Key, Value>* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node<Key, Value>))) unordered_hash_node<Key, Value>(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<typename Key, typename Value, typename Alloc>
inline pair<typename unordered_map<Key, Value, Alloc>::iterator, bool> unordered_map<Key, Value, Alloc>::emplace(pair<Key, Value>&& p) {
pair<iterator, bool> 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<Key, Value>* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node<Key, Value>))) unordered_hash_node<Key, Value>(static_cast<Key&&>(p.first), static_cast<Value&&>(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<typename Key, typename Value, typename Alloc>
inline void unordered_map<Key, Value, Alloc>::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<Key, Value>();
Alloc::static_deallocate((void*)where.node, sizeof(unordered_hash_node<Key, Value>));
--m_size;
}
template<typename Key, typename Value, typename Alloc>
inline Value& unordered_map<Key, Value, Alloc>::operator[](const Key& key) {
return insert(pair<Key, Value>(key, Value())).first->second;
}
template<typename Key, typename Value, typename Alloc>
inline void unordered_map<Key, Value, Alloc>::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

View File

@ -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 <TINYSTL/allocator.h>
#include <TINYSTL/buffer.h>
#include <TINYSTL/hash.h>
#include <TINYSTL/hash_base.h>
namespace tinystl {
template<typename Key, typename Alloc = TINYSTL_ALLOCATOR>
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 unordered_hash_node<Key, void> > 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<iterator, bool> insert(const Key& key);
pair<iterator, bool> 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<Key, void>* pointer;
size_t m_size;
tinystl::buffer<pointer, Alloc> m_buckets;
};
template<typename Key, typename Alloc>
inline unordered_set<Key, Alloc>::unordered_set()
: m_size(0)
{
buffer_init<pointer, Alloc>(&m_buckets);
buffer_resize<pointer, Alloc>(&m_buckets, 9, 0);
}
template<typename Key, typename Alloc>
inline unordered_set<Key, Alloc>::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<pointer, Alloc>(&m_buckets);
buffer_resize<pointer, Alloc>(&m_buckets, nbuckets, 0);
for (pointer it = *other.m_buckets.first; it; it = it->next) {
unordered_hash_node<Key, void>* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node<Key, void>))) unordered_hash_node<Key, void>(*it);
newnode->next = newnode->prev = 0;
unordered_hash_node_insert(newnode, hash(it->first), m_buckets.first, nbuckets - 1);
}
}
template<typename Key, typename Alloc>
inline unordered_set<Key, Alloc>::unordered_set(unordered_set&& other)
: m_size(other.m_size)
{
buffer_move(&m_buckets, &other.m_buckets);
other.m_size = 0;
}
template<typename Key, typename Alloc>
inline unordered_set<Key, Alloc>::~unordered_set() {
if (m_buckets.first != m_buckets.last)
clear();
buffer_destroy<pointer, Alloc>(&m_buckets);
}
template<typename Key, typename Alloc>
inline unordered_set<Key, Alloc>& unordered_set<Key, Alloc>::operator=(const unordered_set<Key, Alloc>& other) {
unordered_set<Key, Alloc>(other).swap(*this);
return *this;
}
template<typename Key, typename Alloc>
inline unordered_set<Key, Alloc>& unordered_set<Key, Alloc>::operator=(unordered_set&& other) {
unordered_set(static_cast<unordered_set&&>(other)).swap(*this);
return *this;
}
template<typename Key, typename Alloc>
inline typename unordered_set<Key, Alloc>::iterator unordered_set<Key, Alloc>::begin() const {
iterator cit;
cit.node = *m_buckets.first;
return cit;
}
template<typename Key, typename Alloc>
inline typename unordered_set<Key, Alloc>::iterator unordered_set<Key, Alloc>::end() const {
iterator cit;
cit.node = 0;
return cit;
}
template<typename Key, typename Alloc>
inline bool unordered_set<Key, Alloc>::empty() const {
return m_size == 0;
}
template<typename Key, typename Alloc>
inline size_t unordered_set<Key, Alloc>::size() const {
return m_size;
}
template<typename Key, typename Alloc>
inline void unordered_set<Key, Alloc>::clear() {
pointer it = *m_buckets.first;
while (it) {
const pointer next = it->next;
it->~unordered_hash_node<Key, void>();
Alloc::static_deallocate(it, sizeof(unordered_hash_node<Key, void>));
it = next;
}
m_buckets.last = m_buckets.first;
buffer_resize<pointer, Alloc>(&m_buckets, 9, 0);
m_size = 0;
}
template<typename Key, typename Alloc>
inline typename unordered_set<Key, Alloc>::iterator unordered_set<Key, Alloc>::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<typename Key, typename Alloc>
inline void unordered_set<Key, Alloc>::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<pointer, Alloc>(&m_buckets, newnbuckets + 1, 0);
unordered_hash_node<Key, void>** 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<typename Key, typename Alloc>
inline pair<typename unordered_set<Key, Alloc>::iterator, bool> unordered_set<Key, Alloc>::insert(const Key& key) {
pair<iterator, bool> result;
result.second = false;
result.first = find(key);
if (result.first.node != 0)
return result;
unordered_hash_node<Key, void>* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node<Key, void>))) unordered_hash_node<Key, void>(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<typename Key, typename Alloc>
inline pair<typename unordered_set<Key, Alloc>::iterator, bool> unordered_set<Key, Alloc>::emplace(Key&& key) {
pair<iterator, bool> result;
result.second = false;
result.first = find(key);
if (result.first.node != 0)
return result;
const size_t keyhash = hash(key);
unordered_hash_node<Key, void>* newnode = new(placeholder(), Alloc::static_allocate(sizeof(unordered_hash_node<Key, void>))) unordered_hash_node<Key, void>(static_cast<Key&&>(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<typename Key, typename Alloc>
inline void unordered_set<Key, Alloc>::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<Key, void>();
Alloc::static_deallocate((void*)where.node, sizeof(unordered_hash_node<Key, void>));
--m_size;
}
template<typename Key, typename Alloc>
inline size_t unordered_set<Key, Alloc>::erase(const Key& key) {
const iterator it = find(key);
if (it.node == 0)
return 0;
erase(it);
return 1;
}
template <typename Key, typename Alloc>
void unordered_set<Key, Alloc>::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

View File

@ -0,0 +1,353 @@
/*-
* Copyright 2012-2018 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TINYSTL_VECTOR_H
#define TINYSTL_VECTOR_H
#include <TINYSTL/allocator.h>
#include <TINYSTL/buffer.h>
#include <TINYSTL/new.h>
#include <TINYSTL/stddef.h>
namespace tinystl {
template <typename T, typename Alloc = TINYSTL_ALLOCATOR> class vector {
using iterator = T *;
public:
vector();
vector(const vector &other);
vector(vector &&other);
vector(size_t size);
vector(size_t size, const T &value);
vector(const T *first, const T *last);
~vector();
vector &operator=(const vector &other);
vector &operator=(vector &&other);
void assign(const T *first, const T *last);
const T *data() const;
T *data();
size_t size() const;
size_t capacity() const;
bool empty() const;
T &operator[](size_t idx);
const T &operator[](size_t idx) const;
const T &front() const;
T &front();
const T &back() const;
T &back();
void resize(size_t size);
void resize(size_t size, const T &value);
void clear();
void reserve(size_t capacity);
void push_back(const T &t);
void pop_back();
void emplace_back();
template <typename Param> void emplace_back(const Param &param);
void shrink_to_fit();
void swap(vector &other);
typedef T value_type;
iterator begin();
iterator end();
typedef const T *const_iterator;
const_iterator begin() const;
const_iterator end() const;
void insert(iterator where);
void insert(iterator where, const T &value);
void insert(iterator where, const T *first, const T *last);
template <typename Param> 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);
iterator find(const T &other) const;
void sort(int (*compare)(const T &elem0, const T &elem1));
void sort(unsigned begin, unsigned end, int (*compare)(const T &elem0, const T &elem1));
private:
int partition(int (*compare)(const T &elem0, const T &elem1), int p, int r);
void quick_sort(int (*compare)(const T &elem0, const T &elem1), int p, int r);
private:
buffer<T, Alloc> m_buffer;
};
template <typename T, typename Alloc> inline vector<T, Alloc>::vector() {
buffer_init(&m_buffer);
}
template <typename T, typename Alloc> inline vector<T, Alloc>::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 <typename T, typename Alloc> inline vector<T, Alloc>::vector(vector &&other) {
buffer_move(&m_buffer, &other.m_buffer);
}
template <typename T, typename Alloc> inline vector<T, Alloc>::vector(size_t size) {
buffer_init(&m_buffer);
buffer_resize(&m_buffer, size);
}
template <typename T, typename Alloc> inline vector<T, Alloc>::vector(size_t size, const T &value) {
buffer_init(&m_buffer);
buffer_resize(&m_buffer, size, value);
}
template <typename T, typename Alloc> inline vector<T, Alloc>::vector(const T *first, const T *last) {
buffer_init(&m_buffer);
buffer_insert(&m_buffer, m_buffer.last, first, last);
}
template <typename T, typename Alloc> inline vector<T, Alloc>::~vector() {
buffer_destroy(&m_buffer);
}
template <typename T, typename Alloc> inline vector<T, Alloc> &vector<T, Alloc>::operator=(const vector &other) {
vector(other).swap(*this);
return *this;
}
template <typename T, typename Alloc> vector<T, Alloc> &vector<T, Alloc>::operator=(vector &&other) {
buffer_destroy(&m_buffer);
buffer_move(&m_buffer, &other.m_buffer);
return *this;
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::assign(const T *first, const T *last) {
buffer_clear(&m_buffer);
buffer_insert(&m_buffer, m_buffer.last, first, last);
}
template <typename T, typename Alloc> inline const T *vector<T, Alloc>::data() const {
return m_buffer.first;
}
template <typename T, typename Alloc> inline T *vector<T, Alloc>::data() {
return m_buffer.first;
}
template <typename T, typename Alloc> inline size_t vector<T, Alloc>::size() const {
return (size_t)(m_buffer.last - m_buffer.first);
}
template <typename T, typename Alloc> inline size_t vector<T, Alloc>::capacity() const {
return (size_t)(m_buffer.capacity - m_buffer.first);
}
template <typename T, typename Alloc> inline bool vector<T, Alloc>::empty() const {
return m_buffer.last == m_buffer.first;
}
template <typename T, typename Alloc> inline T &vector<T, Alloc>::operator[](size_t idx) {
return m_buffer.first[idx];
}
template <typename T, typename Alloc> inline const T &vector<T, Alloc>::operator[](size_t idx) const {
return m_buffer.first[idx];
}
template <typename T, typename Alloc> inline const T &vector<T, Alloc>::front() const {
return m_buffer.first[0];
}
template <typename T, typename Alloc> inline T &vector<T, Alloc>::front() {
return m_buffer.first[0];
}
template <typename T, typename Alloc> inline const T &vector<T, Alloc>::back() const {
return m_buffer.last[-1];
}
template <typename T, typename Alloc> inline T &vector<T, Alloc>::back() {
return m_buffer.last[-1];
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::resize(size_t size) {
buffer_resize(&m_buffer, size);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::resize(size_t size, const T &value) {
buffer_resize(&m_buffer, size, value);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::clear() {
buffer_clear(&m_buffer);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::reserve(size_t capacity) {
buffer_reserve(&m_buffer, capacity);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::push_back(const T &t) {
buffer_append(&m_buffer, &t);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::emplace_back() {
buffer_append(&m_buffer);
}
template <typename T, typename Alloc>
template <typename Param>
inline void vector<T, Alloc>::emplace_back(const Param &param) {
buffer_append(&m_buffer, &param);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::pop_back() {
buffer_erase(&m_buffer, m_buffer.last - 1, m_buffer.last);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::shrink_to_fit() {
buffer_shrink_to_fit(&m_buffer);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::swap(vector &other) {
buffer_swap(&m_buffer, &other.m_buffer);
}
template <typename T, typename Alloc> inline typename vector<T, Alloc>::iterator vector<T, Alloc>::begin() {
return m_buffer.first;
}
template <typename T, typename Alloc> inline typename vector<T, Alloc>::iterator vector<T, Alloc>::end() {
return m_buffer.last;
}
template <typename T, typename Alloc> inline typename vector<T, Alloc>::const_iterator vector<T, Alloc>::begin() const {
return m_buffer.first;
}
template <typename T, typename Alloc> inline typename vector<T, Alloc>::const_iterator vector<T, Alloc>::end() const {
return m_buffer.last;
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::insert(typename vector::iterator where) {
buffer_insert(&m_buffer, where, 1);
}
template <typename T, typename Alloc> inline void vector<T, Alloc>::insert(iterator where, const T &value) {
buffer_insert(&m_buffer, where, &value, &value + 1);
}
template <typename T, typename Alloc>
inline void vector<T, Alloc>::insert(iterator where, const T *first, const T *last) {
buffer_insert(&m_buffer, where, first, last);
}
template <typename T, typename Alloc>
inline typename vector<T, Alloc>::iterator vector<T, Alloc>::erase(iterator where) {
return buffer_erase(&m_buffer, where, where + 1);
}
template <typename T, typename Alloc>
inline typename vector<T, Alloc>::iterator vector<T, Alloc>::erase(iterator first, iterator last) {
return buffer_erase(&m_buffer, first, last);
}
template <typename T, typename Alloc>
inline typename vector<T, Alloc>::iterator vector<T, Alloc>::erase_unordered(iterator where) {
return buffer_erase_unordered(&m_buffer, where, where + 1);
}
template <typename T, typename Alloc>
inline typename vector<T, Alloc>::iterator vector<T, Alloc>::erase_unordered(iterator first, iterator last) {
return buffer_erase_unordered(&m_buffer, first, last);
}
template <typename T, typename Alloc>
template <typename Param>
void vector<T, Alloc>::emplace(typename vector::iterator where, const Param &param) {
buffer_insert(&m_buffer, where, &param, &param + 1);
}
template <typename T, typename Alloc>
inline typename vector<T, Alloc>::iterator vector<T, Alloc>::find(const T &other) const {
for (unsigned i = 0; i < size(); ++i)
if (m_buffer.first[i] == other)
return &m_buffer.first[i];
return m_buffer.last;
}
template <typename T, typename Alloc>
inline int vector<T, Alloc>::partition(int (*compare)(const T &elem0, const T &elem1), int p, int r) {
T tmp, pivot = m_buffer.first[p];
int left = p;
for (int i = p + 1; i <= r; i++) {
if (compare(m_buffer.first[i], pivot) < 0) {
left++;
tmp = m_buffer.first[i];
m_buffer.first[i] = m_buffer.first[left];
m_buffer.first[left] = tmp;
}
}
tmp = m_buffer.first[p];
m_buffer.first[p] = m_buffer.first[left];
m_buffer.first[left] = tmp;
return left;
}
template <typename T, typename Alloc>
inline void vector<T, Alloc>::quick_sort(int (*compare)(const T &elem0, const T &elem1), int p, int r) {
if (p < r) {
int q = partition(compare, p, r);
quick_sort(compare, p, q - 1);
quick_sort(compare, q + 1, r);
}
}
template <typename T, typename Alloc>
inline void vector<T, Alloc>::sort(int (*compare)(const T &elem0, const T &elem1)) {
quick_sort(compare, 0, (int)size() - 1);
}
template <typename T, typename Alloc>
inline void vector<T, Alloc>::sort(unsigned begin, unsigned end, int (*compare)(const T &elem0, const T &elem1)) {
quick_sort(compare, (int)begin, (int)end);
}
} // namespace tinystl
#endif // TINYSTL_VECTOR_H

View File

@ -0,0 +1,18 @@
include_directories(.)
if(NOT DOBBY_BUILD_KERNEL_MODE)
set(SOURCE_FILE_LIST
${CMAKE_CURRENT_SOURCE_DIR}/variable_cache.c
${CMAKE_CURRENT_SOURCE_DIR}/async_logger.cc
${CMAKE_CURRENT_SOURCE_DIR}/format_printer.cc
)
else()
set(SOURCE_FILE_LIST
${CMAKE_CURRENT_SOURCE_DIR}/format_printer.cc
)
endif()
add_library(misc_helper
${SOURCE_FILE_LIST}
${SOURCE_HEADER_LIST}
)

View File

@ -0,0 +1,65 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#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);
}

View File

@ -0,0 +1,129 @@
#include "pthread_helper.h"
#include <stdio.h>
#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

View File

@ -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 <windows.h>
#include <process.h>
#include <errno.h>
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 <pthread.h>
#include <unistd.h>
#define Sleep(num) usleep(num * 1000)
#endif
#endif /* CROSS_THREAD_H */

View File

@ -0,0 +1,30 @@
#ifdef _WIN32
#include <io.h>
#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 <sys/types.h> */
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 <unistd.h>
#endif

View File

@ -0,0 +1,11 @@
#include "misc-helper/format_printer.h"
void hexdump(const uint8_t *bytes, size_t len) {
size_t ix;
for (ix = 0; ix < len; ++ix) {
if (ix != 0 && !(ix % 16))
LOG_FUNCTION_IMPL(0, "\n");
LOG_FUNCTION_IMPL(0, "%02X ", bytes[ix]);
}
LOG_FUNCTION_IMPL(0, "\n");
}

View File

@ -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

View File

@ -0,0 +1,3 @@
#include "dobby/common.h"
void hexdump(const uint8_t *bytes, size_t len);

View File

@ -0,0 +1,17 @@
#ifndef VARIABLE_CACHE_H
#define VARIABLE_CACHE_H
#include <stdint.h>
#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

View File

@ -0,0 +1,118 @@
#include "misc-helper/variable_cache.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "deprecated/unistd_helper.h"
#include <errno.h>
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;
}

View File

@ -0,0 +1,18 @@
include_directories(.)
set(SOURCE_FILE_LIST
logging.cc
)
if (DOBBY_BUILD_KERNEL_MODE)
set(SOURCE_FILE_LIST
logging_kern.cc
)
endif ()
get_absolute_path_list(SOURCE_FILE_LIST SOURCE_FILE_LIST_)
set(SOURCE_FILE_LIST ${SOURCE_FILE_LIST_})
add_library(logging
${SOURCE_FILE_LIST}
)

View File

@ -0,0 +1,137 @@
#include "logging/logging.h"
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#endif
#if defined(__APPLE__)
#include <dlfcn.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include "./priv_headers/_simple.h"
#endif
#if defined(_WIN32)
#define PUBLIC
#else
#define PUBLIC __attribute__((visibility("default")))
#define INTERNAL __attribute__((visibility("internal")))
#endif
#if defined(__ANDROID__)
#include <android/log.h>
#endif
#pragma clang diagnostic ignored "-Wformat"
Logger *Logger::g_logger = nullptr;
void Logger::logv(LogLevel level, const char *_fmt, va_list ap) {
if (level < log_level_)
return;
char fmt_buffer[4096] = {0};
if (log_tag_ != nullptr) {
snprintf(fmt_buffer + strlen(fmt_buffer), sizeof(fmt_buffer) - strlen(fmt_buffer), "%s ", log_tag_);
}
if (enable_time_tag_) {
time_t now = time(NULL);
struct tm *tm = localtime(&now);
snprintf(fmt_buffer + strlen(fmt_buffer), sizeof(fmt_buffer) - strlen(fmt_buffer), "%04d-%02d-%02d %02d:%02d:%02d ",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
}
snprintf(fmt_buffer + strlen(fmt_buffer), sizeof(fmt_buffer) - strlen(fmt_buffer), "%s\n", _fmt);
if (enable_syslog_) {
#if defined(__APPLE__)
extern void *_os_log_default;
static void (*os_log_with_args)(void *oslog, char type, const char *format, va_list args, void *ret_addr) = 0;
if (!os_log_with_args)
os_log_with_args = (__typeof(os_log_with_args))dlsym((void *)-2, "os_log_with_args");
// os_log_with_args(&_os_log_default, 0x10, fmt_buffer, ap, (void *)&os_log_with_args);
vsyslog(LOG_ALERT, fmt_buffer, ap);
static int _logDescriptor = 0;
if (_logDescriptor == 0) {
_logDescriptor = socket(AF_UNIX, SOCK_DGRAM, 0);
if (_logDescriptor != -1) {
fcntl(_logDescriptor, F_SETFD, FD_CLOEXEC);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, _PATH_LOG, sizeof(addr.sun_path));
if (connect(_logDescriptor, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
close(_logDescriptor);
_logDescriptor = -1;
ERROR_LOG("Failed to connect to syslogd: %s", strerror(errno));
}
}
}
if (_logDescriptor > 0) {
vdprintf(_logDescriptor, fmt_buffer, ap);
}
#elif defined(_POSIX_VERSION)
vsyslog(LOG_ERR, fmt_buffer, ap);
#endif
}
if (log_file_ != nullptr) {
char buffer[0x4000] = {0};
vsnprintf(buffer, sizeof(buffer) - 1, fmt_buffer, ap);
#if defined(USER_CXX_FILESTREAM)
log_file_stream_->write(buffer, strlen(buffer));
log_file_stream_->flush();
#else
fwrite(buffer, strlen(buffer), 1, log_file_stream_);
fflush(log_file_stream_);
#endif
}
if (1 || !enable_syslog_ && log_file_ == nullptr) {
#if defined(__ANDROID__)
__android_log_vprint(ANDROID_LOG_INFO, NULL, fmt_buffer, ap);
#else
vprintf(fmt_buffer, ap);
#endif
}
}
#pragma clang diagnostic warning "-Wformat"
void *logger_create(const char *tag, const char *file, LogLevel level, bool enable_time_tag, bool enable_syslog) {
Logger *logger = new Logger(tag, file, level, enable_time_tag, enable_syslog);
return logger;
}
void logger_set_options(void *logger, const char *tag, const char *file, LogLevel level, bool enable_time_tag,
bool enable_syslog) {
if (logger == nullptr) {
logger = Logger::Shared();
}
((Logger *)logger)->setOptions(tag, file, level, enable_time_tag, enable_syslog);
}
void logger_log_impl(void *logger, LogLevel level, const char *fmt, ...) {
if (logger == nullptr) {
logger = Logger::Shared();
}
va_list ap;
va_start(ap, fmt);
((Logger *)logger)->logv(level, fmt, ap);
va_end(ap);
}

View File

@ -0,0 +1,87 @@
#ifndef CHECK_LOGGING_H_
#define CHECK_LOGGING_H_
#include "logging.h"
#define CHECK_WITH_MSG(condition, message) \
do { \
if (!(condition)) { \
FATAL_LOG("Check failed: %s.\n", message); \
} \
} while (0)
#define CHECK(condition) CHECK_WITH_MSG(condition, #condition)
#ifdef LOGGING_DEBUG
#define DCHECK_WITH_MSG(condition, message) \
do { \
if (!(condition)) { \
FATAL_LOG("%s", message); \
} \
} while (0)
#define DCHECK(condition) DCHECK_WITH_MSG(condition, #condition)
// Helper macro for binary operators.
// Don't use this macro directly in your code, use CHECK_EQ et al below.
#define CHECK_OP(name, op, lhs, rhs) \
do { \
if (!(lhs op rhs)) { \
FATAL_LOG(" Check failed: %s.\n", #lhs " " #op " " #rhs); \
} \
} while (0)
#define DCHECK_OP(name, op, lhs, rhs) \
do { \
if (!((lhs)op(rhs))) { \
FATAL_LOG("%s", ""); \
} \
} while (0)
#else
// Make all CHECK functions discard their log strings to reduce code
// bloat for official release builds.
#define CHECK_OP(name, op, lhs, rhs) \
do { \
bool _cond = lhs op rhs; \
CHECK_WITH_MSG(_cond, #lhs " " #op " " #rhs "\n"); \
} while (0)
#define DCHECK_WITH_MSG(condition, msg) void(0);
#endif
#define CHECK_EQ(lhs, rhs) CHECK_OP(EQ, ==, lhs, rhs)
#define CHECK_NE(lhs, rhs) CHECK_OP(NE, !=, lhs, rhs)
#define CHECK_LE(lhs, rhs) CHECK_OP(LE, <=, lhs, rhs)
#define CHECK_LT(lhs, rhs) CHECK_OP(LT, <, lhs, rhs)
#define CHECK_GE(lhs, rhs) CHECK_OP(GE, >=, lhs, rhs)
#define CHECK_GT(lhs, rhs) CHECK_OP(GT, >, lhs, rhs)
#define CHECK_NULL(val) CHECK((val) == NULL)
#define CHECK_NOT_NULL(val) CHECK((val) != NULL)
#ifdef LOGGING_DEBUG
#define DCHECK_EQ(lhs, rhs) DCHECK_OP(EQ, ==, lhs, rhs)
#define DCHECK_NE(lhs, rhs) DCHECK_OP(NE, !=, lhs, rhs)
#define DCHECK_GT(lhs, rhs) DCHECK_OP(GT, >, lhs, rhs)
#define DCHECK_GE(lhs, rhs) DCHECK_OP(GE, >=, lhs, rhs)
#define DCHECK_LT(lhs, rhs) DCHECK_OP(LT, <, lhs, rhs)
#define DCHECK_LE(lhs, rhs) DCHECK_OP(LE, <=, lhs, rhs)
#define DCHECK_NULL(val) DCHECK((val) == nullptr)
#define DCHECK_NOT_NULL(val) DCHECK((val) != nullptr)
#define DCHECK_IMPLIES(lhs, rhs) DCHECK_WITH_MSG(!(lhs) || (rhs), #lhs " implies " #rhs)
#else
#define DCHECK(condition) ((void)0)
#define DCHECK_EQ(v1, v2) ((void)0)
#define DCHECK_NE(v1, v2) ((void)0)
#define DCHECK_GT(v1, v2) ((void)0)
#define DCHECK_GE(v1, v2) ((void)0)
#define DCHECK_LT(v1, v2) ((void)0)
#define DCHECK_LE(v1, v2) ((void)0)
#define DCHECK_NULL(val) ((void)0)
#define DCHECK_NOT_NULL(val) ((void)0)
#define DCHECK_IMPLIES(v1, v2) ((void)0)
#endif
#endif

View File

@ -0,0 +1,202 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#define LOG_TAG NULL
typedef enum {
LOG_LEVEL_DEBUG = 0,
LOG_LEVEL_INFO = 1,
LOG_LEVEL_WARN = 2,
LOG_LEVEL_ERROR = 3,
LOG_LEVEL_FATAL = 4
} LogLevel;
#ifdef __cplusplus
#if defined(USE_CXX_FILESTREAM)
#include <fstream>
#endif
class Logger {
public:
LogLevel log_level_;
const char *log_tag_;
const char *log_file_;
#if defined(USE_CXX_FILESTREAM)
std::fstream *log_file_stream_;
#else
FILE *log_file_stream_;
#endif
bool enable_time_tag_;
bool enable_syslog_;
static Logger *g_logger;
static Logger *Shared() {
if (g_logger == nullptr) {
g_logger = new Logger();
}
return g_logger;
}
Logger() {
log_tag_ = nullptr;
log_file_ = nullptr;
log_level_ = LOG_LEVEL_DEBUG;
enable_time_tag_ = false;
enable_syslog_ = false;
}
Logger(const char *tag, const char *file, LogLevel level, bool enable_time_tag, bool enable_syslog) {
setTag(tag);
setLogFile(file);
setLogLevel(level);
enable_time_tag_ = enable_time_tag;
enable_syslog_ = enable_syslog;
}
void setOptions(const char *tag, const char *file, LogLevel level, bool enable_time_tag, bool enable_syslog) {
if (tag)
setTag(tag);
if (file)
setLogFile(file);
setLogLevel(level);
enable_time_tag_ = enable_time_tag;
enable_syslog_ = enable_syslog;
}
void setTag(const char *tag) {
log_tag_ = tag;
}
void setLogFile(const char *file) {
log_file_ = file;
#if defined(USE_CXX_FILESTREAM)
log_file_stream_ = new std::fstream();
log_file_stream_->open(log_file_, std::ios::out | std::ios::app);
#else
log_file_stream_ = fopen(log_file_, "a");
#endif
}
void setLogLevel(LogLevel level) {
log_level_ = level;
}
void enableTimeTag() {
enable_time_tag_ = true;
}
void enableSyslog() {
enable_syslog_ = true;
}
void logv(LogLevel level, const char *fmt, va_list ap);
void log(LogLevel level, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logv(level, fmt, ap);
va_end(ap);
}
void debug(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logv(LOG_LEVEL_DEBUG, fmt, ap);
va_end(ap);
}
void info(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logv(LOG_LEVEL_INFO, fmt, ap);
va_end(ap);
}
void warn(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logv(LOG_LEVEL_WARN, fmt, ap);
va_end(ap);
}
void error(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logv(LOG_LEVEL_ERROR, fmt, ap);
va_end(ap);
}
void fatal(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logv(LOG_LEVEL_FATAL, fmt, ap);
va_end(ap);
}
};
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(DOBBY_LOGGING_DISABLE)
#define LOG_FUNCTION_IMPL(...)
#else
#if !defined(LOG_FUNCTION_IMPL)
#define LOG_FUNCTION_IMPL logger_log_impl
#endif
#endif
void *logger_create(const char *tag, const char *file, LogLevel level, bool enable_time_tag, bool enable_syslog);
void logger_set_options(void *logger, const char *tag, const char *file, LogLevel level, bool enable_time_tag,
bool enable_syslog);
void logger_log_impl(void *logger, LogLevel level, const char *fmt, ...);
#ifdef __cplusplus
}
#endif
#define LOG(level, fmt, ...) \
do { \
if (LOG_TAG) \
LOG_FUNCTION_IMPL(NULL, level, "[%s] " fmt, LOG_TAG, ##__VA_ARGS__); \
else \
LOG_FUNCTION_IMPL(NULL, level, fmt, ##__VA_ARGS__); \
} while (0)
#define DEBUG_LOG(fmt, ...) \
do { \
LOG(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__); \
} while (0)
#define INFO_LOG(fmt, ...) \
do { \
LOG(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__); \
} while (0)
#define WARN_LOG(fmt, ...) \
do { \
LOG(LOG_LEVEL_WARN, fmt, ##__VA_ARGS__); \
} while (0)
#define ERROR_LOG(fmt, ...) \
do { \
LOG(LOG_LEVEL_ERROR, "[!] [%s:%d:%s]" fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
} while (0)
#define FATAL_LOG(fmt, ...) \
do { \
LOG(LOG_LEVEL_FATAL, "[!] [%s:%d:%s]" fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
} while (0)
#define UNIMPLEMENTED() FATAL_LOG("%s\n", "unimplemented code!!!")
#define UNREACHABLE() FATAL_LOG("%s\n", "unreachable code!!!")

View File

@ -0,0 +1,162 @@
/*
* Copyright (c) 2006, 2010, 2013 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#ifndef _SYSTEM_SIMPLE_H_
#define _SYSTEM_SIMPLE_H_
#include <sys/cdefs.h>
#include <stdarg.h>
#include <Availability.h>
typedef void *_SIMPLE_STRING;
typedef const char *_esc_func(unsigned char);
__BEGIN_DECLS
/*
* A simplified vfprintf variant. The format string is interpreted with
* arguments from the va_list, and the results are written to the given
* file descriptor.
*/
void _simple_vdprintf(int __fd, const char *__fmt, va_list __ap) __printflike(2, 0);
/*
* A simplified fprintf variant. The format string is interpreted with
* arguments from the variable argument list, and the results are written
* to the given file descriptor.
*/
void _simple_dprintf(int __fd, const char *__fmt, ...) __printflike(2, 3);
/*
* A simplified string allocate routine. Pass the opaque pointer to structure
* to _simple_*sprintf() routines. Use _simple_string() to retrieve the
* current string (the string is guaranteed to be null terminated only on
* the call to _simple_string()). Use _simple_sfree() to free the structure
* and string memory.
*/
_SIMPLE_STRING _simple_salloc(void);
/*
* The format string is interpreted with arguments from the va_list, and the
* results are appended to the string maintained by the opaque structure, as
* returned by a previous call to _simple_salloc(). Non-zero is returned on
* out-of-memory error.
*/
int _simple_vsprintf(_SIMPLE_STRING __b, const char *__fmt, va_list __ap) __printflike(2, 0);
/*
* The format string is interpreted with arguments from the variable argument
* list, and the results are appended to the string maintained by the opaque
* structure, as returned by a previous call to _simple_salloc(). Non-zero is
* returned on out-of-memory error.
*/
int _simple_sprintf(_SIMPLE_STRING __b, const char *__fmt, ...) __printflike(2, 3);
/*
* Like _simple_vsprintf(), except __esc is a function to call on each
* character; the function returns NULL if the character should be passed
* as is, otherwise, the returned character string is used instead.
*/
int _simple_vesprintf(_SIMPLE_STRING __b, _esc_func __esc, const char *__fmt, va_list __ap) __printflike(3, 0);
/*
* Like _simple_sprintf(), except __esc is a function to call on each
* character; the function returns NULL if the character should be passed
* as is, otherwise, the returned character string is used instead.
*/
int _simple_esprintf(_SIMPLE_STRING __b, _esc_func __esc, const char *__fmt, ...) __printflike(3, 4);
/*
* Return the null terminated string from the opaque structure, as returned
* by a previous call to _simple_salloc().
*/
char *_simple_string(_SIMPLE_STRING __b);
/*
* Reposition the pointer to the first null in the buffer. After a call to
* _simple_string, the buffer can be modified, and shrunk.
*/
void _simple_sresize(_SIMPLE_STRING __b);
/*
* Append the null-terminated string to the string associated with the opaque
* structure. Non-zero is returned on out-of-memory error.
*/
int _simple_sappend(_SIMPLE_STRING __b, const char *__str);
/*
* Like _simple_sappend(), except __esc is a function to call on each
* character; the function returns NULL if the character should be passed
* as is, otherwise, the returned character string is used instead.
*/
int _simple_esappend(_SIMPLE_STRING __b, _esc_func __esc, const char *__str);
/*
* Write the string associated with the opaque structure to the file descriptor.
*/
void _simple_put(_SIMPLE_STRING __b, int __fd);
/*
* Write the string associated with the opaque structure and a trailing newline,
* to the file descriptor.
*/
void _simple_putline(_SIMPLE_STRING __b, int __fd);
/*
* Free the opaque structure, and the associated string.
*/
void _simple_sfree(_SIMPLE_STRING __b);
/*
* Simplified ASL log interface; does not use malloc. Unfortunately, this
* requires knowledge of the format used by ASL.
*/
#ifndef ASL_LEVEL_DEBUG
#define ASL_LEVEL_EMERG 0
#define ASL_LEVEL_ALERT 1
#define ASL_LEVEL_CRIT 2
#define ASL_LEVEL_ERR 3
#define ASL_LEVEL_WARNING 4
#define ASL_LEVEL_NOTICE 5
#define ASL_LEVEL_INFO 6
#define ASL_LEVEL_DEBUG 7
#endif
void _simple_asl_log(int __level, const char *__facility, const char *__message);
void _simple_asl_log_prog(int level, const char *facility, const char *message, const char *progname);
__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0)
_SIMPLE_STRING _simple_asl_msg_new(void);
__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0)
void _simple_asl_msg_set(_SIMPLE_STRING __b, const char *__key, const char *__val);
__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0)
void _simple_asl_send(_SIMPLE_STRING __b);
__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0)
const char *_simple_getenv(const char *envp[], const char *var);
__END_DECLS
#endif /* _SYSTEM_SIMPLE_H_ */

View File

@ -0,0 +1,3 @@
add_library(osbase STATIC
${PROJECT_SOURCE_DIR}/source/Backend/UserMode/UnifiedInterface/platform-posix.cc
)

View File

@ -0,0 +1,152 @@
#ifndef dobby_h
#define dobby_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
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

View File

@ -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

View File

@ -0,0 +1,242 @@
import os
import pipes
import re
import shutil
import subprocess
import sys
import logging
import argparse
platforms = {
"macos": ["x86_64", "arm64", "arm64e"],
"iphoneos": ["arm64", "arm64e"],
"linux": ["x86", "x86_64", "arm", "arm64"],
"android": ["x86", "x86_64", "armeabi-v7a", "arm64-v8a"]
}
class PlatformBuilder(object):
cmake_args = list()
cmake_build_type = "Release"
cmake_build_verbose = False
cmake_build_dir = ""
library_build_type = "static"
project_dir = ""
output_dir = ""
shared_output_name = ""
static_output_name = ""
platform = ""
arch = ""
def __init__(self, project_dir, library_build_type, platform, arch):
self.project_dir = project_dir
self.library_build_type = library_build_type
self.platform = platform
self.arch = arch
self.cmake_build_dir = f"{self.project_dir}/build/cmake-build-{platform}-{arch}"
self.output_dir = f"{self.project_dir}/build/{platform}/{arch}"
self.cmake = "cmake" if PlatformBuilder.cmake_dir is None else f"{PlatformBuilder.cmake_dir}/bin/cmake"
self.clang = "clang" if PlatformBuilder.llvm_dir is None else f"{PlatformBuilder.llvm_dir}/bin/clang"
self.clangxx = "clang++" if PlatformBuilder.llvm_dir is None else f"{PlatformBuilder.llvm_dir}/bin/clang++"
self.setup_common_args()
def cmake_generate_build_system(self):
cmake_cmd_options = ["-S {}".format(self.project_dir), "-B {}".format(self.cmake_build_dir)]
cmd = [f"{self.cmake}"] + cmake_cmd_options + self.cmake_args
# subprocess.run(cmd, check=True)
cmd_line = " ".join(cmd)
print(cmd_line)
os.system(cmd_line)
def setup_common_args(self):
self.cmake_args += [f"-DCMAKE_C_COMPILER={self.clang}", f"-DCMAKE_CXX_COMPILER={self.clangxx}"]
self.cmake_args += ["-DCMAKE_BUILD_TYPE={}".format(self.cmake_build_type)]
if self.library_build_type == "shared":
pass
# self.cmake_args += ["-DDOBBY_GENERATE_SHARED=ON"]
elif self.library_build_type == "static":
pass
# self.cmake_args += ["-DDOBBY_GENERATE_SHARED=OFF"]
def build(self):
subprocess.run(["mkdir", "-p", "{}".format(self.output_dir)], check=True)
self.cmake_generate_build_system()
subprocess.run("cmake --build . --clean-first --target dobby --target dobby_static -- -j8", cwd=self.cmake_build_dir, shell=True, check=True)
os.system(f"mkdir -p {self.output_dir}")
os.system(f"cp {self.cmake_build_dir}/{self.shared_output_name} {self.output_dir}")
os.system(f"cp {self.cmake_build_dir}/{self.static_output_name} {self.output_dir}")
class WindowsPlatformBuilder(PlatformBuilder):
def __init__(self, project_dir, library_build_type, platform, arch):
super().__init__(project_dir, library_build_type, platform, arch)
if self.library_build_type == "shared":
self.output_name = "libdobby.dll"
else:
self.output_name = "libdobby.lib"
triples = {
"x86": "i686-pc-windows-msvc",
"x64": "x86_64-pc-windows-msvc",
# "arm": "arm-pc-windows-msvc",
"arm64": "arm64-pc-windows-msvc",
}
# self.cmake_args += ["--target {}".format(triples[arch])]
self.cmake_args += [
"-DCMAKE_SYSTEM_PROCESSOR={}".format(arch),
]
class LinuxPlatformBuilder(PlatformBuilder):
def __init__(self, project_dir, library_build_type, arch):
super().__init__(project_dir, library_build_type, "linux", arch)
self.shared_output_name = "libdobby.so"
self.static_output_name = "libdobby.a"
targets = {
"x86": "i686-linux-gnu",
"x86_64": "x86_64-linux-gnu",
"arm": "arm-linux-gnueabi",
"aarch64": "aarch64-linux-gnu",
}
# self.cmake_args += ["--target={}".format(targets[arch])]
self.cmake_args += [
"-DCMAKE_SYSTEM_NAME=Linux",
"-DCMAKE_SYSTEM_PROCESSOR={}".format(arch),
]
class AndroidPlatformBuilder(PlatformBuilder):
def __init__(self, android_nkd_dir, project_dir, library_build_type, arch):
super().__init__(project_dir, library_build_type, "android", arch)
self.shared_output_name = "libdobby.so"
self.static_output_name = "libdobby.a"
android_api_level = 21
if arch == "armeabi-v7a" or arch == "x86":
android_api_level = 19
self.cmake_args += [
"-DCMAKE_SYSTEM_NAME=Android", f"-DCMAKE_ANDROID_NDK={android_nkd_dir}", f"-DCMAKE_ANDROID_ARCH_ABI={arch}",
f"-DCMAKE_SYSTEM_VERSION={android_api_level}"
]
class DarwinPlatformBuilder(PlatformBuilder):
def __init__(self, project_dir, library_build_type, platform, arch):
super().__init__(project_dir, library_build_type, platform, arch)
self.cmake_args += [
"-DCMAKE_OSX_ARCHITECTURES={}".format(arch),
"-DCMAKE_SYSTEM_PROCESSOR={}".format(arch),
]
if platform == "macos":
self.cmake_args += ["-DCMAKE_SYSTEM_NAME=Darwin"]
elif platform == "iphoneos":
self.cmake_args += ["-DCMAKE_SYSTEM_NAME=iOS", "-DCMAKE_OSX_DEPLOYMENT_TARGET=9.3"]
self.shared_output_name = "libdobby.dylib"
self.static_output_name = "libdobby.a"
@classmethod
def lipo_create_fat(cls, project_dir, platform, output_name):
files = list()
archs = platforms[platform]
for arch in archs:
file = f"{project_dir}/build/{platform}/{arch}/{output_name}"
files.append(file)
fat_output_dir = f"{project_dir}/build/{platform}/universal"
subprocess.run(["mkdir", "-p", "{}".format(fat_output_dir)], check=True)
cmd = ["lipo", "-create"] + files + ["-output", f"{fat_output_dir}/{output_name}"]
subprocess.run(cmd, check=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--platform", type=str, required=True)
parser.add_argument("--arch", type=str, required=True)
parser.add_argument("--library_build_type", type=str, default="static")
parser.add_argument("--android_ndk_dir", type=str)
parser.add_argument("--cmake_dir", type=str)
parser.add_argument("--llvm_dir", type=str)
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
platform = args.platform
arch = args.arch
library_build_type = args.library_build_type
PlatformBuilder.cmake_dir = args.cmake_dir
PlatformBuilder.llvm_dir = args.llvm_dir
project_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
logging.info("project dir: {}".format(project_dir))
if not os.path.exists(f"{project_dir}/CMakeLists.txt"):
logging.error("Please run this script in Dobby project root directory")
sys.exit(1)
if platform not in platforms:
logging.error("invalid platform {}".format(platform))
sys.exit(-1)
if arch != "all" and arch not in platforms[platform]:
logging.error("invalid arch {} for platform {}".format(arch, platform))
sys.exit(-1)
if platform == "android":
if args.android_ndk_dir is None:
logging.error("ndk dir is required for android platform")
sys.exit(-1)
archs = list()
if arch == "all":
archs = platforms[platform]
else:
archs.append(arch)
logging.info("build platform: {}, archs: {}".format(platform, archs))
builder: PlatformBuilder = None
for arch_ in archs:
if platform == "macos":
builder = DarwinPlatformBuilder(project_dir, library_build_type, platform, arch_)
elif platform == "iphoneos":
builder = DarwinPlatformBuilder(project_dir, library_build_type, platform, arch_)
elif platform == "android":
builder = AndroidPlatformBuilder(args.android_ndk_dir, project_dir, library_build_type, arch_)
elif platform == "linux":
builder = LinuxPlatformBuilder(project_dir, library_build_type, arch_)
else:
logging.error("invalid platform {}".format(platform))
sys.exit(-1)
logging.info(
f"build platform: {platform}, arch: {arch_}, cmake_build_dir: {builder.cmake_build_dir}, output_dir: {builder.output_dir}"
)
builder.build()
if platform in ["iphoneos", "macos"] and arch == "all":
DarwinPlatformBuilder.lipo_create_fat(project_dir, platform, builder.shared_output_name)
DarwinPlatformBuilder.lipo_create_fat(project_dir, platform, builder.static_output_name)

View File

@ -0,0 +1,61 @@
#!/bin/sh
set -x
set -e
# sudo dpkg --add-architecture armhf
# sudo dpkg --add-architecture i386
# sudo dpkg --add-architecture arm64
# sudo apt-get -y update
# sudo apt-get -y dist-upgrade
# sudo apt-get -y install git build-essential libssl-dev pkg-config unzip gcc-multilib
# sudo apt-get -y install libc6-armhf-cross libc6-dev-armhf-cross gcc-arm-linux-gnueabihf libssl-dev:armhf
# sudo apt-get -y install libc6-i386-cross libc6-dev-i386-cross gcc-i686-linux-gnu libssl-dev:i386
# sudo apt-get -y install libc6-arm64-cross libc6-dev-arm64-cross gcc-aarch64-linux-gnu libssl-dev:arm64
sudo apt-get -y update
sudo apt-get -y install aptitude
sudo apt-get -f -y install \
apt-utils \
binutils \
build-essential \
curl \
wget \
unzip \
gcc-multilib \
g++-multilib \
make \
zsh
sudo apt-get -f -y install gcc g++ libc6-dev
sudo apt-get -f -y install gcc-i686-linux-gnu g++-i686-linux-gnu
sudo apt-get -f -y install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
sudo apt-get -f -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
mkdir -p ~/opt
cd ~/opt
CMAKE_VERSION=3.25.2
CMAKE_DOWNLOAD_PACKAGE=cmake-$CMAKE_VERSION-linux-x86_64
wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/$CMAKE_DOWNLOAD_PACKAGE.tar.gz &&
tar -zxf $CMAKE_DOWNLOAD_PACKAGE.tar.gz >/dev/null &&
mv $CMAKE_DOWNLOAD_PACKAGE cmake-$CMAKE_VERSION
CMAKE_HOME=~/opt/cmake-$CMAKE_VERSION
cd ~/opt
LLVM_VERSION=15.0.6
LLVM_DOWNLOAD_PACKAGE=clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-18.04
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/$LLVM_DOWNLOAD_PACKAGE.tar.xz &&
tar -xf $LLVM_DOWNLOAD_PACKAGE.tar.xz >/dev/null &&
mv $LLVM_DOWNLOAD_PACKAGE llvm-$LLVM_VERSION
LLVM_HOME=~/opt/llvm-$LLVM_VERSION
cd ~/opt
NDK_VERSION=r25b
NDK_DOWNLOAD_PACKAGE=android-ndk-$NDK_VERSION-linux
NDK_DOWNLOAD_UNZIP_PACKAGE=android-ndk-$NDK_VERSION
wget https://dl.google.com/android/repository/$NDK_DOWNLOAD_PACKAGE.zip &&
unzip -q $NDK_DOWNLOAD_PACKAGE.zip >/dev/null &&
mv $NDK_DOWNLOAD_UNZIP_PACKAGE ndk-$NDK_VERSION &&
rm $NDK_DOWNLOAD_PACKAGE.zip
ANDROID_NDK_HOME=~/opt/android-ndk-$NDK_VERSION

View File

@ -0,0 +1,22 @@
#!/bin/sh
set -x
set -e
mkdir -p ~/opt
cd ~/opt
CMAKE_VERSION=3.25.2
CMAKE_DOWNLOAD_PACKAGE=cmake-$CMAKE_VERSION-macos-universal
wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/$CMAKE_DOWNLOAD_PACKAGE.tar.gz &&
tar -zxf $CMAKE_DOWNLOAD_PACKAGE.tar.gz >/dev/null &&
mv $CMAKE_DOWNLOAD_PACKAGE cmake-$CMAKE_VERSION
CMAKE_HOME=~/opt/cmake-$CMAKE_VERSION
cd ~/opt
LLVM_VERSION=15.0.6
LLVM_DOWNLOAD_PACKAGE=clang+llvm-$LLVM_VERSION-x86_64-apple-darwin
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/$LLVM_DOWNLOAD_PACKAGE.tar.xz &&
tar -xf $LLVM_DOWNLOAD_PACKAGE.tar.xz >/dev/null &&
mv $LLVM_DOWNLOAD_PACKAGE llvm-$LLVM_VERSION
LLVM_HOME=~/opt/llvm-$LLVM_VERSION

Some files were not shown because too many files have changed in this diff Show More