diff --git a/.gitmodules b/.gitmodules index b79ccd467f895049602ca3947757d423b96f97c2..15d4b83af0efbd63e085498c5f48f525cb6898e5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "app/src/main/cpp/Dobby"] - path = app/src/main/cpp/Dobby - url = https://github.com/jmpews/Dobby.git +[submodule "app/src/main/cpp/libcxx"] + path = app/src/main/cpp/libcxx + url = https://github.com/topjohnwu/libcxx.git diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7d39699ab05949d8e54e4ad87ad7898a1b3f5f1d..c6a5fc3fee295f3a215d34b34a1b698e90e28618 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,16 +8,6 @@ android { ndkVersion = "26.1.10909125" buildToolsVersion = "34.0.0" - packaging { - jniLibs { - excludes += "**/libdobby.so" - } - } - - buildFeatures { - prefab = true - } - defaultConfig { applicationId = "es.chiteroman.playintegrityfix" minSdk = 26 @@ -26,15 +16,10 @@ android { versionName = "1.0" externalNativeBuild { - cmake { - arguments += "-DANDROID_STL=none" - arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel" - - cppFlags += "-std=c++20" - cppFlags += "-fno-exceptions" - cppFlags += "-fno-rtti" - cppFlags += "-fvisibility=hidden" - cppFlags += "-fvisibility-inlines-hidden" + ndk { + jobs = Runtime.getRuntime().availableProcessors() + abiFilters += "armeabi-v7a" + abiFilters += "arm64-v8a" } } } @@ -53,13 +38,8 @@ android { } externalNativeBuild { - cmake { - path = file("src/main/cpp/CMakeLists.txt") - version = "3.22.1" + ndkBuild { + path = file("src/main/cpp/Android.mk") } } -} - -dependencies { - implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0") } \ No newline at end of file diff --git a/app/src/main/cpp/Android.mk b/app/src/main/cpp/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..9632627a8ec7c030098b14d15ff5100625760f6f --- /dev/null +++ b/app/src/main/cpp/Android.mk @@ -0,0 +1,33 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := zygisk +LOCAL_SRC_FILES := main.cpp +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +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)/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 +endif + +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_STATIC_LIBRARIES := libcxx +LOCAL_LDLIBS := -llog +include $(BUILD_SHARED_LIBRARY) + +include $(LOCAL_PATH)/libcxx/Android.mk \ No newline at end of file diff --git a/app/src/main/cpp/Application.mk b/app/src/main/cpp/Application.mk new file mode 100644 index 0000000000000000000000000000000000000000..da73f7ceb22cdfd30d8f384aabbc1bbaf18260eb --- /dev/null +++ b/app/src/main/cpp/Application.mk @@ -0,0 +1,4 @@ +APP_STL := none +APP_CFLAGS := -Oz -fno-exceptions -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden +APP_CPPFLAGS := -std=c++20 +APP_LDFLAGS := -Oz \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt deleted file mode 100644 index 2071d710ca6dcc58db8e2596b19fef5aadafb498..0000000000000000000000000000000000000000 --- a/app/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -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) - -SET_OPTION(Plugin.Android.BionicLinkerUtil ON) - -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log dobby_static) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby b/app/src/main/cpp/Dobby deleted file mode 160000 index b0176de574104726bb68dff3b77ee666300fc338..0000000000000000000000000000000000000000 --- a/app/src/main/cpp/Dobby +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b0176de574104726bb68dff3b77ee666300fc338 diff --git a/app/src/main/cpp/json.hpp b/app/src/main/cpp/json.hpp index dc58ca118954cdf1d27fee4578b504f855f699c3..4d1a37ad7cb874769d911fc3362d567da3aca291 100644 --- a/app/src/main/cpp/json.hpp +++ b/app/src/main/cpp/json.hpp @@ -27,6 +27,7 @@ #endif // JSON_NO_IO #include <iterator> // random_access_iterator_tag #include <memory> // unique_ptr +#include <numeric> // accumulate #include <string> // string, stoi, to_string #include <utility> // declval, forward, move, pair, swap #include <vector> // vector @@ -182,9 +183,6 @@ #include <cstddef> // nullptr_t #include <exception> // exception -#if JSON_DIAGNOSTICS - #include <numeric> // accumulate -#endif #include <stdexcept> // runtime_error #include <string> // to_string #include <vector> // vector @@ -2485,14 +2483,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif #endif -#ifndef JSON_HAS_STATIC_RTTI - #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 - #define JSON_HAS_STATIC_RTTI 1 - #else - #define JSON_HAS_STATIC_RTTI 0 - #endif -#endif - #ifdef JSON_HAS_CPP_17 #define JSON_INLINE_VARIABLE inline #else @@ -2600,13 +2590,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP class NumberUnsignedType, class NumberFloatType, \ template<typename> class AllocatorType, \ template<typename, typename = void> class JSONSerializer, \ - class BinaryType, \ - class CustomBaseClass> + class BinaryType> #define NLOHMANN_BASIC_JSON_TPL \ basic_json<ObjectType, ArrayType, StringType, BooleanType, \ NumberIntegerType, NumberUnsignedType, NumberFloatType, \ - AllocatorType, JSONSerializer, BinaryType, CustomBaseClass> + AllocatorType, JSONSerializer, BinaryType> // Macros to simplify conversion from/to types @@ -2756,7 +2745,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } /*! @brief macro @@ -2769,7 +2758,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + // inspired from https://stackoverflow.com/a/26745591 // allows to call any std function as if (e.g. with begin): @@ -3399,8 +3389,7 @@ NLOHMANN_JSON_NAMESPACE_END template<typename U> class AllocatorType = std::allocator, template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer, - class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError - class CustomBaseClass = void> + class BinaryType = std::vector<std::uint8_t>> class basic_json; /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document @@ -3624,6 +3613,7 @@ template <typename... Ts> struct is_default_constructible<const std::tuple<Ts...>> : conjunction<is_default_constructible<Ts>...> {}; + template <typename T, typename... Args> struct is_constructible : std::is_constructible<T, Args...> {}; @@ -3639,6 +3629,7 @@ struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple template <typename... Ts> struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {}; + template<typename T, typename = void> struct is_iterator_traits : std::false_type {}; @@ -4048,6 +4039,7 @@ struct value_in_range_of_impl2<OfType, T, false, true> } }; + template<typename OfType, typename T> struct value_in_range_of_impl2<OfType, T, true, true> { @@ -4173,28 +4165,28 @@ inline std::size_t concat_length() } template<typename... Args> -inline std::size_t concat_length(const char* cstr, const Args& ... rest); +inline std::size_t concat_length(const char* cstr, Args&& ... rest); template<typename StringType, typename... Args> -inline std::size_t concat_length(const StringType& str, const Args& ... rest); +inline std::size_t concat_length(const StringType& str, Args&& ... rest); template<typename... Args> -inline std::size_t concat_length(const char /*c*/, const Args& ... rest) +inline std::size_t concat_length(const char /*c*/, Args&& ... rest) { - return 1 + concat_length(rest...); + return 1 + concat_length(std::forward<Args>(rest)...); } template<typename... Args> -inline std::size_t concat_length(const char* cstr, const Args& ... rest) +inline std::size_t concat_length(const char* cstr, Args&& ... rest) { // cppcheck-suppress ignoredReturnValue - return ::strlen(cstr) + concat_length(rest...); + return ::strlen(cstr) + concat_length(std::forward<Args>(rest)...); } template<typename StringType, typename... Args> -inline std::size_t concat_length(const StringType& str, const Args& ... rest) +inline std::size_t concat_length(const StringType& str, Args&& ... rest) { - return str.size() + concat_length(rest...); + return str.size() + concat_length(std::forward<Args>(rest)...); } template<typename OutStringType> @@ -4285,7 +4277,7 @@ template<typename OutStringType = std::string, typename... Args> inline OutStringType concat(Args && ... args) { OutStringType str; - str.reserve(concat_length(args...)); + str.reserve(concat_length(std::forward<Args>(args)...)); concat_into(str, std::forward<Args>(args)...); return str; } @@ -4294,6 +4286,7 @@ inline OutStringType concat(Args && ... args) NLOHMANN_JSON_NAMESPACE_END + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -4341,9 +4334,9 @@ class exception : public std::exception { case value_t::array: { - for (std::size_t i = 0; i < current->m_parent->m_data.m_value.array->size(); ++i) + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) { - if (¤t->m_parent->m_data.m_value.array->operator[](i) == current) + if (¤t->m_parent->m_value.array->operator[](i) == current) { tokens.emplace_back(std::to_string(i)); break; @@ -4354,7 +4347,7 @@ class exception : public std::exception case value_t::object: { - for (const auto& element : *current->m_parent->m_data.m_value.object) + for (const auto& element : *current->m_parent->m_value.object) { if (&element.second == current) { @@ -4417,17 +4410,17 @@ class parse_error : public exception template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0> static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("parse_error", id_), "parse error", - position_string(pos), ": ", exception::diagnostics(context), what_arg); + std::string w = concat(exception::name("parse_error", id_), "parse error", + position_string(pos), ": ", exception::diagnostics(context), what_arg); return {id_, pos.chars_read_total, w.c_str()}; } template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0> static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("parse_error", id_), "parse error", - (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""), - ": ", exception::diagnostics(context), what_arg); + std::string w = concat(exception::name("parse_error", id_), "parse error", + (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""), + ": ", exception::diagnostics(context), what_arg); return {id_, byte_, w.c_str()}; } @@ -4461,7 +4454,7 @@ class invalid_iterator : public exception template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0> static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg); + std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg); return {id_, w.c_str()}; } @@ -4479,7 +4472,7 @@ class type_error : public exception template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0> static type_error create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg); + std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg); return {id_, w.c_str()}; } @@ -4496,7 +4489,7 @@ class out_of_range : public exception template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0> static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg); + std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg); return {id_, w.c_str()}; } @@ -4513,7 +4506,7 @@ class other_error : public exception template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0> static other_error create(int id_, const std::string& what_arg, BasicJsonContext context) { - const std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg); + std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg); return {id_, w.c_str()}; } @@ -5154,10 +5147,10 @@ template<typename IteratorType> class iteration_proxy_value // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions iteration_proxy_value(iteration_proxy_value&&) noexcept(std::is_nothrow_move_constructible<IteratorType>::value - && std::is_nothrow_move_constructible<string_type>::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations) + && std::is_nothrow_move_constructible<string_type>::value) = default; iteration_proxy_value& operator=(iteration_proxy_value&&) noexcept(std::is_nothrow_move_assignable<IteratorType>::value - && std::is_nothrow_move_assignable<string_type>::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations) + && std::is_nothrow_move_assignable<string_type>::value) = default; ~iteration_proxy_value() = default; /// dereference operator (needed for range-based for) @@ -5304,11 +5297,11 @@ namespace std #pragma clang diagnostic ignored "-Wmismatched-tags" #endif template<typename IteratorType> -class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> // NOLINT(cert-dcl58-cpp) +class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> : public std::integral_constant<std::size_t, 2> {}; template<std::size_t N, typename IteratorType> -class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> // NOLINT(cert-dcl58-cpp) +class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> { public: using type = decltype( @@ -5347,7 +5340,7 @@ namespace detail /* * Note all external_constructor<>::construct functions need to call - * j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an + * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an * allocated value (e.g., a string). See bug issue * https://github.com/nlohmann/json/issues/2865 for more information. */ @@ -5360,9 +5353,9 @@ struct external_constructor<value_t::boolean> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::boolean; - j.m_data.m_value = b; + j.m_value.destroy(j.m_type); + j.m_type = value_t::boolean; + j.m_value = b; j.assert_invariant(); } }; @@ -5373,18 +5366,18 @@ struct external_constructor<value_t::string> template<typename BasicJsonType> static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::string; - j.m_data.m_value = s; + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = s; j.assert_invariant(); } template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::string; - j.m_data.m_value = std::move(s); + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = std::move(s); j.assert_invariant(); } @@ -5393,9 +5386,9 @@ struct external_constructor<value_t::string> int > = 0 > static void construct(BasicJsonType& j, const CompatibleStringType& str) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::string; - j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str); + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value.string = j.template create<typename BasicJsonType::string_t>(str); j.assert_invariant(); } }; @@ -5406,18 +5399,18 @@ struct external_constructor<value_t::binary> template<typename BasicJsonType> static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::binary; - j.m_data.m_value = typename BasicJsonType::binary_t(b); + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(b); j.assert_invariant(); } template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::binary; - j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b)); + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(std::move(b)); j.assert_invariant(); } }; @@ -5428,9 +5421,9 @@ struct external_constructor<value_t::number_float> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::number_float; - j.m_data.m_value = val; + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_float; + j.m_value = val; j.assert_invariant(); } }; @@ -5441,9 +5434,9 @@ struct external_constructor<value_t::number_unsigned> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::number_unsigned; - j.m_data.m_value = val; + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_unsigned; + j.m_value = val; j.assert_invariant(); } }; @@ -5454,9 +5447,9 @@ struct external_constructor<value_t::number_integer> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::number_integer; - j.m_data.m_value = val; + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_integer; + j.m_value = val; j.assert_invariant(); } }; @@ -5467,9 +5460,9 @@ struct external_constructor<value_t::array> template<typename BasicJsonType> static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::array; - j.m_data.m_value = arr; + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = arr; j.set_parents(); j.assert_invariant(); } @@ -5477,9 +5470,9 @@ struct external_constructor<value_t::array> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::array; - j.m_data.m_value = std::move(arr); + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = std::move(arr); j.set_parents(); j.assert_invariant(); } @@ -5492,9 +5485,9 @@ struct external_constructor<value_t::array> using std::begin; using std::end; - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::array; - j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr)); + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr)); j.set_parents(); j.assert_invariant(); } @@ -5502,14 +5495,14 @@ struct external_constructor<value_t::array> template<typename BasicJsonType> static void construct(BasicJsonType& j, const std::vector<bool>& arr) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::array; - j.m_data.m_value = value_t::array; - j.m_data.m_value.array->reserve(arr.size()); + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); for (const bool x : arr) { - j.m_data.m_value.array->push_back(x); - j.set_parent(j.m_data.m_value.array->back()); + j.m_value.array->push_back(x); + j.set_parent(j.m_value.array->back()); } j.assert_invariant(); } @@ -5518,13 +5511,13 @@ struct external_constructor<value_t::array> enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> static void construct(BasicJsonType& j, const std::valarray<T>& arr) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::array; - j.m_data.m_value = value_t::array; - j.m_data.m_value.array->resize(arr.size()); + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); if (arr.size() > 0) { - std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin()); + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); } j.set_parents(); j.assert_invariant(); @@ -5537,9 +5530,9 @@ struct external_constructor<value_t::object> template<typename BasicJsonType> static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::object; - j.m_data.m_value = obj; + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = obj; j.set_parents(); j.assert_invariant(); } @@ -5547,9 +5540,9 @@ struct external_constructor<value_t::object> template<typename BasicJsonType> static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::object; - j.m_data.m_value = std::move(obj); + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = std::move(obj); j.set_parents(); j.assert_invariant(); } @@ -5561,9 +5554,9 @@ struct external_constructor<value_t::object> using std::begin; using std::end; - j.m_data.m_value.destroy(j.m_data.m_type); - j.m_data.m_type = value_t::object; - j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj)); + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj)); j.set_parents(); j.assert_invariant(); } @@ -6147,6 +6140,7 @@ class file_input_adapter std::FILE* m_file; }; + /*! Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at beginning of input. Does not support changing the underlying std::streambuf @@ -6245,6 +6239,7 @@ class iterator_input_adapter } }; + template<typename BaseInputAdapter, size_t T> struct wide_string_input_helper; @@ -6368,7 +6363,7 @@ struct wide_string_input_helper<BaseInputAdapter, 2> } }; -// Wraps another input adapter to convert wide character types into individual bytes. +// Wraps another input apdater to convert wide character types into individual bytes. template<typename BaseInputAdapter, typename WideCharType> class wide_string_input_adapter { @@ -6413,6 +6408,7 @@ class wide_string_input_adapter std::size_t utf8_bytes_filled = 0; }; + template<typename IteratorType, typename Enable = void> struct iterator_input_adapter_factory { @@ -6714,6 +6710,7 @@ struct json_sax virtual ~json_sax() = default; }; + namespace detail { /*! @@ -6815,7 +6812,7 @@ class json_sax_dom_parser JSON_ASSERT(ref_stack.back()->is_object()); // add null at given key and store the reference for later - object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val)); + object_element = &(ref_stack.back()->m_value.object->operator[](val)); return true; } @@ -6890,8 +6887,8 @@ class json_sax_dom_parser if (ref_stack.back()->is_array()) { - ref_stack.back()->m_data.m_value.array->emplace_back(std::forward<Value>(v)); - return &(ref_stack.back()->m_data.m_value.array->back()); + ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v)); + return &(ref_stack.back()->m_value.array->back()); } JSON_ASSERT(ref_stack.back()->is_object()); @@ -7010,7 +7007,7 @@ class json_sax_dom_callback_parser // add discarded value at given key and store the reference for later if (keep && ref_stack.back()) { - object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded); + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); } return true; @@ -7095,7 +7092,7 @@ class json_sax_dom_callback_parser // remove discarded value if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) { - ref_stack.back()->m_data.m_value.array->pop_back(); + ref_stack.back()->m_value.array->pop_back(); } return true; @@ -7162,7 +7159,7 @@ class json_sax_dom_callback_parser if (ref_stack.empty()) { root = std::move(value); - return {true, & root}; + return {true, &root}; } // skip this value if we already decided to skip the parent @@ -7178,8 +7175,8 @@ class json_sax_dom_callback_parser // array if (ref_stack.back()->is_array()) { - ref_stack.back()->m_data.m_value.array->emplace_back(std::move(value)); - return {true, & (ref_stack.back()->m_data.m_value.array->back())}; + ref_stack.back()->m_value.array->emplace_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; } // object @@ -7526,7 +7523,7 @@ class lexer : public lexer_base<BasicJsonType> for (auto range = ranges.begin(); range != ranges.end(); ++range) { get(); - if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions) + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) { add(current); } @@ -9132,6 +9129,7 @@ static inline bool little_endianness(int num = 1) noexcept return *reinterpret_cast<char*>(&num) == 1; } + /////////////////// // binary reader // /////////////////// @@ -9406,7 +9404,7 @@ class binary_reader { std::array<char, 3> cr{{}}; static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - const std::string cr_str{cr.data()}; + std::string cr_str{cr.data()}; return sax->parse_error(element_type_parse_position, cr_str, parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr)); } @@ -11229,7 +11227,7 @@ class binary_reader } if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimensional vector is not allowed", "size"), nullptr)); + return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimentional vector is not allowed", "size"), nullptr)); } std::vector<size_t> dim; if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim))) @@ -11341,7 +11339,7 @@ class binary_reader exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); } - const bool is_error = get_ubjson_size_value(result.first, is_ndarray); + bool is_error = get_ubjson_size_value(result.first, is_ndarray); if (input_format == input_format_t::bjdata && is_ndarray) { if (inside_ndarray) @@ -11356,7 +11354,7 @@ class binary_reader if (current == '#') { - const bool is_error = get_ubjson_size_value(result.first, is_ndarray); + bool is_error = get_ubjson_size_value(result.first, is_ndarray); if (input_format == input_format_t::bjdata && is_ndarray) { return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read, @@ -12441,25 +12439,13 @@ class parser m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); } - case token_type::end_of_input: - { - if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); - } - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr)); - } case token_type::uninitialized: case token_type::end_array: case token_type::end_object: case token_type::name_separator: case token_type::value_separator: + case token_type::end_of_input: case token_type::literal_or_value: default: // the last token was unexpected { @@ -12901,7 +12887,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: { @@ -12998,17 +12984,17 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: { - m_it.object_iterator = m_object->m_data.m_value.object->begin(); + m_it.object_iterator = m_object->m_value.object->begin(); break; } case value_t::array: { - m_it.array_iterator = m_object->m_data.m_value.array->begin(); + m_it.array_iterator = m_object->m_value.array->begin(); break; } @@ -13042,17 +13028,17 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: { - m_it.object_iterator = m_object->m_data.m_value.object->end(); + m_it.object_iterator = m_object->m_value.object->end(); break; } case value_t::array: { - m_it.array_iterator = m_object->m_data.m_value.array->end(); + m_it.array_iterator = m_object->m_value.array->end(); break; } @@ -13081,17 +13067,17 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: { - JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case value_t::array: { - JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } @@ -13125,17 +13111,17 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: { - JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case value_t::array: { - JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } @@ -13178,7 +13164,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: { @@ -13229,7 +13215,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: { @@ -13276,7 +13262,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: return (m_it.object_iterator == other.m_it.object_iterator); @@ -13321,7 +13307,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object)); @@ -13377,7 +13363,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); @@ -13456,7 +13442,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); @@ -13485,7 +13471,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - switch (m_object->m_data.m_type) + switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object)); @@ -13687,40 +13673,6 @@ NLOHMANN_JSON_NAMESPACE_END // #include <nlohmann/detail/iterators/primitive_iterator.hpp> -// #include <nlohmann/detail/json_custom_base_class.hpp> - - -#include <type_traits> // conditional, is_same - -// #include <nlohmann/detail/abi_macros.hpp> - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/*! -@brief Default base class of the @ref basic_json class. - -So that the correct implementations of the copy / move ctors / assign operators -of @ref basic_json do not require complex case distinctions -(no base class / custom base class used as customization point), -@ref basic_json always has a base class. -By default, this class is used because it is empty and thus has no effect -on the behavior of @ref basic_json. -*/ -struct json_default_base {}; - -template<class T> -using json_base_class = typename std::conditional < - std::is_same<T, void>::value, - json_default_base, - T - >::type; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - // #include <nlohmann/detail/json_pointer.hpp> // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ @@ -13959,7 +13911,7 @@ class json_pointer const char* p = s.c_str(); char* p_end = nullptr; errno = 0; // strtoull doesn't reset errno - const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) + unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) if (p == p_end // invalid input or empty string || errno == ERANGE // out of range || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read @@ -14115,7 +14067,7 @@ class json_pointer if (reference_token == "-") { // explicitly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_data.m_value.array->size()); + ptr = &ptr->operator[](ptr->m_value.array->size()); } else { @@ -14167,7 +14119,7 @@ class json_pointer { // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, detail::concat( - "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), + "array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr)); } @@ -14224,7 +14176,7 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); + JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr)); } // use unchecked array access @@ -14274,7 +14226,7 @@ class json_pointer { // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, detail::concat( - "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), + "array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr)); } @@ -14469,7 +14421,7 @@ class json_pointer { case detail::value_t::array: { - if (value.m_data.m_value.array->empty()) + if (value.m_value.array->empty()) { // flatten empty array as null result[reference_string] = nullptr; @@ -14477,10 +14429,10 @@ class json_pointer else { // iterate array and use index as reference string - for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i) + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) { flatten(detail::concat(reference_string, '/', std::to_string(i)), - value.m_data.m_value.array->operator[](i), result); + value.m_value.array->operator[](i), result); } } break; @@ -14488,7 +14440,7 @@ class json_pointer case detail::value_t::object: { - if (value.m_data.m_value.object->empty()) + if (value.m_value.object->empty()) { // flatten empty object as null result[reference_string] = nullptr; @@ -14496,7 +14448,7 @@ class json_pointer else { // iterate object and use keys as reference string - for (const auto& element : *value.m_data.m_value.object) + for (const auto& element : *value.m_value.object) { flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); } @@ -14543,7 +14495,7 @@ class json_pointer BasicJsonType result; // iterate the JSON object values - for (const auto& element : *value.m_data.m_value.object) + for (const auto& element : *value.m_value.object) { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { @@ -15026,7 +14978,7 @@ class binary_writer { case value_t::object: { - write_bson_object(*j.m_data.m_value.object); + write_bson_object(*j.m_value.object); break; } @@ -15061,7 +15013,7 @@ class binary_writer case value_t::boolean: { - oa->write_character(j.m_data.m_value.boolean + oa->write_character(j.m_value.boolean ? to_char_type(0xF5) : to_char_type(0xF4)); break; @@ -15069,42 +15021,42 @@ class binary_writer case value_t::number_integer: { - if (j.m_data.m_value.number_integer >= 0) + if (j.m_value.number_integer >= 0) { // CBOR does not differentiate between positive signed // integers and unsigned integers. Therefore, we used the // code from the value_t::number_unsigned case here. - if (j.m_data.m_value.number_integer <= 0x17) + if (j.m_value.number_integer <= 0x17) { - write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) + else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) { oa->write_character(to_char_type(0x18)); - write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()) + else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()) { oa->write_character(to_char_type(0x19)); - write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint16_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()) + else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()) { oa->write_character(to_char_type(0x1A)); - write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint32_t>(j.m_value.number_integer)); } else { oa->write_character(to_char_type(0x1B)); - write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint64_t>(j.m_value.number_integer)); } } else { // The conversions below encode the sign in the first // byte, and the value is converted to a positive number. - const auto positive_number = -1 - j.m_data.m_value.number_integer; - if (j.m_data.m_value.number_integer >= -24) + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) { write_number(static_cast<std::uint8_t>(0x20 + positive_number)); } @@ -15134,52 +15086,52 @@ class binary_writer case value_t::number_unsigned: { - if (j.m_data.m_value.number_unsigned <= 0x17) + if (j.m_value.number_unsigned <= 0x17) { - write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_unsigned)); + write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) { oa->write_character(to_char_type(0x18)); - write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_unsigned)); + write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) { oa->write_character(to_char_type(0x19)); - write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_unsigned)); + write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) { oa->write_character(to_char_type(0x1A)); - write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_unsigned)); + write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned)); } else { oa->write_character(to_char_type(0x1B)); - write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned)); + write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned)); } break; } case value_t::number_float: { - if (std::isnan(j.m_data.m_value.number_float)) + if (std::isnan(j.m_value.number_float)) { // NaN is 0xf97e00 in CBOR oa->write_character(to_char_type(0xF9)); oa->write_character(to_char_type(0x7E)); oa->write_character(to_char_type(0x00)); } - else if (std::isinf(j.m_data.m_value.number_float)) + else if (std::isinf(j.m_value.number_float)) { // Infinity is 0xf97c00, -Infinity is 0xf9fc00 oa->write_character(to_char_type(0xf9)); - oa->write_character(j.m_data.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); oa->write_character(to_char_type(0x00)); } else { - write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::cbor); + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); } break; } @@ -15187,7 +15139,7 @@ class binary_writer case value_t::string: { // step 1: write control byte and the string length - const auto N = j.m_data.m_value.string->size(); + const auto N = j.m_value.string->size(); if (N <= 0x17) { write_number(static_cast<std::uint8_t>(0x60 + N)); @@ -15217,15 +15169,15 @@ class binary_writer // step 2: write the string oa->write_characters( - reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()), - j.m_data.m_value.string->size()); + reinterpret_cast<const CharType*>(j.m_value.string->c_str()), + j.m_value.string->size()); break; } case value_t::array: { // step 1: write control byte and the array size - const auto N = j.m_data.m_value.array->size(); + const auto N = j.m_value.array->size(); if (N <= 0x17) { write_number(static_cast<std::uint8_t>(0x80 + N)); @@ -15254,7 +15206,7 @@ class binary_writer // LCOV_EXCL_STOP // step 2: write each element - for (const auto& el : *j.m_data.m_value.array) + for (const auto& el : *j.m_value.array) { write_cbor(el); } @@ -15263,32 +15215,32 @@ class binary_writer case value_t::binary: { - if (j.m_data.m_value.binary->has_subtype()) + if (j.m_value.binary->has_subtype()) { - if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)()) + if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)()) { write_number(static_cast<std::uint8_t>(0xd8)); - write_number(static_cast<std::uint8_t>(j.m_data.m_value.binary->subtype())); + write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype())); } - else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)()) + else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)()) { write_number(static_cast<std::uint8_t>(0xd9)); - write_number(static_cast<std::uint16_t>(j.m_data.m_value.binary->subtype())); + write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype())); } - else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)()) + else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)()) { write_number(static_cast<std::uint8_t>(0xda)); - write_number(static_cast<std::uint32_t>(j.m_data.m_value.binary->subtype())); + write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype())); } - else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)()) + else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)()) { write_number(static_cast<std::uint8_t>(0xdb)); - write_number(static_cast<std::uint64_t>(j.m_data.m_value.binary->subtype())); + write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype())); } } // step 1: write control byte and the binary array size - const auto N = j.m_data.m_value.binary->size(); + const auto N = j.m_value.binary->size(); if (N <= 0x17) { write_number(static_cast<std::uint8_t>(0x40 + N)); @@ -15318,7 +15270,7 @@ class binary_writer // step 2: write each element oa->write_characters( - reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()), + reinterpret_cast<const CharType*>(j.m_value.binary->data()), N); break; @@ -15327,7 +15279,7 @@ class binary_writer case value_t::object: { // step 1: write control byte and the object size - const auto N = j.m_data.m_value.object->size(); + const auto N = j.m_value.object->size(); if (N <= 0x17) { write_number(static_cast<std::uint8_t>(0xA0 + N)); @@ -15356,7 +15308,7 @@ class binary_writer // LCOV_EXCL_STOP // step 2: write each element - for (const auto& el : *j.m_data.m_value.object) + for (const auto& el : *j.m_value.object) { write_cbor(el.first); write_cbor(el.second); @@ -15385,7 +15337,7 @@ class binary_writer case value_t::boolean: // true and false { - oa->write_character(j.m_data.m_value.boolean + oa->write_character(j.m_value.boolean ? to_char_type(0xC3) : to_char_type(0xC2)); break; @@ -15393,75 +15345,75 @@ class binary_writer case value_t::number_integer: { - if (j.m_data.m_value.number_integer >= 0) + if (j.m_value.number_integer >= 0) { // MessagePack does not differentiate between positive // signed integers and unsigned integers. Therefore, we used // the code from the value_t::number_unsigned case here. - if (j.m_data.m_value.number_unsigned < 128) + if (j.m_value.number_unsigned < 128) { // positive fixnum - write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) { // uint 8 oa->write_character(to_char_type(0xCC)); - write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) { // uint 16 oa->write_character(to_char_type(0xCD)); - write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint16_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) { // uint 32 oa->write_character(to_char_type(0xCE)); - write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint32_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)()) { // uint 64 oa->write_character(to_char_type(0xCF)); - write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint64_t>(j.m_value.number_integer)); } } else { - if (j.m_data.m_value.number_integer >= -32) + if (j.m_value.number_integer >= -32) { // negative fixnum - write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::int8_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() && - j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) + else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() && + j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) { // int 8 oa->write_character(to_char_type(0xD0)); - write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::int8_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() && - j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) + else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() && + j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) { // int 16 oa->write_character(to_char_type(0xD1)); - write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::int16_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() && - j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) + else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() && + j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) { // int 32 oa->write_character(to_char_type(0xD2)); - write_number(static_cast<std::int32_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::int32_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() && - j.m_data.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)()) + else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() && + j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)()) { // int 64 oa->write_character(to_char_type(0xD3)); - write_number(static_cast<std::int64_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::int64_t>(j.m_value.number_integer)); } } break; @@ -15469,48 +15421,48 @@ class binary_writer case value_t::number_unsigned: { - if (j.m_data.m_value.number_unsigned < 128) + if (j.m_value.number_unsigned < 128) { // positive fixnum - write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)()) { // uint 8 oa->write_character(to_char_type(0xCC)); - write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint8_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)()) { // uint 16 oa->write_character(to_char_type(0xCD)); - write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint16_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)()) { // uint 32 oa->write_character(to_char_type(0xCE)); - write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint32_t>(j.m_value.number_integer)); } - else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)()) + else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)()) { // uint 64 oa->write_character(to_char_type(0xCF)); - write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer)); + write_number(static_cast<std::uint64_t>(j.m_value.number_integer)); } break; } case value_t::number_float: { - write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::msgpack); + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } case value_t::string: { // step 1: write control byte and the string length - const auto N = j.m_data.m_value.string->size(); + const auto N = j.m_value.string->size(); if (N <= 31) { // fixstr @@ -15537,15 +15489,15 @@ class binary_writer // step 2: write the string oa->write_characters( - reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()), - j.m_data.m_value.string->size()); + reinterpret_cast<const CharType*>(j.m_value.string->c_str()), + j.m_value.string->size()); break; } case value_t::array: { // step 1: write control byte and the array size - const auto N = j.m_data.m_value.array->size(); + const auto N = j.m_value.array->size(); if (N <= 15) { // fixarray @@ -15565,7 +15517,7 @@ class binary_writer } // step 2: write each element - for (const auto& el : *j.m_data.m_value.array) + for (const auto& el : *j.m_value.array) { write_msgpack(el); } @@ -15576,10 +15528,10 @@ class binary_writer { // step 0: determine if the binary type has a set subtype to // determine whether or not to use the ext or fixext types - const bool use_ext = j.m_data.m_value.binary->has_subtype(); + const bool use_ext = j.m_value.binary->has_subtype(); // step 1: write control byte and the byte string length - const auto N = j.m_data.m_value.binary->size(); + const auto N = j.m_value.binary->size(); if (N <= (std::numeric_limits<std::uint8_t>::max)()) { std::uint8_t output_type{}; @@ -15624,18 +15576,18 @@ class binary_writer } else if (N <= (std::numeric_limits<std::uint16_t>::max)()) { - const std::uint8_t output_type = use_ext - ? 0xC8 // ext 16 - : 0xC5; // bin 16 + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 oa->write_character(to_char_type(output_type)); write_number(static_cast<std::uint16_t>(N)); } else if (N <= (std::numeric_limits<std::uint32_t>::max)()) { - const std::uint8_t output_type = use_ext - ? 0xC9 // ext 32 - : 0xC6; // bin 32 + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 oa->write_character(to_char_type(output_type)); write_number(static_cast<std::uint32_t>(N)); @@ -15644,12 +15596,12 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype())); + write_number(static_cast<std::int8_t>(j.m_value.binary->subtype())); } // step 2: write the byte string oa->write_characters( - reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()), + reinterpret_cast<const CharType*>(j.m_value.binary->data()), N); break; @@ -15658,7 +15610,7 @@ class binary_writer case value_t::object: { // step 1: write control byte and the object size - const auto N = j.m_data.m_value.object->size(); + const auto N = j.m_value.object->size(); if (N <= 15) { // fixmap @@ -15678,7 +15630,7 @@ class binary_writer } // step 2: write each element - for (const auto& el : *j.m_data.m_value.object) + for (const auto& el : *j.m_value.object) { write_msgpack(el.first); write_msgpack(el.second); @@ -15718,7 +15670,7 @@ class binary_writer { if (add_prefix) { - oa->write_character(j.m_data.m_value.boolean + oa->write_character(j.m_value.boolean ? to_char_type('T') : to_char_type('F')); } @@ -15727,19 +15679,19 @@ class binary_writer case value_t::number_integer: { - write_number_with_ubjson_prefix(j.m_data.m_value.number_integer, add_prefix, use_bjdata); + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix, use_bjdata); break; } case value_t::number_unsigned: { - write_number_with_ubjson_prefix(j.m_data.m_value.number_unsigned, add_prefix, use_bjdata); + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix, use_bjdata); break; } case value_t::number_float: { - write_number_with_ubjson_prefix(j.m_data.m_value.number_float, add_prefix, use_bjdata); + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix, use_bjdata); break; } @@ -15749,10 +15701,10 @@ class binary_writer { oa->write_character(to_char_type('S')); } - write_number_with_ubjson_prefix(j.m_data.m_value.string->size(), true, use_bjdata); + write_number_with_ubjson_prefix(j.m_value.string->size(), true, use_bjdata); oa->write_characters( - reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()), - j.m_data.m_value.string->size()); + reinterpret_cast<const CharType*>(j.m_value.string->c_str()), + j.m_value.string->size()); break; } @@ -15764,7 +15716,7 @@ class binary_writer } bool prefix_required = true; - if (use_type && !j.m_data.m_value.array->empty()) + if (use_type && !j.m_value.array->empty()) { JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); @@ -15787,10 +15739,10 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_data.m_value.array->size(), true, use_bjdata); + write_number_with_ubjson_prefix(j.m_value.array->size(), true, use_bjdata); } - for (const auto& el : *j.m_data.m_value.array) + for (const auto& el : *j.m_value.array) { write_ubjson(el, use_count, use_type, prefix_required, use_bjdata); } @@ -15810,7 +15762,7 @@ class binary_writer oa->write_character(to_char_type('[')); } - if (use_type && !j.m_data.m_value.binary->empty()) + if (use_type && !j.m_value.binary->empty()) { JSON_ASSERT(use_count); oa->write_character(to_char_type('$')); @@ -15820,21 +15772,21 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_data.m_value.binary->size(), true, use_bjdata); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true, use_bjdata); } if (use_type) { oa->write_characters( - reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()), - j.m_data.m_value.binary->size()); + reinterpret_cast<const CharType*>(j.m_value.binary->data()), + j.m_value.binary->size()); } else { - for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i) + for (size_t i = 0; i < j.m_value.binary->size(); ++i) { oa->write_character(to_char_type('U')); - oa->write_character(j.m_data.m_value.binary->data()[i]); + oa->write_character(j.m_value.binary->data()[i]); } } @@ -15848,9 +15800,9 @@ class binary_writer case value_t::object: { - if (use_bjdata && j.m_data.m_value.object->size() == 3 && j.m_data.m_value.object->find("_ArrayType_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArraySize_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArrayData_") != j.m_data.m_value.object->end()) + if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find("_ArrayType_") != j.m_value.object->end() && j.m_value.object->find("_ArraySize_") != j.m_value.object->end() && j.m_value.object->find("_ArrayData_") != j.m_value.object->end()) { - if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) + if (!write_bjdata_ndarray(*j.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) { break; } @@ -15862,7 +15814,7 @@ class binary_writer } bool prefix_required = true; - if (use_type && !j.m_data.m_value.object->empty()) + if (use_type && !j.m_value.object->empty()) { JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); @@ -15885,10 +15837,10 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_data.m_value.object->size(), true, use_bjdata); + write_number_with_ubjson_prefix(j.m_value.object->size(), true, use_bjdata); } - for (const auto& el : *j.m_data.m_value.object) + for (const auto& el : *j.m_value.object) { write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata); oa->write_characters( @@ -16038,19 +15990,19 @@ class binary_writer void write_bson_unsigned(const string_t& name, const BasicJsonType& j) { - if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) { write_bson_entry_header(name, 0x10 /* int32 */); - write_number<std::int32_t>(static_cast<std::int32_t>(j.m_data.m_value.number_unsigned), true); + write_number<std::int32_t>(static_cast<std::int32_t>(j.m_value.number_unsigned), true); } - else if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)())) + else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)())) { write_bson_entry_header(name, 0x12 /* int64 */); - write_number<std::int64_t>(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned), true); + write_number<std::int64_t>(static_cast<std::int64_t>(j.m_value.number_unsigned), true); } else { - JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j)); + JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j)); } } @@ -16131,13 +16083,13 @@ class binary_writer switch (j.type()) { case value_t::object: - return header_size + calc_bson_object_size(*j.m_data.m_value.object); + return header_size + calc_bson_object_size(*j.m_value.object); case value_t::array: - return header_size + calc_bson_array_size(*j.m_data.m_value.array); + return header_size + calc_bson_array_size(*j.m_value.array); case value_t::binary: - return header_size + calc_bson_binary_size(*j.m_data.m_value.binary); + return header_size + calc_bson_binary_size(*j.m_value.binary); case value_t::boolean: return header_size + 1ul; @@ -16146,13 +16098,13 @@ class binary_writer return header_size + 8ul; case value_t::number_integer: - return header_size + calc_bson_integer_size(j.m_data.m_value.number_integer); + return header_size + calc_bson_integer_size(j.m_value.number_integer); case value_t::number_unsigned: - return header_size + calc_bson_unsigned_size(j.m_data.m_value.number_unsigned); + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); case value_t::string: - return header_size + calc_bson_string_size(*j.m_data.m_value.string); + return header_size + calc_bson_string_size(*j.m_value.string); case value_t::null: return header_size + 0ul; @@ -16178,28 +16130,28 @@ class binary_writer switch (j.type()) { case value_t::object: - return write_bson_object_entry(name, *j.m_data.m_value.object); + return write_bson_object_entry(name, *j.m_value.object); case value_t::array: - return write_bson_array(name, *j.m_data.m_value.array); + return write_bson_array(name, *j.m_value.array); case value_t::binary: - return write_bson_binary(name, *j.m_data.m_value.binary); + return write_bson_binary(name, *j.m_value.binary); case value_t::boolean: - return write_bson_boolean(name, j.m_data.m_value.boolean); + return write_bson_boolean(name, j.m_value.boolean); case value_t::number_float: - return write_bson_double(name, j.m_data.m_value.number_float); + return write_bson_double(name, j.m_value.number_float); case value_t::number_integer: - return write_bson_integer(name, j.m_data.m_value.number_integer); + return write_bson_integer(name, j.m_value.number_integer); case value_t::number_unsigned: return write_bson_unsigned(name, j); case value_t::string: - return write_bson_string(name, *j.m_data.m_value.string); + return write_bson_string(name, *j.m_value.string); case value_t::null: return write_bson_null(name); @@ -16221,8 +16173,8 @@ class binary_writer */ static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) { - const std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0), - [](size_t result, const typename BasicJsonType::object_t::value_type & el) + std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) { return result += calc_bson_element_size(el.first, el.second); }); @@ -16472,35 +16424,35 @@ class binary_writer return 'Z'; case value_t::boolean: - return j.m_data.m_value.boolean ? 'T' : 'F'; + return j.m_value.boolean ? 'T' : 'F'; case value_t::number_integer: { - if ((std::numeric_limits<std::int8_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) + if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)()) { return 'i'; } - if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) + if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)()) { return 'U'; } - if ((std::numeric_limits<std::int16_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) + if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)()) { return 'I'; } - if (use_bjdata && ((std::numeric_limits<std::uint16_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())) + if (use_bjdata && ((std::numeric_limits<std::uint16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())) { return 'u'; } - if ((std::numeric_limits<std::int32_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) + if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)()) { return 'l'; } - if (use_bjdata && ((std::numeric_limits<std::uint32_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())) + if (use_bjdata && ((std::numeric_limits<std::uint32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())) { return 'm'; } - if ((std::numeric_limits<std::int64_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)()) + if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)()) { return 'L'; } @@ -16510,35 +16462,35 @@ class binary_writer case value_t::number_unsigned: { - if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)())) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)())) { return 'i'; } - if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)())) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)())) { return 'U'; } - if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)())) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)())) { return 'I'; } - if (use_bjdata && j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)())) + if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)())) { return 'u'; } - if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)())) { return 'l'; } - if (use_bjdata && j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)())) + if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)())) { return 'm'; } - if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)())) + if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)())) { return 'L'; } - if (use_bjdata && j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)()) + if (use_bjdata && j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)()) { return 'M'; } @@ -16547,7 +16499,7 @@ class binary_writer } case value_t::number_float: - return get_ubjson_float_prefix(j.m_data.m_value.number_float); + return get_ubjson_float_prefix(j.m_value.number_float); case value_t::string: return 'S'; @@ -16596,7 +16548,7 @@ class binary_writer std::size_t len = (value.at(key).empty() ? 0 : 1); for (const auto& el : value.at(key)) { - len *= static_cast<std::size_t>(el.m_data.m_value.number_unsigned); + len *= static_cast<std::size_t>(el.m_value.number_unsigned); } key = "_ArrayData_"; @@ -16618,70 +16570,70 @@ class binary_writer { for (const auto& el : value.at(key)) { - write_number(static_cast<std::uint8_t>(el.m_data.m_value.number_unsigned), true); + write_number(static_cast<std::uint8_t>(el.m_value.number_unsigned), true); } } else if (dtype == 'i') { for (const auto& el : value.at(key)) { - write_number(static_cast<std::int8_t>(el.m_data.m_value.number_integer), true); + write_number(static_cast<std::int8_t>(el.m_value.number_integer), true); } } else if (dtype == 'u') { for (const auto& el : value.at(key)) { - write_number(static_cast<std::uint16_t>(el.m_data.m_value.number_unsigned), true); + write_number(static_cast<std::uint16_t>(el.m_value.number_unsigned), true); } } else if (dtype == 'I') { for (const auto& el : value.at(key)) { - write_number(static_cast<std::int16_t>(el.m_data.m_value.number_integer), true); + write_number(static_cast<std::int16_t>(el.m_value.number_integer), true); } } else if (dtype == 'm') { for (const auto& el : value.at(key)) { - write_number(static_cast<std::uint32_t>(el.m_data.m_value.number_unsigned), true); + write_number(static_cast<std::uint32_t>(el.m_value.number_unsigned), true); } } else if (dtype == 'l') { for (const auto& el : value.at(key)) { - write_number(static_cast<std::int32_t>(el.m_data.m_value.number_integer), true); + write_number(static_cast<std::int32_t>(el.m_value.number_integer), true); } } else if (dtype == 'M') { for (const auto& el : value.at(key)) { - write_number(static_cast<std::uint64_t>(el.m_data.m_value.number_unsigned), true); + write_number(static_cast<std::uint64_t>(el.m_value.number_unsigned), true); } } else if (dtype == 'L') { for (const auto& el : value.at(key)) { - write_number(static_cast<std::int64_t>(el.m_data.m_value.number_integer), true); + write_number(static_cast<std::int64_t>(el.m_value.number_integer), true); } } else if (dtype == 'd') { for (const auto& el : value.at(key)) { - write_number(static_cast<float>(el.m_data.m_value.number_float), true); + write_number(static_cast<float>(el.m_value.number_float), true); } } else if (dtype == 'D') { for (const auto& el : value.at(key)) { - write_number(static_cast<double>(el.m_data.m_value.number_float), true); + write_number(static_cast<double>(el.m_value.number_float), true); } } return false; @@ -17740,7 +17692,7 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) // NB: If the neighbors are computed for single-precision numbers, there is a single float // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision // value is off by 1 ulp. -#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if) +#if 0 const boundaries w = compute_boundaries(static_cast<double>(value)); #else const boundaries w = compute_boundaries(value); @@ -18042,11 +17994,11 @@ class serializer const unsigned int indent_step, const unsigned int current_indent = 0) { - switch (val.m_data.m_type) + switch (val.m_type) { case value_t::object: { - if (val.m_data.m_value.object->empty()) + if (val.m_value.object->empty()) { o->write_characters("{}", 2); return; @@ -18064,8 +18016,8 @@ class serializer } // first n-1 elements - auto i = val.m_data.m_value.object->cbegin(); - for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i) + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); @@ -18076,8 +18028,8 @@ class serializer } // last element - JSON_ASSERT(i != val.m_data.m_value.object->cend()); - JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); @@ -18093,8 +18045,8 @@ class serializer o->write_character('{'); // first n-1 elements - auto i = val.m_data.m_value.object->cbegin(); - for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i) + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { o->write_character('\"'); dump_escaped(i->first, ensure_ascii); @@ -18104,8 +18056,8 @@ class serializer } // last element - JSON_ASSERT(i != val.m_data.m_value.object->cend()); - JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); @@ -18119,7 +18071,7 @@ class serializer case value_t::array: { - if (val.m_data.m_value.array->empty()) + if (val.m_value.array->empty()) { o->write_characters("[]", 2); return; @@ -18137,8 +18089,8 @@ class serializer } // first n-1 elements - for (auto i = val.m_data.m_value.array->cbegin(); - i != val.m_data.m_value.array->cend() - 1; ++i) + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) { o->write_characters(indent_string.c_str(), new_indent); dump(*i, true, ensure_ascii, indent_step, new_indent); @@ -18146,9 +18098,9 @@ class serializer } // last element - JSON_ASSERT(!val.m_data.m_value.array->empty()); + JSON_ASSERT(!val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); - dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -18159,16 +18111,16 @@ class serializer o->write_character('['); // first n-1 elements - for (auto i = val.m_data.m_value.array->cbegin(); - i != val.m_data.m_value.array->cend() - 1; ++i) + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) { dump(*i, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element - JSON_ASSERT(!val.m_data.m_value.array->empty()); - dump(val.m_data.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + JSON_ASSERT(!val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); } @@ -18179,7 +18131,7 @@ class serializer case value_t::string: { o->write_character('\"'); - dump_escaped(*val.m_data.m_value.string, ensure_ascii); + dump_escaped(*val.m_value.string, ensure_ascii); o->write_character('\"'); return; } @@ -18201,24 +18153,24 @@ class serializer o->write_characters("\"bytes\": [", 10); - if (!val.m_data.m_value.binary->empty()) + if (!val.m_value.binary->empty()) { - for (auto i = val.m_data.m_value.binary->cbegin(); - i != val.m_data.m_value.binary->cend() - 1; ++i) + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) { dump_integer(*i); o->write_characters(", ", 2); } - dump_integer(val.m_data.m_value.binary->back()); + dump_integer(val.m_value.binary->back()); } o->write_characters("],\n", 3); o->write_characters(indent_string.c_str(), new_indent); o->write_characters("\"subtype\": ", 11); - if (val.m_data.m_value.binary->has_subtype()) + if (val.m_value.binary->has_subtype()) { - dump_integer(val.m_data.m_value.binary->subtype()); + dump_integer(val.m_value.binary->subtype()); } else { @@ -18232,21 +18184,21 @@ class serializer { o->write_characters("{\"bytes\":[", 10); - if (!val.m_data.m_value.binary->empty()) + if (!val.m_value.binary->empty()) { - for (auto i = val.m_data.m_value.binary->cbegin(); - i != val.m_data.m_value.binary->cend() - 1; ++i) + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) { dump_integer(*i); o->write_character(','); } - dump_integer(val.m_data.m_value.binary->back()); + dump_integer(val.m_value.binary->back()); } o->write_characters("],\"subtype\":", 12); - if (val.m_data.m_value.binary->has_subtype()) + if (val.m_value.binary->has_subtype()) { - dump_integer(val.m_data.m_value.binary->subtype()); + dump_integer(val.m_value.binary->subtype()); o->write_character('}'); } else @@ -18259,7 +18211,7 @@ class serializer case value_t::boolean: { - if (val.m_data.m_value.boolean) + if (val.m_value.boolean) { o->write_characters("true", 4); } @@ -18272,19 +18224,19 @@ class serializer case value_t::number_integer: { - dump_integer(val.m_data.m_value.number_integer); + dump_integer(val.m_value.number_integer); return; } case value_t::number_unsigned: { - dump_integer(val.m_data.m_value.number_unsigned); + dump_integer(val.m_value.number_unsigned); return; } case value_t::number_float: { - dump_float(val.m_data.m_value.number_float); + dump_float(val.m_value.number_float); return; } @@ -18858,8 +18810,8 @@ class serializer ? (byte & 0x3fu) | (codep << 6u) : (0xFFu >> type) & (byte); - const std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type); - JSON_ASSERT(index < utf8d.size()); + std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type); + JSON_ASSERT(index < 400); state = utf8d[index]; return state; } @@ -19046,7 +18998,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>, template<class KeyType, detail::enable_if_t< detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> - T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) + T & at(KeyType && key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -19074,7 +19026,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>, template<class KeyType, detail::enable_if_t< detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> - const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) + const T & at(KeyType && key) const { for (auto it = this->begin(); it != this->end(); ++it) { @@ -19108,7 +19060,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>, template<class KeyType, detail::enable_if_t< detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> - size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) + size_type erase(KeyType && key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -19199,7 +19151,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>, template<class KeyType, detail::enable_if_t< detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> - size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward) + size_type count(KeyType && key) const { for (auto it = this->begin(); it != this->end(); ++it) { @@ -19225,7 +19177,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>, template<class KeyType, detail::enable_if_t< detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0> - iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward) + iterator find(KeyType && key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -19288,9 +19240,7 @@ NLOHMANN_JSON_NAMESPACE_END #if defined(JSON_HAS_CPP_17) - #if JSON_HAS_STATIC_RTTI - #include <any> - #endif + #include <any> #include <string_view> #endif @@ -19321,7 +19271,6 @@ The invariants are checked by member function assert_invariant(). */ NLOHMANN_BASIC_JSON_TPL_DECLARATION class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) - : public ::nlohmann::detail::json_base_class<CustomBaseClass> { private: template<detail::value_t> friend struct detail::external_constructor; @@ -19348,7 +19297,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; - using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>; JSON_PRIVATE_UNLESS_TESTED: // convenience aliases for types residing in namespace detail; @@ -19420,6 +19368,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} + ///////////////////// // container types // ///////////////////// @@ -19461,6 +19410,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} + /// @brief returns the allocator associated with the container /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/ static allocator_type get_allocator() @@ -19523,6 +19473,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; #endif + #if defined(_MSVC_LANG) result["compiler"]["c++"] = std::to_string(_MSVC_LANG); #elif defined(__cplusplus) @@ -19533,6 +19484,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return result; } + /////////////////////////// // JSON value data types // /////////////////////////// @@ -19779,16 +19731,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec void destroy(value_t t) { - if ( - (t == value_t::object && object == nullptr) || - (t == value_t::array && array == nullptr) || - (t == value_t::string && string == nullptr) || - (t == value_t::binary && binary == nullptr) - ) - { - //not initialized (e.g. due to exception in the ctor) - return; - } if (t == value_t::array || t == value_t::object) { // flatten the current json_value to a heap-allocated stack @@ -19819,18 +19761,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // its children to the stack to be processed later if (current_item.is_array()) { - std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack)); + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack)); - current_item.m_data.m_value.array->clear(); + current_item.m_value.array->clear(); } else if (current_item.is_object()) { - for (auto&& it : *current_item.m_data.m_value.object) + for (auto&& it : *current_item.m_value.object) { stack.push_back(std::move(it.second)); } - current_item.m_data.m_value.object->clear(); + current_item.m_value.object->clear(); } // it's now safe that current_item get destructed @@ -19907,10 +19849,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec */ void assert_invariant(bool check_parents = true) const noexcept { - JSON_ASSERT(m_data.m_type != value_t::object || m_data.m_value.object != nullptr); - JSON_ASSERT(m_data.m_type != value_t::array || m_data.m_value.array != nullptr); - JSON_ASSERT(m_data.m_type != value_t::string || m_data.m_value.string != nullptr); - JSON_ASSERT(m_data.m_type != value_t::binary || m_data.m_value.binary != nullptr); + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); #if JSON_DIAGNOSTICS JSON_TRY @@ -19929,11 +19871,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec void set_parents() { #if JSON_DIAGNOSTICS - switch (m_data.m_type) + switch (m_type) { case value_t::array: { - for (auto& element : *m_data.m_value.array) + for (auto& element : *m_value.array) { element.m_parent = this; } @@ -19942,7 +19884,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::object: { - for (auto& element : *m_data.m_value.object) + for (auto& element : *m_value.object) { element.second.m_parent = this; } @@ -19983,7 +19925,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { // see https://github.com/nlohmann/json/issues/2838 JSON_ASSERT(type() == value_t::array); - if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity)) + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) { // capacity has changed: update all parents set_parents(); @@ -20039,7 +19981,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief create an empty value with a given type /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(const value_t v) - : m_data(v) + : m_type(v), m_value(v) { assert_invariant(); } @@ -20113,12 +20055,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec *this = nullptr; break; case value_t::discarded: - m_data.m_type = value_t::discarded; + m_type = value_t::discarded; break; default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } - JSON_ASSERT(m_data.m_type == val.type()); + JSON_ASSERT(m_type == val.type()); set_parents(); assert_invariant(); } @@ -20134,10 +20076,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec bool is_an_object = std::all_of(init.begin(), init.end(), [](const detail::json_ref<basic_json>& element_ref) { - // The cast is to ensure op[size_type] is called, bearing in mind size_type may not be int; - // (many string types can be constructed from 0 via its null-pointer guise, so we get a - // broken call to op[key_type], the wrong semantics and a 4804 warning on Windows) - return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[static_cast<size_type>(0)].is_string(); + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); }); // adjust type if type deduction is not wanted @@ -20159,22 +20098,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_an_object) { // the initializer list is a list of pairs -> create object - m_data.m_type = value_t::object; - m_data.m_value = value_t::object; + m_type = value_t::object; + m_value = value_t::object; for (auto& element_ref : init) { auto element = element_ref.moved_or_copied(); - m_data.m_value.object->emplace( - std::move(*((*element.m_data.m_value.array)[0].m_data.m_value.string)), - std::move((*element.m_data.m_value.array)[1])); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); } } else { // the initializer list describes an array -> create array - m_data.m_type = value_t::array; - m_data.m_value.array = create<array_t>(init.begin(), init.end()); + m_type = value_t::array; + m_value.array = create<array_t>(init.begin(), init.end()); } set_parents(); @@ -20187,8 +20126,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static basic_json binary(const typename binary_t::container_type& init) { auto res = basic_json(); - res.m_data.m_type = value_t::binary; - res.m_data.m_value = init; + res.m_type = value_t::binary; + res.m_value = init; return res; } @@ -20198,8 +20137,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) { auto res = basic_json(); - res.m_data.m_type = value_t::binary; - res.m_data.m_value = binary_t(init, subtype); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); return res; } @@ -20209,8 +20148,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static basic_json binary(typename binary_t::container_type&& init) { auto res = basic_json(); - res.m_data.m_type = value_t::binary; - res.m_data.m_value = std::move(init); + res.m_type = value_t::binary; + res.m_value = std::move(init); return res; } @@ -20220,8 +20159,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) { auto res = basic_json(); - res.m_data.m_type = value_t::binary; - res.m_data.m_value = binary_t(std::move(init), subtype); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); return res; } @@ -20243,9 +20182,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief construct an array with count copies of given value /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ - basic_json(size_type cnt, const basic_json& val): - m_data{cnt, val} + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) { + m_value.array = create<array_t>(cnt, val); set_parents(); assert_invariant(); } @@ -20267,10 +20207,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } // copy type from first iterator - m_data.m_type = first.m_object->m_data.m_type; + m_type = first.m_object->m_type; // check if iterator range is complete for primitive values - switch (m_data.m_type) + switch (m_type) { case value_t::boolean: case value_t::number_float: @@ -20295,55 +20235,55 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec break; } - switch (m_data.m_type) + switch (m_type) { case value_t::number_integer: { - m_data.m_value.number_integer = first.m_object->m_data.m_value.number_integer; + m_value.number_integer = first.m_object->m_value.number_integer; break; } case value_t::number_unsigned: { - m_data.m_value.number_unsigned = first.m_object->m_data.m_value.number_unsigned; + m_value.number_unsigned = first.m_object->m_value.number_unsigned; break; } case value_t::number_float: { - m_data.m_value.number_float = first.m_object->m_data.m_value.number_float; + m_value.number_float = first.m_object->m_value.number_float; break; } case value_t::boolean: { - m_data.m_value.boolean = first.m_object->m_data.m_value.boolean; + m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { - m_data.m_value = *first.m_object->m_data.m_value.string; + m_value = *first.m_object->m_value.string; break; } case value_t::object: { - m_data.m_value.object = create<object_t>(first.m_it.object_iterator, - last.m_it.object_iterator); + m_value.object = create<object_t>(first.m_it.object_iterator, + last.m_it.object_iterator); break; } case value_t::array: { - m_data.m_value.array = create<array_t>(first.m_it.array_iterator, - last.m_it.array_iterator); + m_value.array = create<array_t>(first.m_it.array_iterator, + last.m_it.array_iterator); break; } case value_t::binary: { - m_data.m_value = *first.m_object->m_data.m_value.binary; + m_value = *first.m_object->m_value.binary; break; } @@ -20357,6 +20297,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } + /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// @@ -20369,59 +20310,58 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief copy constructor /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(const basic_json& other) - : json_base_class_t(other) + : m_type(other.m_type) { - m_data.m_type = other.m_data.m_type; // check of passed value is valid other.assert_invariant(); - switch (m_data.m_type) + switch (m_type) { case value_t::object: { - m_data.m_value = *other.m_data.m_value.object; + m_value = *other.m_value.object; break; } case value_t::array: { - m_data.m_value = *other.m_data.m_value.array; + m_value = *other.m_value.array; break; } case value_t::string: { - m_data.m_value = *other.m_data.m_value.string; + m_value = *other.m_value.string; break; } case value_t::boolean: { - m_data.m_value = other.m_data.m_value.boolean; + m_value = other.m_value.boolean; break; } case value_t::number_integer: { - m_data.m_value = other.m_data.m_value.number_integer; + m_value = other.m_value.number_integer; break; } case value_t::number_unsigned: { - m_data.m_value = other.m_data.m_value.number_unsigned; + m_value = other.m_value.number_unsigned; break; } case value_t::number_float: { - m_data.m_value = other.m_data.m_value.number_float; + m_value = other.m_value.number_float; break; } case value_t::binary: { - m_data.m_value = *other.m_data.m_value.binary; + m_value = *other.m_value.binary; break; } @@ -20438,15 +20378,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief move constructor /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(basic_json&& other) noexcept - : json_base_class_t(std::forward<json_base_class_t>(other)), - m_data(std::move(other.m_data)) + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) { // check that passed value is valid other.assert_invariant(false); // invalidate payload - other.m_data.m_type = value_t::null; - other.m_data.m_value = {}; + other.m_type = value_t::null; + other.m_value = {}; set_parents(); assert_invariant(); @@ -20458,17 +20398,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec std::is_nothrow_move_constructible<value_t>::value&& std::is_nothrow_move_assignable<value_t>::value&& std::is_nothrow_move_constructible<json_value>::value&& - std::is_nothrow_move_assignable<json_value>::value&& - std::is_nothrow_move_assignable<json_base_class_t>::value + std::is_nothrow_move_assignable<json_value>::value ) { // check that passed value is valid other.assert_invariant(); using std::swap; - swap(m_data.m_type, other.m_data.m_type); - swap(m_data.m_value, other.m_data.m_value); - json_base_class_t::operator=(std::move(other)); + swap(m_type, other.m_type); + swap(m_value, other.m_value); set_parents(); assert_invariant(); @@ -20480,6 +20418,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec ~basic_json() noexcept { assert_invariant(false); + m_value.destroy(m_type); } /// @} @@ -20519,7 +20458,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/type/ constexpr value_t type() const noexcept { - return m_data.m_type; + return m_type; } /// @brief return whether type is primitive @@ -20540,14 +20479,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/is_null/ constexpr bool is_null() const noexcept { - return m_data.m_type == value_t::null; + return m_type == value_t::null; } /// @brief return whether value is a boolean /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ constexpr bool is_boolean() const noexcept { - return m_data.m_type == value_t::boolean; + return m_type == value_t::boolean; } /// @brief return whether value is a number @@ -20561,63 +20500,63 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ constexpr bool is_number_integer() const noexcept { - return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned; + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; } /// @brief return whether value is an unsigned integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ constexpr bool is_number_unsigned() const noexcept { - return m_data.m_type == value_t::number_unsigned; + return m_type == value_t::number_unsigned; } /// @brief return whether value is a floating-point number /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ constexpr bool is_number_float() const noexcept { - return m_data.m_type == value_t::number_float; + return m_type == value_t::number_float; } /// @brief return whether value is an object /// @sa https://json.nlohmann.me/api/basic_json/is_object/ constexpr bool is_object() const noexcept { - return m_data.m_type == value_t::object; + return m_type == value_t::object; } /// @brief return whether value is an array /// @sa https://json.nlohmann.me/api/basic_json/is_array/ constexpr bool is_array() const noexcept { - return m_data.m_type == value_t::array; + return m_type == value_t::array; } /// @brief return whether value is a string /// @sa https://json.nlohmann.me/api/basic_json/is_string/ constexpr bool is_string() const noexcept { - return m_data.m_type == value_t::string; + return m_type == value_t::string; } /// @brief return whether value is a binary array /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ constexpr bool is_binary() const noexcept { - return m_data.m_type == value_t::binary; + return m_type == value_t::binary; } /// @brief return whether value is discarded /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ constexpr bool is_discarded() const noexcept { - return m_data.m_type == value_t::discarded; + return m_type == value_t::discarded; } /// @brief return the type of the JSON value (implicit) /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/ constexpr operator value_t() const noexcept { - return m_data.m_type; + return m_type; } /// @} @@ -20632,7 +20571,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (JSON_HEDLEY_LIKELY(is_boolean())) { - return m_data.m_value.boolean; + return m_value.boolean; } JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this)); @@ -20641,97 +20580,97 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// get a pointer to the value (object) object_t* get_impl_ptr(object_t* /*unused*/) noexcept { - return is_object() ? m_data.m_value.object : nullptr; + return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (object) constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept { - return is_object() ? m_data.m_value.object : nullptr; + return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (array) array_t* get_impl_ptr(array_t* /*unused*/) noexcept { - return is_array() ? m_data.m_value.array : nullptr; + return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (array) constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept { - return is_array() ? m_data.m_value.array : nullptr; + return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (string) string_t* get_impl_ptr(string_t* /*unused*/) noexcept { - return is_string() ? m_data.m_value.string : nullptr; + return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (string) constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept { - return is_string() ? m_data.m_value.string : nullptr; + return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (boolean) boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept { - return is_boolean() ? &m_data.m_value.boolean : nullptr; + return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (boolean) constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept { - return is_boolean() ? &m_data.m_value.boolean : nullptr; + return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (integer number) number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { - return is_number_integer() ? &m_data.m_value.number_integer : nullptr; + return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { - return is_number_integer() ? &m_data.m_value.number_integer : nullptr; + return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (unsigned number) number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept { - return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr; + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (unsigned number) constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept { - return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr; + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (floating-point number) number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept { - return is_number_float() ? &m_data.m_value.number_float : nullptr; + return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (floating-point number) constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { - return is_number_float() ? &m_data.m_value.number_float : nullptr; + return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (binary) binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept { - return is_binary() ? m_data.m_value.binary : nullptr; + return is_binary() ? m_value.binary : nullptr; } /// get a pointer to the value (binary) constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept { - return is_binary() ? m_data.m_value.binary : nullptr; + return is_binary() ? m_value.binary : nullptr; } /*! @@ -21114,7 +21053,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) detail::negation<std::is_same<ValueType, std::string_view>>, #endif -#if defined(JSON_HAS_CPP_17) && JSON_HAS_STATIC_RTTI +#if defined(JSON_HAS_CPP_17) detail::negation<std::is_same<ValueType, std::any>>, #endif detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType> @@ -21151,6 +21090,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} + //////////////////// // element access // //////////////////// @@ -21168,7 +21108,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { JSON_TRY { - return set_parent(m_data.m_value.array->at(idx)); + return set_parent(m_value.array->at(idx)); } JSON_CATCH (std::out_of_range&) { @@ -21191,7 +21131,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { JSON_TRY { - return m_data.m_value.array->at(idx); + return m_value.array->at(idx); } JSON_CATCH (std::out_of_range&) { @@ -21215,8 +21155,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } - auto it = m_data.m_value.object->find(key); - if (it == m_data.m_value.object->end()) + auto it = m_value.object->find(key); + if (it == m_value.object->end()) { JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); } @@ -21235,8 +21175,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } - auto it = m_data.m_value.object->find(std::forward<KeyType>(key)); - if (it == m_data.m_value.object->end()) + auto it = m_value.object->find(std::forward<KeyType>(key)); + if (it == m_value.object->end()) { JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this)); } @@ -21253,8 +21193,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } - auto it = m_data.m_value.object->find(key); - if (it == m_data.m_value.object->end()) + auto it = m_value.object->find(key); + if (it == m_value.object->end()) { JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); } @@ -21273,8 +21213,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } - auto it = m_data.m_value.object->find(std::forward<KeyType>(key)); - if (it == m_data.m_value.object->end()) + auto it = m_value.object->find(std::forward<KeyType>(key)); + if (it == m_value.object->end()) { JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this)); } @@ -21288,8 +21228,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // implicitly convert null value to an empty array if (is_null()) { - m_data.m_type = value_t::array; - m_data.m_value.array = create<array_t>(); + m_type = value_t::array; + m_value.array = create<array_t>(); assert_invariant(); } @@ -21297,17 +21237,17 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_LIKELY(is_array())) { // fill up array with null values if given idx is outside range - if (idx >= m_data.m_value.array->size()) + if (idx >= m_value.array->size()) { #if JSON_DIAGNOSTICS // remember array size & capacity before resizing - const auto old_size = m_data.m_value.array->size(); - const auto old_capacity = m_data.m_value.array->capacity(); + const auto old_size = m_value.array->size(); + const auto old_capacity = m_value.array->capacity(); #endif - m_data.m_value.array->resize(idx + 1); + m_value.array->resize(idx + 1); #if JSON_DIAGNOSTICS - if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity)) + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) { // capacity has changed: update all parents set_parents(); @@ -21321,7 +21261,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - return m_data.m_value.array->operator[](idx); + return m_value.array->operator[](idx); } JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); @@ -21334,7 +21274,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // const operator[] only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { - return m_data.m_value.array->operator[](idx); + return m_value.array->operator[](idx); } JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); @@ -21347,15 +21287,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // implicitly convert null value to an empty object if (is_null()) { - m_data.m_type = value_t::object; - m_data.m_value.object = create<object_t>(); + m_type = value_t::object; + m_value.object = create<object_t>(); assert_invariant(); } // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - auto result = m_data.m_value.object->emplace(std::move(key), nullptr); + auto result = m_value.object->emplace(std::move(key), nullptr); return set_parent(result.first->second); } @@ -21369,8 +21309,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // const operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - auto it = m_data.m_value.object->find(key); - JSON_ASSERT(it != m_data.m_value.object->end()); + auto it = m_value.object->find(key); + JSON_ASSERT(it != m_value.object->end()); return it->second; } @@ -21400,15 +21340,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // implicitly convert null value to an empty object if (is_null()) { - m_data.m_type = value_t::object; - m_data.m_value.object = create<object_t>(); + m_type = value_t::object; + m_value.object = create<object_t>(); assert_invariant(); } // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - auto result = m_data.m_value.object->emplace(std::forward<KeyType>(key), nullptr); + auto result = m_value.object->emplace(std::forward<KeyType>(key), nullptr); return set_parent(result.first->second); } @@ -21424,8 +21364,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // const operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - auto it = m_data.m_value.object->find(std::forward<KeyType>(key)); - JSON_ASSERT(it != m_data.m_value.object->end()); + auto it = m_value.object->find(std::forward<KeyType>(key)); + JSON_ASSERT(it != m_value.object->end()); return it->second; } @@ -21662,7 +21602,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec IteratorType result = end(); - switch (m_data.m_type) + switch (m_type) { case value_t::boolean: case value_t::number_float: @@ -21679,32 +21619,32 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_string()) { AllocatorType<string_t> alloc; - std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string); - std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1); - m_data.m_value.string = nullptr; + std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string); + std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; } else if (is_binary()) { AllocatorType<binary_t> alloc; - std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary); - std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1); - m_data.m_value.binary = nullptr; + std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary); + std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; } - m_data.m_type = value_t::null; + m_type = value_t::null; assert_invariant(); break; } case value_t::object: { - result.m_it.object_iterator = m_data.m_value.object->erase(pos.m_it.object_iterator); + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); break; } case value_t::array: { - result.m_it.array_iterator = m_data.m_value.array->erase(pos.m_it.array_iterator); + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); break; } @@ -21732,7 +21672,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec IteratorType result = end(); - switch (m_data.m_type) + switch (m_type) { case value_t::boolean: case value_t::number_float: @@ -21750,33 +21690,33 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_string()) { AllocatorType<string_t> alloc; - std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string); - std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1); - m_data.m_value.string = nullptr; + std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string); + std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; } else if (is_binary()) { AllocatorType<binary_t> alloc; - std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary); - std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1); - m_data.m_value.binary = nullptr; + std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary); + std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; } - m_data.m_type = value_t::null; + m_type = value_t::null; assert_invariant(); break; } case value_t::object: { - result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator, + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); break; } case value_t::array: { - result.m_it.array_iterator = m_data.m_value.array->erase(first.m_it.array_iterator, + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); break; } @@ -21801,7 +21741,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } - return m_data.m_value.object->erase(std::forward<KeyType>(key)); + return m_value.object->erase(std::forward<KeyType>(key)); } template < typename KeyType, detail::enable_if_t < @@ -21814,10 +21754,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } - const auto it = m_data.m_value.object->find(std::forward<KeyType>(key)); - if (it != m_data.m_value.object->end()) + const auto it = m_value.object->find(std::forward<KeyType>(key)); + if (it != m_value.object->end()) { - m_data.m_value.object->erase(it); + m_value.object->erase(it); return 1; } return 0; @@ -21855,7 +21795,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); } - m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx)); + m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx)); } else { @@ -21865,6 +21805,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} + //////////// // lookup // //////////// @@ -21880,7 +21821,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_object()) { - result.m_it.object_iterator = m_data.m_value.object->find(key); + result.m_it.object_iterator = m_value.object->find(key); } return result; @@ -21894,7 +21835,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_object()) { - result.m_it.object_iterator = m_data.m_value.object->find(key); + result.m_it.object_iterator = m_value.object->find(key); } return result; @@ -21910,7 +21851,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_object()) { - result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key)); + result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key)); } return result; @@ -21926,7 +21867,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_object()) { - result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key)); + result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key)); } return result; @@ -21937,7 +21878,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec size_type count(const typename object_t::key_type& key) const { // return 0 for all nonobject types - return is_object() ? m_data.m_value.object->count(key) : 0; + return is_object() ? m_value.object->count(key) : 0; } /// @brief returns the number of occurrences of a key in a JSON object @@ -21947,14 +21888,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec size_type count(KeyType && key) const { // return 0 for all nonobject types - return is_object() ? m_data.m_value.object->count(std::forward<KeyType>(key)) : 0; + return is_object() ? m_value.object->count(std::forward<KeyType>(key)) : 0; } /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ bool contains(const typename object_t::key_type& key) const { - return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end(); + return is_object() && m_value.object->find(key) != m_value.object->end(); } /// @brief check the existence of an element in a JSON object @@ -21963,7 +21904,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0> bool contains(KeyType && key) const { - return is_object() && m_data.m_value.object->find(std::forward<KeyType>(key)) != m_data.m_value.object->end(); + return is_object() && m_value.object->find(std::forward<KeyType>(key)) != m_value.object->end(); } /// @brief check the existence of an element in a JSON object given a JSON pointer @@ -21982,6 +21923,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} + /////////////// // iterators // /////////////// @@ -22120,6 +22062,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} + ////////////// // capacity // ////////////// @@ -22131,7 +22074,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/empty/ bool empty() const noexcept { - switch (m_data.m_type) + switch (m_type) { case value_t::null: { @@ -22142,13 +22085,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::array: { // delegate call to array_t::empty() - return m_data.m_value.array->empty(); + return m_value.array->empty(); } case value_t::object: { // delegate call to object_t::empty() - return m_data.m_value.object->empty(); + return m_value.object->empty(); } case value_t::string: @@ -22170,7 +22113,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/size/ size_type size() const noexcept { - switch (m_data.m_type) + switch (m_type) { case value_t::null: { @@ -22181,13 +22124,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::array: { // delegate call to array_t::size() - return m_data.m_value.array->size(); + return m_value.array->size(); } case value_t::object: { // delegate call to object_t::size() - return m_data.m_value.object->size(); + return m_value.object->size(); } case value_t::string: @@ -22209,18 +22152,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/max_size/ size_type max_size() const noexcept { - switch (m_data.m_type) + switch (m_type) { case value_t::array: { // delegate call to array_t::max_size() - return m_data.m_value.array->max_size(); + return m_value.array->max_size(); } case value_t::object: { // delegate call to object_t::max_size() - return m_data.m_value.object->max_size(); + return m_value.object->max_size(); } case value_t::null: @@ -22241,6 +22184,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} + /////////////// // modifiers // /////////////// @@ -22252,53 +22196,53 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/clear/ void clear() noexcept { - switch (m_data.m_type) + switch (m_type) { case value_t::number_integer: { - m_data.m_value.number_integer = 0; + m_value.number_integer = 0; break; } case value_t::number_unsigned: { - m_data.m_value.number_unsigned = 0; + m_value.number_unsigned = 0; break; } case value_t::number_float: { - m_data.m_value.number_float = 0.0; + m_value.number_float = 0.0; break; } case value_t::boolean: { - m_data.m_value.boolean = false; + m_value.boolean = false; break; } case value_t::string: { - m_data.m_value.string->clear(); + m_value.string->clear(); break; } case value_t::binary: { - m_data.m_value.binary->clear(); + m_value.binary->clear(); break; } case value_t::array: { - m_data.m_value.array->clear(); + m_value.array->clear(); break; } case value_t::object: { - m_data.m_value.object->clear(); + m_value.object->clear(); break; } @@ -22322,15 +22266,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // transform null object into an array if (is_null()) { - m_data.m_type = value_t::array; - m_data.m_value = value_t::array; + m_type = value_t::array; + m_value = value_t::array; assert_invariant(); } // add element to array (move semantics) - const auto old_capacity = m_data.m_value.array->capacity(); - m_data.m_value.array->push_back(std::move(val)); - set_parent(m_data.m_value.array->back(), old_capacity); + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(std::move(val)); + set_parent(m_value.array->back(), old_capacity); // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor } @@ -22355,15 +22299,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // transform null object into an array if (is_null()) { - m_data.m_type = value_t::array; - m_data.m_value = value_t::array; + m_type = value_t::array; + m_value = value_t::array; assert_invariant(); } // add element to array - const auto old_capacity = m_data.m_value.array->capacity(); - m_data.m_value.array->push_back(val); - set_parent(m_data.m_value.array->back(), old_capacity); + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(val); + set_parent(m_value.array->back(), old_capacity); } /// @brief add an object to an array @@ -22387,13 +22331,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // transform null object into an object if (is_null()) { - m_data.m_type = value_t::object; - m_data.m_value = value_t::object; + m_type = value_t::object; + m_value = value_t::object; assert_invariant(); } // add element to object - auto res = m_data.m_value.object->insert(val); + auto res = m_value.object->insert(val); set_parent(res.first->second); } @@ -22443,15 +22387,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // transform null object into an array if (is_null()) { - m_data.m_type = value_t::array; - m_data.m_value = value_t::array; + m_type = value_t::array; + m_value = value_t::array; assert_invariant(); } // add element to array (perfect forwarding) - const auto old_capacity = m_data.m_value.array->capacity(); - m_data.m_value.array->emplace_back(std::forward<Args>(args)...); - return set_parent(m_data.m_value.array->back(), old_capacity); + const auto old_capacity = m_value.array->capacity(); + m_value.array->emplace_back(std::forward<Args>(args)...); + return set_parent(m_value.array->back(), old_capacity); } /// @brief add an object to an object if key does not exist @@ -22468,13 +22412,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // transform null object into an object if (is_null()) { - m_data.m_type = value_t::object; - m_data.m_value = value_t::object; + m_type = value_t::object; + m_value = value_t::object; assert_invariant(); } // add element to array (perfect forwarding) - auto res = m_data.m_value.object->emplace(std::forward<Args>(args)...); + auto res = m_value.object->emplace(std::forward<Args>(args)...); set_parent(res.first->second); // create result iterator and set iterator to the result of emplace @@ -22492,14 +22436,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec iterator insert_iterator(const_iterator pos, Args&& ... args) { iterator result(this); - JSON_ASSERT(m_data.m_value.array != nullptr); + JSON_ASSERT(m_value.array != nullptr); - auto insert_pos = std::distance(m_data.m_value.array->begin(), pos.m_it.array_iterator); - m_data.m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...); - result.m_it.array_iterator = m_data.m_value.array->begin() + insert_pos; + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; // This could have been written as: - // result.m_it.array_iterator = m_data.m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); // but the return value of insert is missing in GCC 4.8, so it is written this way instead. set_parents(); @@ -22626,7 +22570,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this)); } - m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); } /// @brief updates a JSON object from another object, overwriting existing keys @@ -22643,8 +22587,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // implicitly convert null value to an empty object if (is_null()) { - m_data.m_type = value_t::object; - m_data.m_value.object = create<object_t>(); + m_type = value_t::object; + m_value.object = create<object_t>(); assert_invariant(); } @@ -22669,16 +22613,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (merge_objects && it.value().is_object()) { - auto it2 = m_data.m_value.object->find(it.key()); - if (it2 != m_data.m_value.object->end()) + auto it2 = m_value.object->find(it.key()); + if (it2 != m_value.object->end()) { it2->second.update(it.value(), true); continue; } } - m_data.m_value.object->operator[](it.key()) = it.value(); + m_value.object->operator[](it.key()) = it.value(); #if JSON_DIAGNOSTICS - m_data.m_value.object->operator[](it.key()).m_parent = this; + m_value.object->operator[](it.key()).m_parent = this; #endif } } @@ -22688,12 +22632,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec void swap(reference other) noexcept ( std::is_nothrow_move_constructible<value_t>::value&& std::is_nothrow_move_assignable<value_t>::value&& - std::is_nothrow_move_constructible<json_value>::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + std::is_nothrow_move_constructible<json_value>::value&& std::is_nothrow_move_assignable<json_value>::value ) { - std::swap(m_data.m_type, other.m_data.m_type); - std::swap(m_data.m_value, other.m_data.m_value); + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); set_parents(); other.set_parents(); @@ -22705,7 +22649,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec friend void swap(reference left, reference right) noexcept ( std::is_nothrow_move_constructible<value_t>::value&& std::is_nothrow_move_assignable<value_t>::value&& - std::is_nothrow_move_constructible<json_value>::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + std::is_nothrow_move_constructible<json_value>::value&& std::is_nothrow_move_assignable<json_value>::value ) { @@ -22714,13 +22658,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ - void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + void swap(array_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) { using std::swap; - swap(*(m_data.m_value.array), other); + swap(*(m_value.array), other); } else { @@ -22730,13 +22674,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ - void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + void swap(object_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { using std::swap; - swap(*(m_data.m_value.object), other); + swap(*(m_value.object), other); } else { @@ -22746,13 +22690,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ - void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + void swap(string_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for strings if (JSON_HEDLEY_LIKELY(is_string())) { using std::swap; - swap(*(m_data.m_value.string), other); + swap(*(m_value.string), other); } else { @@ -22762,13 +22706,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ - void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + void swap(binary_t& other) // NOLINT(bugprone-exception-escape) { // swap only works for strings if (JSON_HEDLEY_LIKELY(is_binary())) { using std::swap; - swap(*(m_data.m_value.binary), other); + swap(*(m_value.binary), other); } else { @@ -22784,7 +22728,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_LIKELY(is_binary())) { using std::swap; - swap(*(m_data.m_value.binary), other); + swap(*(m_value.binary), other); } else { @@ -22812,31 +22756,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec switch (lhs_type) \ { \ case value_t::array: \ - return (*lhs.m_data.m_value.array) op (*rhs.m_data.m_value.array); \ + return (*lhs.m_value.array) op (*rhs.m_value.array); \ \ case value_t::object: \ - return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \ + return (*lhs.m_value.object) op (*rhs.m_value.object); \ \ case value_t::null: \ return (null_result); \ \ case value_t::string: \ - return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \ + return (*lhs.m_value.string) op (*rhs.m_value.string); \ \ case value_t::boolean: \ - return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean); \ + return (lhs.m_value.boolean) op (rhs.m_value.boolean); \ \ case value_t::number_integer: \ - return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer); \ + return (lhs.m_value.number_integer) op (rhs.m_value.number_integer); \ \ case value_t::number_unsigned: \ - return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned); \ + return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned); \ \ case value_t::number_float: \ - return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float); \ + return (lhs.m_value.number_float) op (rhs.m_value.number_float); \ \ case value_t::binary: \ - return (*lhs.m_data.m_value.binary) op (*rhs.m_data.m_value.binary); \ + return (*lhs.m_value.binary) op (*rhs.m_value.binary); \ \ case value_t::discarded: \ default: \ @@ -22845,27 +22789,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } \ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) \ { \ - return static_cast<number_float_t>(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_float; \ + return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float; \ } \ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) \ { \ - return lhs.m_data.m_value.number_float op static_cast<number_float_t>(rhs.m_data.m_value.number_integer); \ + return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer); \ } \ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) \ { \ - return static_cast<number_float_t>(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_float; \ + return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float; \ } \ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) \ { \ - return lhs.m_data.m_value.number_float op static_cast<number_float_t>(rhs.m_data.m_value.number_unsigned); \ + return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned); \ } \ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) \ { \ - return static_cast<number_integer_t>(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_integer; \ + return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \ } \ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) \ { \ - return lhs.m_data.m_value.number_integer op static_cast<number_integer_t>(rhs.m_data.m_value.number_unsigned); \ + return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \ } \ else if(compares_unordered(lhs, rhs))\ {\ @@ -22882,8 +22826,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // an operation is computed as an odd number of inverses of others static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept { - if ((lhs.is_number_float() && std::isnan(lhs.m_data.m_value.number_float) && rhs.is_number()) - || (rhs.is_number_float() && std::isnan(rhs.m_data.m_value.number_float) && lhs.is_number())) + if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number()) + || (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number())) { return true; } @@ -23227,6 +23171,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #endif // JSON_NO_IO /// @} + ///////////////////// // deserialization // ///////////////////// @@ -23383,7 +23328,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_HEDLEY_RETURNS_NON_NULL const char* type_name() const noexcept { - switch (m_data.m_type) + switch (m_type) { case value_t::null: return "null"; @@ -23407,43 +23352,17 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } } + JSON_PRIVATE_UNLESS_TESTED: ////////////////////// // member variables // ////////////////////// - struct data - { - /// the type of the current element - value_t m_type = value_t::null; - - /// the value of the current element - json_value m_value = {}; - - data(const value_t v) - : m_type(v), m_value(v) - { - } - - data(size_type cnt, const basic_json& val) - : m_type(value_t::array) - { - m_value.array = create<array_t>(cnt, val); - } - - data() noexcept = default; - data(data&&) noexcept = default; - data(const data&) noexcept = delete; - data& operator=(data&&) noexcept = delete; - data& operator=(const data&) noexcept = delete; - - ~data() noexcept - { - m_value.destroy(m_type); - } - }; + /// the type of the current element + value_t m_type = value_t::null; - data m_data = {}; + /// the value of the current element + json_value m_value = {}; #if JSON_DIAGNOSTICS /// a pointer to a parent value (for debugging purposes) @@ -23624,6 +23543,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); } + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) static basic_json from_cbor(detail::span_input_adapter&& i, @@ -23747,6 +23667,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return res ? result : basic_json(value_t::discarded); } + /// @brief create a JSON value from an input in BJData format /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ template<typename InputType> @@ -23969,7 +23890,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } // make sure the top element of the pointer exists - json_pointer const top_pointer = ptr.top(); + json_pointer top_pointer = ptr.top(); if (top_pointer != ptr) { result.at(top_pointer); @@ -23981,7 +23902,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // parent must exist when performing patch add per RFC6902 specs basic_json& parent = result.at(ptr); - switch (parent.m_data.m_type) + switch (parent.m_type) { case value_t::null: case value_t::object: @@ -24027,7 +23948,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec }; // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [this, & result](json_pointer & ptr) + const auto operation_remove = [this, &result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); @@ -24070,13 +23991,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec bool string_type) -> basic_json & { // find value - auto it = val.m_data.m_value.object->find(member); + auto it = val.m_value.object->find(member); // context-sensitive error message const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\''); // check if desired value is present - if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end())) + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); @@ -24131,7 +24052,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec json_pointer from_ptr(from_path); // the "from" location must exist - use at() - basic_json const v = result.at(from_ptr); + basic_json v = result.at(from_ptr); // The move operation is functionally identical to a // "remove" operation on the "from" location, followed @@ -24148,7 +24069,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const json_pointer from_ptr(from_path); // the "from" location must exist - use at() - basic_json const v = result.at(from_ptr); + basic_json v = result.at(from_ptr); // The copy is functionally identical to an "add" // operation at the target location using the value @@ -24390,11 +24311,7 @@ inline namespace json_literals /// @brief user-defined string literal for JSON values /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ JSON_HEDLEY_NON_NULL(1) -#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json operator ""_json(const char* s, std::size_t n) -#else - inline nlohmann::json operator "" _json(const char* s, std::size_t n) -#endif +inline nlohmann::json operator "" _json(const char* s, std::size_t n) { return nlohmann::json::parse(s, s + n); } @@ -24402,11 +24319,7 @@ JSON_HEDLEY_NON_NULL(1) /// @brief user-defined string literal for JSON pointer /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ JSON_HEDLEY_NON_NULL(1) -#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json::json_pointer operator ""_json_pointer(const char* s, std::size_t n) -#else - inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) -#endif +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { return nlohmann::json::json_pointer(std::string(s, n)); } @@ -24425,7 +24338,7 @@ namespace std // NOLINT(cert-dcl58-cpp) /// @brief hash value for JSON objects /// @sa https://json.nlohmann.me/api/basic_json/std_hash/ NLOHMANN_BASIC_JSON_TPL_DECLARATION -struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp) +struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> { std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const { @@ -24458,8 +24371,8 @@ struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', /// @brief exchanges the values of two JSON objects /// @sa https://json.nlohmann.me/api/basic_json/std_swap/ NLOHMANN_BASIC_JSON_TPL_DECLARATION -inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp) - is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) +inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) + is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression) is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value) { j1.swap(j2); @@ -24470,13 +24383,8 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC } // namespace std #if JSON_USE_GLOBAL_UDLS - #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - using nlohmann::literals::json_literals::operator ""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) - using nlohmann::literals::json_literals::operator ""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) - #else - using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) - using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) - #endif + using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) #endif // #include <nlohmann/detail/macro_unscope.hpp> @@ -24520,7 +24428,6 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef JSON_HAS_THREE_WAY_COMPARISON #undef JSON_HAS_RANGES - #undef JSON_HAS_STATIC_RTTI #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON #endif diff --git a/app/src/main/cpp/libcxx b/app/src/main/cpp/libcxx new file mode 160000 index 0000000000000000000000000000000000000000..12c8f4e93f196a700137e983dcceeac43cf807f2 --- /dev/null +++ b/app/src/main/cpp/libcxx @@ -0,0 +1 @@ +Subproject commit 12c8f4e93f196a700137e983dcceeac43cf807f2 diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index b902e433aa091b8fa4bc0e89317202f5b19d28b3..034ad45f78379ac94c86ae6ea7f47ad459fdb3d1 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -4,15 +4,15 @@ #include <fstream> #include "zygisk.hpp" -#include "dobby.h" +#include "shadowhook.h" #include "json.hpp" +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__) + #define DEX_FILE_PATH "/data/adb/modules/playintegrityfix/classes.dex" #define PROP_FILE_PATH "/data/adb/modules/playintegrityfix/pif.json" -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__) - static std::string FIRST_API_LEVEL, SECURITY_PATCH; typedef void (*T_Callback)(void *, const char *, const char *, uint32_t); @@ -58,14 +58,39 @@ my_system_property_read_callback(const prop_info *pi, T_Callback callback, void } static void doHook() { - void *handle = DobbySymbolResolver("libc.so", "__system_property_read_callback"); + shadowhook_init(SHADOWHOOK_MODE_UNIQUE, false); + void *handle = shadowhook_hook_sym_name( + "libc.so", + "__system_property_read_callback", + reinterpret_cast<void *>(my_system_property_read_callback), + reinterpret_cast<void **>(&o_system_property_read_callback) + ); if (handle == nullptr) { LOGD("Couldn't find '__system_property_read_callback' handle. Report to @chiteroman"); return; } LOGD("Found '__system_property_read_callback' handle at %p", handle); - DobbyHook(handle, reinterpret_cast<void *>(my_system_property_read_callback), - reinterpret_cast<void **>(&o_system_property_read_callback)); +} + +static void sendVector(int fd, const std::vector<char> &vec) { + // Send the size of the vector + size_t size = vec.size(); + write(fd, &size, sizeof(size_t)); + + // Send the vector data + write(fd, vec.data(), size); +} + +static std::vector<char> receiveVector(int fd) { + // Receive the size of the vector + size_t size; + read(fd, &size, sizeof(size_t)); + + // Receive the vector data + std::vector<char> vec(size); + read(fd, vec.data(), size); + + return vec; } class PlayIntegrityFix : public zygisk::ModuleBase { @@ -99,47 +124,32 @@ public: return; } - ssize_t size; - char buffer[10000]; int fd = api->connectCompanion(); - size = read(fd, buffer, sizeof(buffer)); - - if (size > 0) { - moduleDex.insert(moduleDex.end(), buffer, buffer + size); - } else { - LOGD("Couldn't load classes.dex file in memory"); - close(fd); - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - return; - } - - lseek(fd, 0, SEEK_SET); - - size = read(fd, buffer, sizeof(buffer)); - - if (size > 0) { - jsonStr.insert(jsonStr.end(), buffer, buffer + size); - } else { - LOGD("Couldn't load pif.json file in memory"); - } + dexVector = receiveVector(fd); + propVector = receiveVector(fd); close(fd); - LOGD("Received 'classes.dex' file from socket: %d bytes", - static_cast<int>(moduleDex.size())); + LOGD("Read from file descriptor file 'classes.dex' -> %d bytes", + static_cast<int>(dexVector.size())); + LOGD("Read from file descriptor file 'pif.json' -> %d bytes", + static_cast<int>(propVector.size())); - LOGD("Received 'pif.json' file from socket: %d bytes", static_cast<int>(jsonStr.size())); + if (dexVector.empty() || propVector.empty()) api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); } void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { - if (moduleDex.empty()) return; + if (dexVector.empty() || propVector.empty()) return; readJson(); inject(); doHook(); + + dexVector.clear(); + propVector.clear(); } void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { @@ -149,11 +159,11 @@ public: private: zygisk::Api *api = nullptr; JNIEnv *env = nullptr; - std::vector<char> moduleDex; - std::string jsonStr; + std::vector<char> dexVector, propVector; void readJson() { - nlohmann::json json = nlohmann::json::parse(jsonStr, nullptr, false); + std::string data(propVector.cbegin(), propVector.cend()); + nlohmann::json json = nlohmann::json::parse(data, nullptr, false, true); auto getStringFromJson = [&json](const std::string &key) { return json.contains(key) && !json[key].is_null() ? json[key].get<std::string>() @@ -162,8 +172,6 @@ private: SECURITY_PATCH = getStringFromJson("SECURITY_PATCH"); FIRST_API_LEVEL = getStringFromJson("FIRST_API_LEVEL"); - - json.clear(); } void inject() { @@ -177,8 +185,8 @@ private: auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); auto dexClInit = env->GetMethodID(dexClClass, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); - auto buffer = env->NewDirectByteBuffer(moduleDex.data(), - static_cast<jlong>(moduleDex.size())); + auto buffer = env->NewDirectByteBuffer(dexVector.data(), + static_cast<jlong>(dexVector.size())); auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader); LOGD("load class"); @@ -192,15 +200,13 @@ private: LOGD("read json"); auto readProps = env->GetStaticMethodID(entryClass, "readJson", "(Ljava/lang/String;)V"); - auto javaStr = env->NewStringUTF(jsonStr.c_str()); + std::string data(propVector.cbegin(), propVector.cend()); + auto javaStr = env->NewStringUTF(data.c_str()); env->CallStaticVoidMethod(entryClass, readProps, javaStr); LOGD("call init"); auto entryInit = env->GetStaticMethodID(entryClass, "init", "()V"); env->CallStaticVoidMethod(entryClass, entryInit); - - moduleDex.clear(); - jsonStr.clear(); } }; @@ -210,13 +216,17 @@ static void companion(int fd) { std::vector<char> dexVector((std::istreambuf_iterator<char>(dex)), std::istreambuf_iterator<char>()); - std::vector<char> propVector((std::istreambuf_iterator<char>(prop)), std::istreambuf_iterator<char>()); - write(fd, dexVector.data(), dexVector.size()); - lseek(fd, 0, SEEK_SET); - write(fd, propVector.data(), propVector.size()); + dex.close(); + prop.close(); + + sendVector(fd, dexVector); + sendVector(fd, propVector); + + dexVector.clear(); + propVector.clear(); } REGISTER_ZYGISK_MODULE(PlayIntegrityFix) diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c new file mode 100644 index 0000000000000000000000000000000000000000..d71e4f88970a782a5fc038d98b99dc0537bd3dd6 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c @@ -0,0 +1,446 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_a32.h" + +#include <inttypes.h> +#include <sh_util.h> +#include <stdint.h> + +#include "sh_log.h" + +// https://developer.arm.com/documentation/ddi0406/latest +// https://developer.arm.com/documentation/ddi0597/latest + +typedef enum { + IGNORED = 0, + B_A1, + BX_A1, + BL_IMM_A1, + BLX_IMM_A2, + ADD_REG_A1, + ADD_REG_PC_A1, + SUB_REG_A1, + SUB_REG_PC_A1, + ADR_A1, + ADR_A2, + MOV_REG_A1, + MOV_REG_PC_A1, + LDR_LIT_A1, + LDR_LIT_PC_A1, + LDRB_LIT_A1, + LDRD_LIT_A1, + LDRH_LIT_A1, + LDRSB_LIT_A1, + LDRSH_LIT_A1, + LDR_REG_A1, + LDR_REG_PC_A1, + LDRB_REG_A1, + LDRD_REG_A1, + LDRH_REG_A1, + LDRSB_REG_A1, + LDRSH_REG_A1 +} sh_a32_type_t; + +static sh_a32_type_t sh_a32_get_type(uint32_t inst) { + if (((inst & 0x0F000000u) == 0x0A000000) && ((inst & 0xF0000000) != 0xF0000000)) + return B_A1; + else if (((inst & 0x0FFFFFFFu) == 0x012FFF1F) && ((inst & 0xF0000000) != 0xF0000000)) + return BX_A1; + else if (((inst & 0x0F000000u) == 0x0B000000) && ((inst & 0xF0000000) != 0xF0000000)) + return BL_IMM_A1; + else if ((inst & 0xFE000000) == 0xFA000000) + return BLX_IMM_A2; + else if (((inst & 0x0FE00010u) == 0x00800000) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) && + (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F))) + return ((inst & 0x0000F000u) == 0x0000F000) ? ADD_REG_PC_A1 : ADD_REG_A1; + else if (((inst & 0x0FE00010u) == 0x00400000) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) && + (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F))) + return ((inst & 0x0000F000u) == 0x0000F000) ? SUB_REG_PC_A1 : SUB_REG_A1; + else if (((inst & 0x0FFF0000u) == 0x028F0000) && ((inst & 0xF0000000) != 0xF0000000)) + return ADR_A1; + else if (((inst & 0x0FFF0000u) == 0x024F0000) && ((inst & 0xF0000000) != 0xF0000000)) + return ADR_A2; + else if (((inst & 0x0FEF001Fu) == 0x01A0000F) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x0010F000u) != 0x0010F000) && + (!(((inst & 0x0000F000u) == 0x0000F000) && ((inst & 0x00000FF0u) != 0x00000000)))) + return ((inst & 0x0000F000u) == 0x0000F000) ? MOV_REG_PC_A1 : MOV_REG_A1; + else if (((inst & 0x0F7F0000u) == 0x051F0000) && ((inst & 0xF0000000) != 0xF0000000)) + return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_A1 : LDR_LIT_A1; + else if (((inst & 0x0F7F0000u) == 0x055F0000) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRB_LIT_A1; + else if (((inst & 0x0F7F00F0u) == 0x014F00D0) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRD_LIT_A1; + else if (((inst & 0x0F7F00F0u) == 0x015F00B0) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRH_LIT_A1; + else if (((inst & 0x0F7F00F0u) == 0x015F00D0) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRSB_LIT_A1; + else if (((inst & 0x0F7F00F0u) == 0x015F00F0) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRSH_LIT_A1; + else if (((inst & 0x0E5F0010u) == 0x061F0000) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_REG_PC_A1 : LDR_REG_A1; + else if (((inst & 0x0E5F0010u) == 0x065F0000) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRB_REG_A1; + else if (((inst & 0x0E5F0FF0u) == 0x000F00D0) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRD_REG_A1; + else if (((inst & 0x0E5F0FF0u) == 0x001F00B0) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRH_REG_A1; + else if (((inst & 0x0E5F0FF0u) == 0x001F00D0) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRSB_REG_A1; + else if (((inst & 0x0E5F0FF0u) == 0x001F00F0) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRSH_REG_A1; + else + return IGNORED; +} + +size_t sh_a32_get_rewrite_inst_len(uint32_t inst) { + static uint8_t map[] = { + 4, // IGNORED + 12, // B_A1 + 12, // BX_A1 + 16, // BL_IMM_A1 + 16, // BLX_IMM_A2 + 32, // ADD_REG_A1 + 32, // ADD_REG_PC_A1 + 32, // SUB_REG_A1 + 32, // SUB_REG_PC_A1 + 12, // ADR_A1 + 12, // ADR_A2 + 32, // MOV_REG_A1 + 12, // MOV_REG_PC_A1 + 24, // LDR_LIT_A1 + 36, // LDR_LIT_PC_A1 + 24, // LDRB_LIT_A1 + 24, // LDRD_LIT_A1 + 24, // LDRH_LIT_A1 + 24, // LDRSB_LIT_A1 + 24, // LDRSH_LIT_A1 + 32, // LDR_REG_A1 + 36, // LDR_REG_PC_A1 + 32, // LDRB_REG_A1 + 32, // LDRD_REG_A1 + 32, // LDRH_REG_A1 + 32, // LDRSB_REG_A1 + 32 // LDRSH_REG_A1 + }; + + return (size_t)(map[sh_a32_get_type(inst)]); +} + +static bool sh_a32_is_addr_need_fix(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) { + return (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr); +} + +static uintptr_t sh_a32_fix_addr(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) { + if (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr) { + uintptr_t cursor_addr = rinfo->overwrite_start_addr; + size_t offset = 0; + for (size_t i = 0; i < rinfo->rewrite_inst_lens_cnt; i++) { + if (cursor_addr >= addr) break; + cursor_addr += 4; + offset += rinfo->rewrite_inst_lens[i]; + } + uintptr_t fixed_addr = (uintptr_t)rinfo->rewrite_buf + offset; + SH_LOG_INFO("a32 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr); + return fixed_addr; + } + + return addr; +} + +static size_t sh_a32_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, + sh_a32_rewrite_info_t *rinfo) { + uint32_t cond; + if (type == B_A1 || type == BL_IMM_A1 || type == BX_A1) + cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + else + // type == BLX_IMM_A2 + cond = 0xE; // 1110 None (AL) + + uint32_t addr; + if (type == B_A1 || type == BL_IMM_A1) { + uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0); + uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(imm24 << 2u, 26u); + addr = pc + imm32; // arm -> arm + } else if (type == BLX_IMM_A2) { + uint32_t h = SH_UTIL_GET_BIT_32(inst, 24); + uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0); + uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32((imm24 << 2u) | (h << 1u), 26u); + addr = SH_UTIL_SET_BIT0(pc + imm32); // arm -> thumb + } else { + // type == BX_A1 + // BX PC + // PC must be even, and the "arm" instruction must be at a 4-byte aligned address, + // so the instruction set must keep "arm" unchanged. + addr = pc; // arm -> arm + } + addr = sh_a32_fix_addr(addr, rinfo); + + size_t idx = 0; + if (type == BL_IMM_A1 || type == BLX_IMM_A2) { + buf[idx++] = 0x028FE008u | (cond << 28u); // ADD<c> LR, PC, #8 + } + buf[idx++] = 0x059FF000u | (cond << 28u); // LDR<c> PC, [PC, #0] + buf[idx++] = 0xEA000000; // B #0 + buf[idx++] = addr; + return idx * 4; // 12 or 16 +} + +static size_t sh_a32_rewrite_add_or_sub(uint32_t *buf, uint32_t inst, uintptr_t pc) { + // ADD{S}<c> <Rd>, <Rn>, PC{, <shift>} or ADD{S}<c> <Rd>, PC, <Rm>{, <shift>} + // SUB{S}<c> <Rd>, <Rn>, PC{, <shift>} or SUB{S}<c> <Rd>, PC, <Rm>{, <shift>} + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t rn = SH_UTIL_GET_BITS_32(inst, 19, 16); + uint32_t rm = SH_UTIL_GET_BITS_32(inst, 3, 0); + uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); + + uint32_t rx; // r0 - r3 + for (rx = 3;; --rx) + if (rx != rn && rx != rm && rx != rd) break; + + if (rd == 0xF) // Rd == PC + { + uint32_t ry; // r0 - r4 + for (ry = 4;; --ry) + if (ry != rn && ry != rm && ry != rd && ry != rx) break; + + buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0 + buf[1] = 0xEA000005; // B #20 + buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC} + buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] + if (rn == 0xF) + // Rn == PC + buf[4] = + (inst & 0x0FF00FFFu) | 0xE0000000 | (ry << 12u) | (rx << 16u); // ADD/SUB Ry, Rx, Rm{, <shift>} + else + // Rm == PC + buf[4] = (inst & 0x0FFF0FF0u) | 0xE0000000 | (ry << 12u) | rx; // ADD/SUB Ry, Rn, Rx{, <shift>} + buf[5] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8] + buf[6] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC} + buf[7] = pc; + return 32; + } else { + buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0 + buf[1] = 0xEA000005; // B #20 + buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} + buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] + if (rn == 0xF) + // Rn == PC + buf[4] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // ADD/SUB{S} Rd, Rx, Rm{, <shift>} + else + // Rm == PC + buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // ADD/SUB{S} Rd, Rn, Rx{, <shift>} + buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx} + buf[6] = 0xEA000000; // B #0 + buf[7] = pc; + return 32; + } +} + +static size_t sh_a32_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, + sh_a32_rewrite_info_t *rinfo) { + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); // r0 - r15 + uint32_t imm12 = SH_UTIL_GET_BITS_32(inst, 11, 0); + uint32_t imm32 = sh_util_arm_expand_imm(imm12); + uint32_t addr = (type == ADR_A1 ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32)); + if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + buf[0] = 0x059F0000u | (cond << 28u) | (rd << 12u); // LDR<c> Rd, [PC, #0] + buf[1] = 0xEA000000; // B #0 + buf[2] = addr; + return 12; +} + +static size_t sh_a32_rewrite_mov(uint32_t *buf, uint32_t inst, uintptr_t pc) { + // MOV{S}<c> <Rd>, PC + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); + uint32_t rx = (rd == 0) ? 1 : 0; + + if (rd == 0xF) // Rd == PC (MOV PC, PC) + { + buf[0] = 0x059FF000u | (cond << 28u); // LDR<c> PC, [PC, #0] + buf[1] = 0xEA000000; // B #0 + buf[2] = pc; + return 12; + } else { + buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0 + buf[1] = 0xEA000005; // B #20 + buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} + buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] + buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // MOV{S} Rd, Rx{, <shift> #<amount>/RRX} + buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx} + buf[6] = 0xEA000000; // B #0 + buf[7] = pc; + return 32; + } +} + +static size_t sh_a32_rewrite_ldr_lit(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, + sh_a32_rewrite_info_t *rinfo) { + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t u = SH_UTIL_GET_BIT_32(inst, 23); + uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12); + + uint32_t imm32; + if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1) + imm32 = SH_UTIL_GET_BITS_32(inst, 11, 0); + else + imm32 = (SH_UTIL_GET_BITS_32(inst, 11, 8) << 4u) + SH_UTIL_GET_BITS_32(inst, 3, 0); + uint32_t addr = (u ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32)); + if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + if (type == LDR_LIT_PC_A1 && rt == 0xF) { + // Rt == PC + buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0 + buf[1] = 0xEA000006; // B #24 + buf[2] = 0xE92D0003; // PUSH {R0, R1} + buf[3] = 0xE59F0000; // LDR R0, [PC, #0] + buf[4] = 0xEA000000; // B #0 + buf[5] = addr; // + buf[6] = 0xE5900000; // LDR R0, [R0] + buf[7] = 0xE58D0004; // STR R0, [SP, #4] + buf[8] = 0xE8BD8001; // POP {R0, PC} + return 36; + } else { + buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0 + buf[1] = 0xEA000003; // B #12 + buf[2] = 0xE59F0000 | (rt << 12u); // LDR Rt, [PC, #0] + buf[3] = 0xEA000000; // B #0 + buf[4] = addr; // +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch" + switch (type) { + case LDR_LIT_A1: + buf[5] = 0xE5900000 | (rt << 16u) | (rt << 12u); // LDR Rt, [Rt] + break; + case LDRB_LIT_A1: + buf[5] = 0xE5D00000 | (rt << 16u) | (rt << 12u); // LDRB Rt, [Rt] + break; + case LDRD_LIT_A1: + buf[5] = 0xE1C000D0 | (rt << 16u) | (rt << 12u); // LDRD Rt, [Rt] + break; + case LDRH_LIT_A1: + buf[5] = 0xE1D000B0 | (rt << 16u) | (rt << 12u); // LDRH Rt, [Rt] + break; + case LDRSB_LIT_A1: + buf[5] = 0xE1D000D0 | (rt << 16u) | (rt << 12u); // LDRSB Rt, [Rt] + break; + case LDRSH_LIT_A1: + buf[5] = 0xE1D000F0 | (rt << 16u) | (rt << 12u); // LDRSH Rt, [Rt] + break; + } +#pragma clang diagnostic pop + return 24; + } +} + +static size_t sh_a32_rewrite_ldr_reg(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type) { + // LDR<c> <Rt>, [PC,+/-<Rm>{, <shift>}]{!} + // ...... + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12); + uint32_t rt2 = rt + 1; + uint32_t rm = SH_UTIL_GET_BITS_16(inst, 3, 0); + uint32_t rx; // r0 - r3 + for (rx = 3;; --rx) + if (rx != rt && rx != rt2 && rx != rm) break; + + if (type == LDR_REG_PC_A1 && rt == 0xF) { + // Rt == PC + uint32_t ry; // r0 - r4 + for (ry = 4;; --ry) + if (ry != rt && ry != rt2 && ry != rm && ry != rx) break; + + buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0 + buf[1] = 0xEA000006; // B #24 + buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC} + buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #8] + buf[4] = 0xEA000000; // B #0 + buf[5] = pc; + buf[6] = + (inst & 0x0FF00FFFu) | 0xE0000000 | (rx << 16u) | (ry << 12u); // LDRxx Ry, [Rx],+/-Rm{, <shift>} + buf[7] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8] + buf[8] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC} + return 36; + } else { + buf[0] = 0x0A000000u | (cond << 28u); // B<c> #0 + buf[1] = 0xEA000005; // B #20 + buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} + buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #0] + buf[4] = 0xEA000000; // B #0 + buf[5] = pc; + buf[6] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // LDRxx Rt, [Rx],+/-Rm{, <shift>} + buf[7] = 0xE49D0004 | (rx << 12u); // POP {Rx} + return 32; + } +} + +size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo) { + sh_a32_type_t type = sh_a32_get_type(inst); + SH_LOG_INFO("a32 rewrite: type %d, inst %" PRIx32, type, inst); + + // We will only overwrite 4 to 8 bytes on A32, so PC cannot be in the coverage. + // In this case, the add/sub/mov/ldr_reg instruction does not need to consider + // the problem of PC in the coverage area when rewriting. + + if (type == B_A1 || type == BX_A1 || type == BL_IMM_A1 || type == BLX_IMM_A2) + return sh_a32_rewrite_b(buf, inst, pc, type, rinfo); + else if (type == ADD_REG_A1 || type == ADD_REG_PC_A1 || type == SUB_REG_A1 || type == SUB_REG_PC_A1) + return sh_a32_rewrite_add_or_sub(buf, inst, pc); + else if (type == ADR_A1 || type == ADR_A2) + return sh_a32_rewrite_adr(buf, inst, pc, type, rinfo); + else if (type == MOV_REG_A1 || type == MOV_REG_PC_A1) + return sh_a32_rewrite_mov(buf, inst, pc); + else if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1 || type == LDRD_LIT_A1 || + type == LDRH_LIT_A1 || type == LDRSB_LIT_A1 || type == LDRSH_LIT_A1) + return sh_a32_rewrite_ldr_lit(buf, inst, pc, type, rinfo); + else if (type == LDR_REG_A1 || type == LDR_REG_PC_A1 || type == LDRB_REG_A1 || type == LDRD_REG_A1 || + type == LDRH_REG_A1 || type == LDRSB_REG_A1 || type == LDRSH_REG_A1) + return sh_a32_rewrite_ldr_reg(buf, inst, pc, type); + else { + // IGNORED + buf[0] = inst; + return 4; + } +} + +size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr) { + buf[0] = 0xE51FF004; // LDR PC, [PC, #-4] + buf[1] = addr; + return 8; +} + +size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) { + buf[0] = 0xEA000000 | (((addr - pc) & 0x03FFFFFFu) >> 2u); // B <label> + return 4; +} diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.h b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.h new file mode 100644 index 0000000000000000000000000000000000000000..efe159209e8085261801c3a2bfb00b3213df9fa8 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.h @@ -0,0 +1,41 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stddef.h> +#include <stdint.h> + +typedef struct { + uintptr_t overwrite_start_addr; + uintptr_t overwrite_end_addr; + uint32_t *rewrite_buf; + size_t rewrite_buf_offset; + size_t rewrite_inst_lens[2]; + size_t rewrite_inst_lens_cnt; +} sh_a32_rewrite_info_t; + +size_t sh_a32_get_rewrite_inst_len(uint32_t inst); +size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo); + +size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr); +size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc); diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_inst.c b/app/src/main/cpp/shadowhook/arch/arm/sh_inst.c new file mode 100644 index 0000000000000000000000000000000000000000..9ca310fd22c8e18987fcbaecc79414456132a1a8 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_inst.c @@ -0,0 +1,523 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_inst.h" + +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include "sh_a32.h" +#include "sh_config.h" +#include "sh_enter.h" +#include "sh_exit.h" +#include "sh_log.h" +#include "sh_sig.h" +#include "sh_t16.h" +#include "sh_t32.h" +#include "sh_txx.h" +#include "sh_util.h" +#include "shadowhook.h" + +static void sh_inst_get_thumb_rewrite_info(sh_inst_t *self, uintptr_t target_addr, + sh_txx_rewrite_info_t *rinfo) { + memset(rinfo, 0, sizeof(sh_txx_rewrite_info_t)); + + size_t idx = 0; + uintptr_t target_addr_offset = 0; + uintptr_t pc = target_addr + 4; + size_t rewrite_len = 0; + + while (rewrite_len < self->backup_len) { + // IT block + sh_t16_it_t it; + if (sh_t16_parse_it(&it, *((uint16_t *)(target_addr + target_addr_offset)), pc)) { + rewrite_len += (2 + it.insts_len); + + size_t it_block_idx = idx++; + size_t it_block_len = 4 + 4; // IT-else + IT-then + for (size_t i = 0, j = 0; i < it.insts_cnt; i++) { + bool is_thumb32 = sh_util_is_thumb32((uintptr_t)(&(it.insts[j]))); + if (is_thumb32) { + it_block_len += sh_t32_get_rewrite_inst_len(it.insts[j], it.insts[j + 1]); + rinfo->inst_lens[idx++] = 0; + rinfo->inst_lens[idx++] = 0; + j += 2; + } else { + it_block_len += sh_t16_get_rewrite_inst_len(it.insts[j]); + rinfo->inst_lens[idx++] = 0; + j += 1; + } + } + rinfo->inst_lens[it_block_idx] = it_block_len; + + target_addr_offset += (2 + it.insts_len); + pc += (2 + it.insts_len); + } + // not IT block + else { + bool is_thumb32 = sh_util_is_thumb32(target_addr + target_addr_offset); + size_t inst_len = (is_thumb32 ? 4 : 2); + rewrite_len += inst_len; + + if (is_thumb32) { + rinfo->inst_lens[idx++] = + sh_t32_get_rewrite_inst_len(*((uint16_t *)(target_addr + target_addr_offset)), + *((uint16_t *)(target_addr + target_addr_offset + 2))); + rinfo->inst_lens[idx++] = 0; + } else + rinfo->inst_lens[idx++] = + sh_t16_get_rewrite_inst_len(*((uint16_t *)(target_addr + target_addr_offset))); + + target_addr_offset += inst_len; + pc += inst_len; + } + } + + rinfo->start_addr = target_addr; + rinfo->end_addr = target_addr + rewrite_len; + rinfo->buf = (uint16_t *)self->enter_addr; + rinfo->buf_offset = 0; + rinfo->inst_lens_cnt = idx; +} + +static int sh_inst_hook_thumb_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr, + uintptr_t *orig_addr2, size_t *rewrite_len) { + // backup original instructions (length: 4 or 8 or 10) + memcpy((void *)(self->backup), (void *)target_addr, self->backup_len); + + // package the information passed to rewrite + sh_txx_rewrite_info_t rinfo; + sh_inst_get_thumb_rewrite_info(self, target_addr, &rinfo); + + // backup and rewrite original instructions + uintptr_t target_addr_offset = 0; + uintptr_t pc = target_addr + 4; + *rewrite_len = 0; + while (*rewrite_len < self->backup_len) { + // IT block + sh_t16_it_t it; + if (sh_t16_parse_it(&it, *((uint16_t *)(target_addr + target_addr_offset)), pc)) { + *rewrite_len += (2 + it.insts_len); + + // save space holder point of IT-else B instruction + uintptr_t enter_inst_else_p = self->enter_addr + rinfo.buf_offset; + rinfo.buf_offset += 2; // B<c> <label> + rinfo.buf_offset += 2; // NOP + + // rewrite IT block + size_t enter_inst_else_len = 4; // B<c> + NOP + B + NOP + size_t enter_inst_then_len = 0; // B + NOP + uintptr_t enter_inst_then_p = 0; + for (size_t i = 0, j = 0; i < it.insts_cnt; i++) { + if (i == it.insts_else_cnt) { + // save space holder point of IT-then (for B instruction) + enter_inst_then_p = self->enter_addr + rinfo.buf_offset; + rinfo.buf_offset += 2; // B <label> + rinfo.buf_offset += 2; // NOP + + // fill IT-else B instruction + sh_t16_rewrite_it_else((uint16_t *)enter_inst_else_p, (uint16_t)enter_inst_else_len, &it); + } + + // rewrite instructions in IT block + bool is_thumb32 = sh_util_is_thumb32((uintptr_t)(&(it.insts[j]))); + size_t len; + if (is_thumb32) + len = sh_t32_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset), it.insts[j], + it.insts[j + 1], it.pcs[i], &rinfo); + else + len = sh_t16_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset), it.insts[j], it.pcs[i], + &rinfo); + if (0 == len) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED; + rinfo.buf_offset += len; + j += (is_thumb32 ? 2 : 1); + + // save the total offset for ELSE/THEN in enter + if (i < it.insts_else_cnt) + enter_inst_else_len += len; + else + enter_inst_then_len += len; + + if (i == it.insts_cnt - 1) { + // fill IT-then B instruction + sh_t16_rewrite_it_then((uint16_t *)enter_inst_then_p, (uint16_t)enter_inst_then_len); + } + } + + target_addr_offset += (2 + it.insts_len); + pc += (2 + it.insts_len); + } + // not IT block + else { + bool is_thumb32 = sh_util_is_thumb32(target_addr + target_addr_offset); + size_t inst_len = (is_thumb32 ? 4 : 2); + *rewrite_len += inst_len; + + // rewrite original instructions (fill in enter) + SH_LOG_INFO("thumb rewrite: offset %zu, pc %" PRIxPTR, rinfo.buf_offset, pc); + size_t len; + if (is_thumb32) + len = sh_t32_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset), + *((uint16_t *)(target_addr + target_addr_offset)), + *((uint16_t *)(target_addr + target_addr_offset + 2)), pc, &rinfo); + else + len = sh_t16_rewrite((uint16_t *)(self->enter_addr + rinfo.buf_offset), + *((uint16_t *)(target_addr + target_addr_offset)), pc, &rinfo); + if (0 == len) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED; + rinfo.buf_offset += len; + + target_addr_offset += inst_len; + pc += inst_len; + } + } + SH_LOG_INFO("thumb rewrite: len %zu to %zu", *rewrite_len, rinfo.buf_offset); + + // absolute jump back to remaining original instructions (fill in enter) + rinfo.buf_offset += sh_t32_absolute_jump((uint16_t *)(self->enter_addr + rinfo.buf_offset), true, + SH_UTIL_SET_BIT0(target_addr + *rewrite_len)); + sh_util_clear_cache(self->enter_addr, rinfo.buf_offset); + + // save original function address + if (NULL != orig_addr) __atomic_store_n(orig_addr, SH_UTIL_SET_BIT0(self->enter_addr), __ATOMIC_SEQ_CST); + if (NULL != orig_addr2) __atomic_store_n(orig_addr2, SH_UTIL_SET_BIT0(self->enter_addr), __ATOMIC_SEQ_CST); + return 0; +} + +#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED +static bool sh_inst_thumb_is_long_enough(uintptr_t target_addr, size_t overwrite_len, xdl_info_t *dlinfo) { + if (overwrite_len <= dlinfo->dli_ssize) return true; + + // check align-4 in the end of symbol + if ((overwrite_len == dlinfo->dli_ssize + 2) && ((target_addr + dlinfo->dli_ssize) % 4 == 2)) { + uintptr_t sym_end = target_addr + dlinfo->dli_ssize; + if (0 != sh_util_mprotect(sym_end, 2, PROT_READ | PROT_WRITE | PROT_EXEC)) return false; + + // should be zero-ed + if (0 != *((uint16_t *)sym_end)) return false; + + // should not belong to any symbol + void *dlcache = NULL; + xdl_info_t dlinfo2; + if (sh_util_get_api_level() >= __ANDROID_API_L__) { + xdl_addr((void *)SH_UTIL_SET_BIT0(sym_end), &dlinfo2, &dlcache); + } else { + SH_SIG_TRY(SIGSEGV, SIGBUS) { + xdl_addr((void *)SH_UTIL_SET_BIT0(sym_end), &dlinfo2, &dlcache); + } + SH_SIG_CATCH() { + memset(&dlinfo2, 0, sizeof(dlinfo2)); + SH_LOG_WARN("thumb detect tail aligned: crashed"); + } + SH_SIG_EXIT + } + xdl_addr_clean(&dlcache); + if (NULL != dlinfo2.dli_sname) return false; + + // trust here is useless alignment data + return true; + } + + return false; +} +#endif + +#ifdef SH_CONFIG_TRY_WITH_EXIT + +// B T4: [-16M, +16M - 2] +#define SH_INST_T32_B_RANGE_LOW (16777216) +#define SH_INST_T32_B_RANGE_HIGH (16777214) + +static int sh_inst_hook_thumb_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, + uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) { + int r; + target_addr = SH_UTIL_CLEAR_BIT0(target_addr); + uintptr_t pc = target_addr + 4; + self->backup_len = 4; + +#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED + if (!sh_inst_thumb_is_long_enough(target_addr, self->backup_len, dlinfo)) + return SHADOWHOOK_ERRNO_HOOK_SYMSZ; +#else + if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ; +#endif + + // alloc an exit for absolute jump + sh_t32_absolute_jump((uint16_t *)self->exit, true, new_addr); + if (0 != (r = sh_exit_alloc(&self->exit_addr, &self->exit_type, pc, dlinfo, (uint8_t *)(self->exit), + sizeof(self->exit), SH_INST_T32_B_RANGE_LOW, SH_INST_T32_B_RANGE_HIGH))) + return r; + + // rewrite + if (0 != sh_util_mprotect(target_addr, dlinfo->dli_ssize, PROT_READ | PROT_WRITE | PROT_EXEC)) { + r = SHADOWHOOK_ERRNO_MPROT; + goto err; + } + size_t rewrite_len = 0; + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = sh_inst_hook_thumb_rewrite(self, target_addr, orig_addr, orig_addr2, &rewrite_len); + } + SH_SIG_CATCH() { + r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH; + goto err; + } + SH_SIG_EXIT + if (0 != r) goto err; + + // relative jump to the exit by overwriting the head of original function + sh_t32_relative_jump((uint16_t *)self->trampo, self->exit_addr, pc); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err; + + SH_LOG_INFO("thumb: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR + " -> enter %" PRIxPTR " -> remaining %" PRIxPTR, + target_addr, self->exit_addr, new_addr, self->enter_addr, + SH_UTIL_SET_BIT0(target_addr + rewrite_len)); + return 0; + +err: + sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit)); + self->exit_addr = 0; // this is a flag for with-exit or without-exit + return r; +} +#endif + +static int sh_inst_hook_thumb_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, + uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) { + int r; + target_addr = SH_UTIL_CLEAR_BIT0(target_addr); + bool is_align4 = (0 == (target_addr % 4)); + self->backup_len = (is_align4 ? 8 : 10); + +#ifdef SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED + if (!sh_inst_thumb_is_long_enough(target_addr, self->backup_len, dlinfo)) + return SHADOWHOOK_ERRNO_HOOK_SYMSZ; +#else + if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ; +#endif + + // rewrite + if (0 != sh_util_mprotect(target_addr, dlinfo->dli_ssize, PROT_READ | PROT_WRITE | PROT_EXEC)) + return SHADOWHOOK_ERRNO_MPROT; + size_t rewrite_len = 0; + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = sh_inst_hook_thumb_rewrite(self, target_addr, orig_addr, orig_addr2, &rewrite_len); + } + SH_SIG_CATCH() { + return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH; + } + SH_SIG_EXIT + if (0 != r) return r; + + // absolute jump to new function address by overwriting the head of original function + sh_t32_absolute_jump((uint16_t *)self->trampo, is_align4, new_addr); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r; + + SH_LOG_INFO("thumb: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR + " -> remaining %" PRIxPTR, + target_addr, new_addr, self->enter_addr, SH_UTIL_SET_BIT0(target_addr + rewrite_len)); + return 0; +} + +static int sh_inst_hook_arm_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr, + uintptr_t *orig_addr2) { + // backup original instructions (length: 4 or 8) + memcpy((void *)(self->backup), (void *)target_addr, self->backup_len); + + // package the information passed to rewrite + sh_a32_rewrite_info_t rinfo; + rinfo.overwrite_start_addr = target_addr; + rinfo.overwrite_end_addr = target_addr + self->backup_len; + rinfo.rewrite_buf = (uint32_t *)self->enter_addr; + rinfo.rewrite_buf_offset = 0; + rinfo.rewrite_inst_lens_cnt = self->backup_len / 4; + for (uintptr_t i = 0; i < self->backup_len; i += 4) + rinfo.rewrite_inst_lens[i / 4] = sh_a32_get_rewrite_inst_len(*((uint32_t *)(target_addr + i))); + + // rewrite original instructions (fill in enter) + uintptr_t pc = target_addr + 8; + for (uintptr_t i = 0; i < self->backup_len; i += 4, pc += 4) { + size_t offset = sh_a32_rewrite((uint32_t *)(self->enter_addr + rinfo.rewrite_buf_offset), + *((uint32_t *)(target_addr + i)), pc, &rinfo); + if (0 == offset) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED; + rinfo.rewrite_buf_offset += offset; + } + + // absolute jump back to remaining original instructions (fill in enter) + rinfo.rewrite_buf_offset += sh_a32_absolute_jump((uint32_t *)(self->enter_addr + rinfo.rewrite_buf_offset), + target_addr + self->backup_len); + sh_util_clear_cache(self->enter_addr, rinfo.rewrite_buf_offset); + + // save original function address + if (NULL != orig_addr) __atomic_store_n(orig_addr, self->enter_addr, __ATOMIC_SEQ_CST); + if (NULL != orig_addr2) __atomic_store_n(orig_addr2, self->enter_addr, __ATOMIC_SEQ_CST); + return 0; +} + +#ifdef SH_CONFIG_TRY_WITH_EXIT + +// B A1: [-32M, +32M - 4] +#define SH_INST_A32_B_RANGE_LOW (33554432) +#define SH_INST_A32_B_RANGE_HIGH (33554428) + +static int sh_inst_hook_arm_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, + uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) { + int r; + uintptr_t pc = target_addr + 8; + self->backup_len = 4; + + if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ; + + // alloc an exit for absolute jump + sh_a32_absolute_jump(self->exit, new_addr); + if (0 != (r = sh_exit_alloc(&self->exit_addr, &self->exit_type, pc, dlinfo, (uint8_t *)(self->exit), + sizeof(self->exit), SH_INST_A32_B_RANGE_LOW, SH_INST_A32_B_RANGE_HIGH))) + return r; + + // rewrite + if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) { + r = SHADOWHOOK_ERRNO_MPROT; + goto err; + } + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = sh_inst_hook_arm_rewrite(self, target_addr, orig_addr, orig_addr2); + } + SH_SIG_CATCH() { + r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH; + goto err; + } + SH_SIG_EXIT + if (0 != r) goto err; + + // relative jump to the exit by overwriting the head of original function + sh_a32_relative_jump(self->trampo, self->exit_addr, pc); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err; + + SH_LOG_INFO("a32: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR + " -> enter %" PRIxPTR " -> remaining %" PRIxPTR, + target_addr, self->exit_addr, new_addr, self->enter_addr, target_addr + self->backup_len); + return 0; + +err: + sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit)); + self->exit_addr = 0; // this is a flag for with-exit or without-exit + return r; +} + +#endif + +static int sh_inst_hook_arm_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, + uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) { + int r; + self->backup_len = 8; + + if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ; + + // rewrite + if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) + return SHADOWHOOK_ERRNO_MPROT; + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = sh_inst_hook_arm_rewrite(self, target_addr, orig_addr, orig_addr2); + } + SH_SIG_CATCH() { + return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH; + } + SH_SIG_EXIT + if (0 != r) return r; + + // absolute jump to new function address by overwriting the head of original function + sh_a32_absolute_jump(self->trampo, new_addr); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r; + + SH_LOG_INFO("a32: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR + " -> remaining %" PRIxPTR, + target_addr, new_addr, self->enter_addr, target_addr + self->backup_len); + return 0; +} + +int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr, + uintptr_t *orig_addr, uintptr_t *orig_addr2) { + self->enter_addr = sh_enter_alloc(); + if (0 == self->enter_addr) return SHADOWHOOK_ERRNO_HOOK_ENTER; + + int r; + if (SH_UTIL_IS_THUMB(target_addr)) { +#ifdef SH_CONFIG_TRY_WITH_EXIT + if (0 == (r = sh_inst_hook_thumb_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) + return r; +#endif + if (0 == + (r = sh_inst_hook_thumb_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) + return r; + } else { +#ifdef SH_CONFIG_TRY_WITH_EXIT + if (0 == (r = sh_inst_hook_arm_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) + return r; +#endif + if (0 == (r = sh_inst_hook_arm_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) + return r; + } + + // hook failed + if (NULL != orig_addr) *orig_addr = 0; + if (NULL != orig_addr2) *orig_addr2 = 0; + sh_enter_free(self->enter_addr); + return r; +} + +int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr) { + int r; + bool is_thumb = SH_UTIL_IS_THUMB(target_addr); + + if (is_thumb) target_addr = SH_UTIL_CLEAR_BIT0(target_addr); + + // restore the instructions at the target address + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = memcmp((void *)target_addr, self->trampo, self->backup_len); + } + SH_SIG_CATCH() { + return SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH; + } + SH_SIG_EXIT + if (0 != r) return SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH; + if (0 != (r = sh_util_write_inst(target_addr, self->backup, self->backup_len))) return r; + __atomic_thread_fence(__ATOMIC_SEQ_CST); + + // free memory space for exit + if (0 != self->exit_addr) + if (0 != + (r = sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit)))) + return r; + + // free memory space for enter + sh_enter_free(self->enter_addr); + + SH_LOG_INFO("%s: unhook OK. target %" PRIxPTR, is_thumb ? "thumb" : "a32", target_addr); + return 0; +} diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_inst.h b/app/src/main/cpp/shadowhook/arch/arm/sh_inst.h new file mode 100644 index 0000000000000000000000000000000000000000..ed38ab740a274a7b7ab614ada25a3e697d5f3550 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_inst.h @@ -0,0 +1,41 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdint.h> + +#include "xdl.h" + +typedef struct { + uint32_t trampo[4]; // align 16 // length == backup_len + uint8_t backup[16]; // align 16 + uint16_t backup_len; // == 4 or 8 or 10 + uint16_t exit_type; + uintptr_t exit_addr; + uint32_t exit[2]; + uintptr_t enter_addr; +} sh_inst_t; + +int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr, + uintptr_t *orig_addr, uintptr_t *orig_addr2); +int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr); diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t16.c b/app/src/main/cpp/shadowhook/arch/arm/sh_t16.c new file mode 100644 index 0000000000000000000000000000000000000000..d2505264548284351a82d8419d5ffda664309ab5 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_t16.c @@ -0,0 +1,284 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Pengying Xu (xupengying@bytedance.com) on 2021-04-11. + +#include "sh_t16.h" + +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + +#include "sh_log.h" +#include "sh_util.h" + +// https://developer.arm.com/documentation/ddi0406/latest +// https://developer.arm.com/documentation/ddi0597/latest + +typedef enum { + IGNORED = 0, + IT_T1, + B_T1, + B_T2, + BX_T1, + ADD_REG_T2, + MOV_REG_T1, + ADR_T1, + LDR_LIT_T1, + CBZ_T1, + CBNZ_T1 +} sh_t16_type_t; + +static sh_t16_type_t sh_t16_get_type(uint16_t inst) { + if (((inst & 0xFF00u) == 0xBF00) && ((inst & 0x000Fu) != 0x0000) && ((inst & 0x00F0u) != 0x00F0)) + return IT_T1; + else if (((inst & 0xF000u) == 0xD000) && ((inst & 0x0F00u) != 0x0F00) && ((inst & 0x0F00u) != 0x0E00)) + return B_T1; + else if ((inst & 0xF800u) == 0xE000) + return B_T2; + else if ((inst & 0xFFF8u) == 0x4778) + return BX_T1; + else if (((inst & 0xFF78u) == 0x4478) && ((inst & 0x0087u) != 0x0085)) + return ADD_REG_T2; + else if ((inst & 0xFF78u) == 0x4678) + return MOV_REG_T1; + else if ((inst & 0xF800u) == 0xA000) + return ADR_T1; + else if ((inst & 0xF800u) == 0x4800) + return LDR_LIT_T1; + else if ((inst & 0xFD00u) == 0xB100) + return CBZ_T1; + else if ((inst & 0xFD00u) == 0xB900) + return CBNZ_T1; + else + return IGNORED; +} + +size_t sh_t16_get_rewrite_inst_len(uint16_t inst) { + static uint8_t map[] = { + 4, // IGNORED + 0, // IT_T1 + 12, // B_T1 + 8, // B_T2 + 8, // BX_T1 + 16, // ADD_REG_T2 + 12, // MOV_REG_T1 + 8, // ADR_T1 + 12, // LDR_LIT_T1 + 12, // CBZ_T1 + 12 // CBNZ_T1 + }; + + return (size_t)(map[sh_t16_get_type(inst)]); +} + +static size_t sh_t16_rewrite_b(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_t16_type_t type, + sh_txx_rewrite_info_t *rinfo) { + uint32_t addr; + if (type == B_T1) { + uint32_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0); + addr = pc + SH_UTIL_SIGN_EXTEND_32(imm8 << 1u, 9u); + addr = SH_UTIL_SET_BIT0(addr); // thumb -> thumb + } else if (type == B_T2) { + uint32_t imm11 = SH_UTIL_GET_BITS_16(inst, 10, 0); + addr = pc + SH_UTIL_SIGN_EXTEND_32(imm11 << 1u, 12u); + addr = SH_UTIL_SET_BIT0(addr); // thumb -> thumb + } else { + // type == BX_T1 + // BX PC + // PC must be even, and the "BX PC" instruction must be at a 4-byte aligned address, + // so the instruction set must be exchanged from "thumb" to "arm". + addr = pc; // thumb -> arm + } + addr = sh_txx_fix_addr(addr, rinfo); + + size_t idx = 0; + if (type == B_T1) { + buf[idx++] = inst & 0xFF00u; // B<c> #0 + buf[idx++] = 0xE003; // B PC, #6 + } + buf[idx++] = 0xF8DF; // LDR.W PC, [PC] + buf[idx++] = 0xF000; // ... + buf[idx++] = addr & 0xFFFFu; + buf[idx++] = addr >> 16u; + return idx * 2; // 8 or 12 +} + +static size_t sh_t16_rewrite_add(uint16_t *buf, uint16_t inst, uintptr_t pc) { + // ADD<c> <Rdn>, PC + uint16_t dn = SH_UTIL_GET_BIT_16(inst, 7); + uint16_t rdn = SH_UTIL_GET_BITS_16(inst, 2, 0); + uint16_t rd = (uint16_t)(dn << 3u) | rdn; + uint16_t rx = (rd == 0) ? 1 : 0; // r0 - r1 + + buf[0] = (uint16_t)(0xB400u | (1u << rx)); // PUSH {Rx} + buf[1] = 0x4802u | (uint16_t)(rx << 8u); // LDR Rx, [PC, #8] + buf[2] = (inst & 0xFF87u) | (uint16_t)(rx << 3u); // ADD Rd, Rx + buf[3] = (uint16_t)(0xBC00u | (1u << rx)); // POP {Rx} + buf[4] = 0xE002; // B #4 + buf[5] = 0xBF00; + buf[6] = pc & 0xFFFFu; + buf[7] = pc >> 16u; + return 16; +} + +static size_t sh_t16_rewrite_mov(uint16_t *buf, uint16_t inst, uintptr_t pc) { + // MOV<c> <Rd>, PC + uint16_t D = SH_UTIL_GET_BIT_16(inst, 7); + uint16_t rd = SH_UTIL_GET_BITS_16(inst, 2, 0); + uint16_t d = (uint16_t)(D << 3u) | rd; // r0 - r15 + + buf[0] = 0xF8DF; // LDR.W Rd, [PC, #4] + buf[1] = (uint16_t)(d << 12u) + 4u; // ... + buf[2] = 0xE002; // B #4 + buf[3] = 0xBF00; // NOP + buf[4] = pc & 0xFFFFu; + buf[5] = pc >> 16u; + return 12; +} + +static size_t sh_t16_rewrite_adr(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) { + // ADR<c> <Rd>, <label> + uint16_t rd = SH_UTIL_GET_BITS_16(inst, 10, 8); // r0 - r7 + uint16_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0); + uint32_t addr = SH_UTIL_ALIGN_4(pc) + (uint32_t)(imm8 << 2u); + if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + buf[0] = 0x4800u | (uint16_t)(rd << 8u); // LDR Rd, [PC] + buf[1] = 0xE001; // B #2 + buf[2] = addr & 0xFFFFu; + buf[3] = addr >> 16u; + return 8; +} + +static size_t sh_t16_rewrite_ldr(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) { + // LDR<c> <Rt>, <label> + uint16_t rt = SH_UTIL_GET_BITS_16(inst, 10, 8); // r0 - r7 + uint16_t imm8 = SH_UTIL_GET_BITS_16(inst, 7, 0); + uint32_t addr = SH_UTIL_ALIGN_4(pc) + (uint32_t)(imm8 << 2u); + if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + buf[0] = 0x4800u | (uint16_t)(rt << 8u); // LDR Rt, [PC] + buf[1] = 0xE001; // B #2 + buf[2] = addr & 0xFFFFu; + buf[3] = addr >> 16u; + buf[4] = 0x6800u | (uint16_t)(rt << 3u) | rt; // LDR Rt, [Rt] + buf[5] = 0xBF00; // NOP + return 12; +} + +static size_t sh_t16_rewrite_cb(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) { + // CB{N}Z <Rn>, <label> + uint16_t i = SH_UTIL_GET_BIT_16(inst, 9); + uint16_t imm5 = SH_UTIL_GET_BITS_16(inst, 7, 3); + uint32_t imm32 = (uint32_t)(i << 6u) | (uint32_t)(imm5 << 1u); + uint32_t addr = SH_UTIL_SET_BIT0(pc + imm32); // thumb -> thumb + addr = sh_txx_fix_addr(addr, rinfo); + + buf[0] = inst & 0xFD07u; // CB(N)Z Rn, #0 + buf[1] = 0xE003; // B PC, #6 + buf[2] = 0xF8DF; // LDR.W PC, [PC] + buf[3] = 0xF000; // ... + buf[4] = addr & 0xFFFFu; + buf[5] = addr >> 16u; + return 12; +} + +size_t sh_t16_rewrite(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo) { + sh_t16_type_t type = sh_t16_get_type(inst); + SH_LOG_INFO("t16 rewrite: type %d, inst %" PRIx16, type, inst); + + if (type == B_T1 || type == B_T2 || type == BX_T1) + return sh_t16_rewrite_b(buf, inst, pc, type, rinfo); + else if (type == ADD_REG_T2) + return sh_t16_rewrite_add(buf, inst, pc); + else if (type == MOV_REG_T1) + return sh_t16_rewrite_mov(buf, inst, pc); + else if (type == ADR_T1) + return sh_t16_rewrite_adr(buf, inst, pc, rinfo); + else if (type == LDR_LIT_T1) + return sh_t16_rewrite_ldr(buf, inst, pc, rinfo); + else if (type == CBZ_T1 || type == CBNZ_T1) + return sh_t16_rewrite_cb(buf, inst, pc, rinfo); + else { + // IGNORED + buf[0] = inst; + buf[1] = 0xBF00; // NOP + return 4; + } +} + +static size_t sh_t16_get_it_insts_count(uint16_t inst) { + if ((inst & 0x1u) != 0) return 4; + if ((inst & 0x2u) != 0) return 3; + if ((inst & 0x4u) != 0) return 2; + return 1; +} + +bool sh_t16_parse_it(sh_t16_it_t *it, uint16_t inst, uintptr_t pc) { + if (IT_T1 != sh_t16_get_type(inst)) return false; + SH_LOG_INFO("t16 rewrite: type IT, inst %" PRIx16, inst); + + // address of the first inst in the IT block, skip the IT inst itself (2 bytes) + uintptr_t target_addr = pc - 4 + 2; + + it->firstcond = (uint8_t)(inst >> 4u); + uint8_t firstcond_0 = it->firstcond & 1u; + + memset(it, 0, sizeof(sh_t16_it_t)); + it->insts_cnt = sh_t16_get_it_insts_count(inst); + + size_t insts_idx = 0, pcs_idx = 0; + for (int parse_else = 1; parse_else >= 0; parse_else--) // round 0: parse ELSE, round 1: THEN + { + uintptr_t target_offset = 0; + for (size_t i = 0; i < it->insts_cnt; i++) { + bool is_thumb32 = sh_util_is_thumb32(target_addr + target_offset); + uint8_t mask_x = (uint8_t)(inst >> (uint16_t)(4 - i)) & 1u; + + if ((parse_else && mask_x != firstcond_0) || // parse ELSE or + (!parse_else && mask_x == firstcond_0)) // parse THEN + { + it->insts[insts_idx++] = *((uint16_t *)(target_addr + target_offset)); + if (is_thumb32) it->insts[insts_idx++] = *((uint16_t *)(target_addr + target_offset + 2)); + + it->pcs[pcs_idx++] = target_addr + target_offset + 4; + if (parse_else) it->insts_else_cnt++; + } + + target_offset += (is_thumb32 ? 4 : 2); + } + } + it->insts_len = insts_idx * 2; + + return true; +} + +void sh_t16_rewrite_it_else(uint16_t *buf, uint16_t imm9, sh_t16_it_t *it) { + buf[0] = 0xD000u | (uint16_t)(it->firstcond << 8u) | (uint16_t)(imm9 >> 1u); // B<c> <label> + buf[1] = 0xBF00; // NOP +} + +void sh_t16_rewrite_it_then(uint16_t *buf, uint16_t imm12) { + buf[0] = 0xE000u | (uint16_t)(imm12 >> 1u); // B <label> + buf[1] = 0xBF00; // NOP +} diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t16.h b/app/src/main/cpp/shadowhook/arch/arm/sh_t16.h new file mode 100644 index 0000000000000000000000000000000000000000..9d68d79608ebf329d87f5c37cb5f23e9156c1d07 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_t16.h @@ -0,0 +1,46 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Pengying Xu (xupengying@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "sh_txx.h" + +typedef struct { + uint16_t insts[8]; + size_t insts_len; // 2 - 16 (bytes) + size_t insts_cnt; // 1 - 4 + size_t insts_else_cnt; // 0 - 3 + uintptr_t pcs[4]; + uint8_t firstcond; + uint8_t padding[3]; +} sh_t16_it_t; + +bool sh_t16_parse_it(sh_t16_it_t *it, uint16_t inst, uintptr_t pc); +void sh_t16_rewrite_it_else(uint16_t *buf, uint16_t imm9, sh_t16_it_t *it); +void sh_t16_rewrite_it_then(uint16_t *buf, uint16_t imm12); + +size_t sh_t16_get_rewrite_inst_len(uint16_t inst); +size_t sh_t16_rewrite(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo); diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t32.c b/app/src/main/cpp/shadowhook/arch/arm/sh_t32.c new file mode 100644 index 0000000000000000000000000000000000000000..bb9e859970bde9f4d925c1459619835fccedd1bd --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_t32.c @@ -0,0 +1,408 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_t32.h" + +#include <inttypes.h> +#include <stdint.h> + +#include "sh_log.h" +#include "sh_util.h" + +// https://developer.arm.com/documentation/ddi0406/latest +// https://developer.arm.com/documentation/ddi0597/latest + +typedef enum { + IGNORED = 0, + B_T3, + B_T4, + BL_IMM_T1, + BLX_IMM_T2, + ADR_T2, + ADR_T3, + LDR_LIT_T2, + LDR_LIT_PC_T2, + LDRB_LIT_T1, + LDRD_LIT_T1, + LDRH_LIT_T1, + LDRSB_LIT_T1, + LDRSH_LIT_T1, + PLD_LIT_T1, + PLI_LIT_T3, + TBB_T1, + TBH_T1, + VLDR_LIT_T1 +} sh_t32_type_t; + +static sh_t32_type_t sh_t32_get_type(uint32_t inst) { + if (((inst & 0xF800D000) == 0xF0008000) && ((inst & 0x03800000u) != 0x03800000u)) + return B_T3; + else if ((inst & 0xF800D000) == 0xF0009000) + return B_T4; + else if ((inst & 0xF800D000) == 0xF000D000) + return BL_IMM_T1; + else if ((inst & 0xF800D000) == 0xF000C000) + return BLX_IMM_T2; + else if ((inst & 0xFBFF8000) == 0xF2AF0000) + return ADR_T2; + else if ((inst & 0xFBFF8000) == 0xF20F0000) + return ADR_T3; + else if ((inst & 0xFF7F0000) == 0xF85F0000) + return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_T2 : LDR_LIT_T2; + else if (((inst & 0xFF7F0000) == 0xF81F0000) && ((inst & 0xF000u) != 0xF000u)) + return LDRB_LIT_T1; + else if ((inst & 0xFF7F0000) == 0xE95F0000) + return LDRD_LIT_T1; + else if (((inst & 0xFF7F0000) == 0xF83F0000) && ((inst & 0xF000u) != 0xF000u)) + return LDRH_LIT_T1; + else if (((inst & 0xFF7F0000) == 0xF91F0000) && ((inst & 0xF000u) != 0xF000u)) + return LDRSB_LIT_T1; + else if (((inst & 0xFF7F0000) == 0xF93F0000) && ((inst & 0xF000u) != 0xF000u)) + return LDRSH_LIT_T1; + else if ((inst & 0xFF7FF000) == 0xF81FF000) + return PLD_LIT_T1; + else if ((inst & 0xFF7FF000) == 0xF91FF000) + return PLI_LIT_T3; + else if ((inst & 0xFFF0FFF0) == 0xE8D0F000) + return TBB_T1; + else if ((inst & 0xFFF0FFF0) == 0xE8D0F010) + return TBH_T1; + else if ((inst & 0xFF3F0C00) == 0xED1F0800) + return VLDR_LIT_T1; + else + return IGNORED; +} + +size_t sh_t32_get_rewrite_inst_len(uint16_t high_inst, uint16_t low_inst) { + static uint8_t map[] = { + 4, // IGNORED + 12, // B_T3 + 8, // B_T4 + 12, // BL_IMM_T1 + 12, // BLX_IMM_T2 + 12, // ADR_T2 + 12, // ADR_T3 + 16, // LDR_LIT_T2 + 24, // LDR_LIT_PC_T2 + 16, // LDRB_LIT_T1 + 16, // LDRD_LIT_T1 + 16, // LDRH_LIT_T1 + 16, // LDRSB_LIT_T1 + 16, // LDRSH_LIT_T1 + 20, // PLD_LIT_T1 + 20, // PLI_LIT_T3 + 32, // TBB_T1 + 32, // TBH_T1 + 24 // VLDR_LIT_T1 + }; + + uint32_t inst = (uint32_t)(high_inst << 16u) | low_inst; + return (size_t)(map[sh_t32_get_type(inst)]); +} + +static size_t sh_t32_rewrite_b(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, + sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) { + uint32_t j1 = SH_UTIL_GET_BIT_16(low_inst, 13); + uint32_t j2 = SH_UTIL_GET_BIT_16(low_inst, 11); + uint32_t s = SH_UTIL_GET_BIT_16(high_inst, 10); + uint32_t i1 = !(j1 ^ s); + uint32_t i2 = !(j2 ^ s); + + uint32_t addr; + if (type == B_T3) { + uint32_t x = + (s << 20u) | (j2 << 19u) | (j1 << 18u) | ((high_inst & 0x3Fu) << 12u) | ((low_inst & 0x7FFu) << 1u); + uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 21u); + addr = SH_UTIL_SET_BIT0(pc + imm32); // thumb -> thumb + } else if (type == B_T4) { + uint32_t x = + (s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FFu) << 1u); + uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u); + addr = SH_UTIL_SET_BIT0(pc + imm32); // thumb -> thumb + } else if (type == BL_IMM_T1) { + uint32_t x = + (s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FFu) << 1u); + uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u); + addr = SH_UTIL_SET_BIT0(pc + imm32); // thumb -> thumb + } else // type == BLX_IMM_T2 + { + uint32_t x = + (s << 24u) | (i1 << 23u) | (i2 << 22u) | ((high_inst & 0x3FFu) << 12u) | ((low_inst & 0x7FEu) << 1u); + uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(x, 25u); + // In BL and BLX instructions, only when the target instruction set is "arm", + // you need to do 4-byte alignment for PC. + addr = SH_UTIL_ALIGN_4(pc) + imm32; // thumb -> arm, align4 + } + addr = sh_txx_fix_addr(addr, rinfo); + + size_t idx = 0; + if (type == B_T3) { + uint32_t cond = SH_UTIL_GET_BITS_16(high_inst, 9, 6); + buf[idx++] = 0xD000u | (uint16_t)(cond << 8u); // B<c> #0 + buf[idx++] = 0xE003; // B #6 + } else if (type == BL_IMM_T1 || type == BLX_IMM_T2) { + buf[idx++] = 0xF20F; // ADD LR, PC, #9 + buf[idx++] = 0x0E09; // ... + } + buf[idx++] = 0xF8DF; // LDR.W PC, [PC] + buf[idx++] = 0xF000; // ... + buf[idx++] = addr & 0xFFFFu; + buf[idx++] = addr >> 16u; + return idx * 2; // 8 or 12 +} + +static size_t sh_t32_rewrite_adr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, + sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) { + uint32_t rt = SH_UTIL_GET_BITS_16(low_inst, 11, 8); // r0 - r14 + uint32_t i = SH_UTIL_GET_BIT_16(high_inst, 10); + uint32_t imm3 = SH_UTIL_GET_BITS_16(low_inst, 14, 12); + uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0); + uint32_t imm32 = (i << 11u) | (imm3 << 8u) | imm8; + uint32_t addr = (type == ADR_T2 ? (SH_UTIL_ALIGN_4(pc) - imm32) : (SH_UTIL_ALIGN_4(pc) + imm32)); + if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + buf[0] = 0xF8DF; // LDR.W Rt, [PC, #4] + buf[1] = (uint16_t)(rt << 12u) + 4u; // ... + buf[2] = 0xE002; // B #4 + buf[3] = 0xBF00; // NOP + buf[4] = addr & 0xFFFFu; + buf[5] = addr >> 16u; + return 12; +} + +static size_t sh_t32_rewrite_ldr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, + sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) { + uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7); + uint32_t rt = SH_UTIL_GET_BITS_16(low_inst, 15, 12); // r0 - r15 + uint32_t rt2 = 0; // r0 - r15 + uint32_t addr; + + if (type == LDRD_LIT_T1) { + rt2 = SH_UTIL_GET_BITS_16(low_inst, 11, 8); + uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0); + addr = (u ? SH_UTIL_ALIGN_4(pc) + (imm8 << 2u) : SH_UTIL_ALIGN_4(pc) - (imm8 << 2u)); + } else { + uint32_t imm12 = (uint32_t)SH_UTIL_GET_BITS_16(low_inst, 11, 0); + addr = (u ? SH_UTIL_ALIGN_4(pc) + imm12 : SH_UTIL_ALIGN_4(pc) - imm12); + } + if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + if (type == LDR_LIT_PC_T2 && rt == 0xF) // Rt == PC + { + buf[0] = 0xB403; // PUSH {R0, R1} + buf[1] = 0xBF00; // NOP + buf[2] = 0xF8DF; // LDR.W R0, [PC, #4] + buf[3] = 0x0004; // ... + buf[4] = 0xE002; // B #4 + buf[5] = 0xBF00; // NOP + buf[6] = addr & 0xFFFFu; // + buf[7] = addr >> 16u; // + buf[8] = 0xF8D0; // LDR.W R0, [R0] + buf[9] = 0x0000; // ... + buf[10] = 0x9001; // STR R0, [SP, #4] + buf[11] = 0xBD01; // POP {R0, PC} + return 24; + } else { + buf[0] = 0xF8DF; // LDR.W Rt, [PC, #4] + buf[1] = (uint16_t)(rt << 12u) | 4u; // ... + buf[2] = 0xE002; // B #4 + buf[3] = 0xBF00; // NOP + buf[4] = addr & 0xFFFFu; + buf[5] = addr >> 16u; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch" + switch (type) { + case LDR_LIT_T2: + buf[6] = (uint16_t)(0xF8D0 + rt); // LDR.W Rt, [Rt] + buf[7] = (uint16_t)(rt << 12u); // ... + break; + case LDRB_LIT_T1: + buf[6] = (uint16_t)(0xF890 + rt); // LDRB.W Rt, [Rt] + buf[7] = (uint16_t)(rt << 12u); // ... + break; + case LDRD_LIT_T1: + buf[6] = (uint16_t)(0xE9D0 + rt); // LDRD Rt, Rt2, [Rt] + buf[7] = (uint16_t)(rt << 12u) + (uint16_t)(rt2 << 8u); // ... + break; + case LDRH_LIT_T1: + buf[6] = (uint16_t)(0xF8B0 + rt); // LDRH.W Rt, [Rt] + buf[7] = (uint16_t)(rt << 12u); // ... + break; + case LDRSB_LIT_T1: + buf[6] = (uint16_t)(0xF990 + rt); // LDRSB.W Rt, [Rt] + buf[7] = (uint16_t)(rt << 12u); // ... + break; + case LDRSH_LIT_T1: + buf[6] = (uint16_t)(0xF9B0 + rt); // LDRSH.W Rt, [Rt] + buf[7] = (uint16_t)(rt << 12u); // ... + break; + } +#pragma clang diagnostic pop + return 16; + } +} + +static size_t sh_t32_rewrite_pl(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, + sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) { + uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7); + uint32_t imm12 = SH_UTIL_GET_BITS_16(low_inst, 11, 0); + uint32_t addr = (u ? SH_UTIL_ALIGN_4(pc) + imm12 : SH_UTIL_ALIGN_4(pc) - imm12); + addr = sh_txx_fix_addr(addr, rinfo); + + buf[0] = 0xB401; // PUSH {R0} + buf[1] = 0xBF00; // NOP + buf[2] = 0xF8DF; // LDR.W R0, [PC, #8] + buf[3] = 0x0008; // ... + if (type == PLD_LIT_T1) { + buf[4] = 0xF890; // PLD [R0] + buf[5] = 0xF000; // ... + } else { + buf[4] = 0xF990; // PLI [R0] + buf[5] = 0xF000; // ... + } + buf[6] = 0xBC01; // POP {R0} + buf[7] = 0xE001; // B #2 + buf[8] = addr & 0xFFFFu; + buf[9] = addr >> 16u; + return 20; +} + +static size_t sh_t32_rewrite_tb(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, + sh_t32_type_t type, sh_txx_rewrite_info_t *rinfo) { + // If TBB/TBH is not the last instruction that needs to be rewritten, + // the rewriting can NOT be completed. + uintptr_t target_addr = SH_UTIL_CLEAR_BIT0(pc - 4); + if (target_addr + 4 != rinfo->end_addr) return 0; // rewrite failed + + uint32_t rn = SH_UTIL_GET_BITS_16(high_inst, 3, 0); + uint32_t rm = SH_UTIL_GET_BITS_16(low_inst, 3, 0); + uint32_t rx, ry; // r0 - r7 + for (rx = 7;; --rx) + if (rx != rn && rx != rm) break; + for (ry = 7;; --ry) + if (ry != rn && ry != rm && ry != rx) break; + + buf[0] = (uint16_t)(0xB500u | (1u << rx) | (1u << ry)); // PUSH {Rx, Ry, LR} + buf[1] = 0xBF00; // NOP + buf[2] = 0xF8DF; // LDR.W Rx, [PC, #20] + buf[3] = (uint16_t)(rx << 12u) | 20u; // ... + if (type == TBB_T1) { + buf[4] = (uint16_t)(0xEB00u | (rn == 0xF ? rx : rn)); // ADD.W Ry, Rx|Rn, Rm + buf[5] = (uint16_t)(0x0000u | (ry << 8u) | rm); // ... + buf[6] = (uint16_t)(0x7800u | (ry << 3u) | ry); // LDRB Ry, [Ry] + buf[7] = 0xBF00; // NOP + } else { + buf[4] = (uint16_t)(0xEB00u | (rn == 0xF ? rx : rn)); // ADD.W Ry, Rx|Rn, Rm, LSL #1 + buf[5] = (uint16_t)(0x0040u | (ry << 8u) | rm); // ... + buf[6] = (uint16_t)(0x8800u | (ry << 3u) | ry); // LDRH Ry, [Ry] + buf[7] = 0xBF00; // NOP + } + buf[8] = (uint16_t)(0xEB00u | rx); // ADD Rx, Rx, Ry, LSL #1 + buf[9] = (uint16_t)(0x0040u | (rx << 8u) | ry); // ... + buf[10] = (uint16_t)(0x3001u | (rx << 8u)); // ADD Rx, #1 + buf[11] = (uint16_t)(0x9002u | (rx << 8u)); // STR Rx, [SP, #8] + buf[12] = (uint16_t)(0xBD00u | (1u << rx) | (1u << ry)); // POP {Rx, Ry, PC} + buf[13] = 0xBF00; // NOP + buf[14] = pc & 0xFFFFu; + buf[15] = pc >> 16u; + return 32; +} + +static size_t sh_t32_rewrite_vldr(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, + sh_txx_rewrite_info_t *rinfo) { + uint32_t u = SH_UTIL_GET_BIT_16(high_inst, 7); + uint32_t D = SH_UTIL_GET_BIT_16(high_inst, 6); + uint32_t vd = SH_UTIL_GET_BITS_16(low_inst, 15, 12); + uint32_t size = SH_UTIL_GET_BITS_16(low_inst, 9, 8); + uint32_t imm8 = SH_UTIL_GET_BITS_16(low_inst, 7, 0); + uint32_t esize = (8u << size); + uint32_t imm32 = (esize == 16 ? imm8 << 1u : imm8 << 2u); + uint32_t addr = (u ? SH_UTIL_ALIGN_4(pc) + imm32 : SH_UTIL_ALIGN_4(pc) - imm32); + if (sh_txx_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + buf[0] = 0xB401; // PUSH {R0} + buf[1] = 0xBF00; // NOP + buf[2] = 0xF8DF; // LDR.W R0, [PC, #4] + buf[3] = 0x0004; // ... + buf[4] = 0xE002; // B #4 + buf[5] = 0xBF00; // NOP + buf[6] = addr & 0xFFFFu; // + buf[7] = addr >> 16u; // + buf[8] = (uint16_t)(0xED90u | D << 6u); // VLDR Sd|Dd, [R0] + buf[9] = (uint16_t)(0x800u | vd << 12u | size << 8u); // ... + buf[10] = 0xBC01; // POP {R0} + buf[11] = 0xBF00; // NOP + return 24; +} + +size_t sh_t32_rewrite(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, + sh_txx_rewrite_info_t *rinfo) { + uint32_t inst = (uint32_t)(high_inst << 16u) | low_inst; + sh_t32_type_t type = sh_t32_get_type(inst); + SH_LOG_INFO("t32 rewrite: type %d, high inst %" PRIx16 ", low inst %" PRIx16, type, high_inst, low_inst); + + if (type == B_T3 || type == B_T4 || type == BL_IMM_T1 || type == BLX_IMM_T2) + return sh_t32_rewrite_b(buf, high_inst, low_inst, pc, type, rinfo); + else if (type == ADR_T2 || type == ADR_T3) + return sh_t32_rewrite_adr(buf, high_inst, low_inst, pc, type, rinfo); + else if (type == LDR_LIT_T2 || type == LDR_LIT_PC_T2 || type == LDRB_LIT_T1 || type == LDRD_LIT_T1 || + type == LDRH_LIT_T1 || type == LDRSB_LIT_T1 || type == LDRSH_LIT_T1) + return sh_t32_rewrite_ldr(buf, high_inst, low_inst, pc, type, rinfo); + else if (type == PLD_LIT_T1 || type == PLI_LIT_T3) + return sh_t32_rewrite_pl(buf, high_inst, low_inst, pc, type, rinfo); + else if (type == TBB_T1 || type == TBH_T1) + return sh_t32_rewrite_tb(buf, high_inst, low_inst, pc, type, rinfo); + else if (type == VLDR_LIT_T1) + return sh_t32_rewrite_vldr(buf, high_inst, low_inst, pc, rinfo); + else { + // IGNORED + buf[0] = high_inst; + buf[1] = low_inst; + return 4; + } +} + +size_t sh_t32_absolute_jump(uint16_t *buf, bool is_align4, uintptr_t addr) { + size_t i = 0; + if (!is_align4) buf[i++] = 0xBF00; // NOP + buf[i++] = 0xF8DF; // LDR.W PC, [PC] + buf[i++] = 0xF000; // ... + buf[i++] = addr & 0xFFFFu; + buf[i++] = addr >> 16u; + return i * 2; +} + +size_t sh_t32_relative_jump(uint16_t *buf, uintptr_t addr, uintptr_t pc) { + uint32_t imm32 = addr - pc; + uint32_t s = SH_UTIL_GET_BIT_32(imm32, 24); + uint32_t i1 = SH_UTIL_GET_BIT_32(imm32, 23); + uint32_t i2 = SH_UTIL_GET_BIT_32(imm32, 22); + uint32_t imm10 = SH_UTIL_GET_BITS_32(imm32, 21, 12); + uint32_t imm11 = SH_UTIL_GET_BITS_32(imm32, 11, 1); + uint32_t j1 = (!i1) ^ s; + uint32_t j2 = (!i2) ^ s; + + buf[0] = (uint16_t)(0xF000u | (s << 10u) | imm10); + buf[1] = (uint16_t)(0x9000u | (j1 << 13u) | (j2 << 11u) | imm11); + return 4; +} diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t32.h b/app/src/main/cpp/shadowhook/arch/arm/sh_t32.h new file mode 100644 index 0000000000000000000000000000000000000000..1dc11797063e57aad53d00c38939bf2b7219a09b --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_t32.h @@ -0,0 +1,36 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "sh_txx.h" + +size_t sh_t32_get_rewrite_inst_len(uint16_t high_inst, uint16_t low_inst); +size_t sh_t32_rewrite(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, + sh_txx_rewrite_info_t *rinfo); + +size_t sh_t32_absolute_jump(uint16_t *buf, bool is_align4, uintptr_t addr); +size_t sh_t32_relative_jump(uint16_t *buf, uintptr_t addr, uintptr_t pc); diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_txx.c b/app/src/main/cpp/shadowhook/arch/arm/sh_txx.c new file mode 100644 index 0000000000000000000000000000000000000000..79cefdb90f26afa312e5ae2d5436dcaa0249a5c5 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_txx.c @@ -0,0 +1,60 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_txx.h" + +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + +#include "sh_log.h" +#include "sh_util.h" + +bool sh_txx_is_addr_need_fix(uintptr_t addr, sh_txx_rewrite_info_t *rinfo) { + return (rinfo->start_addr <= addr && addr < rinfo->end_addr); +} + +uintptr_t sh_txx_fix_addr(uintptr_t addr, sh_txx_rewrite_info_t *rinfo) { + bool is_thumb = SH_UTIL_IS_THUMB(addr); + + if (is_thumb) addr = SH_UTIL_CLEAR_BIT0(addr); + + if (rinfo->start_addr <= addr && addr < rinfo->end_addr) { + uintptr_t cursor_addr = rinfo->start_addr; + size_t offset = 0; + for (size_t i = 0; i < rinfo->inst_lens_cnt; i++) { + if (cursor_addr >= addr) break; + cursor_addr += 2; + offset += rinfo->inst_lens[i]; + } + uintptr_t fixed_addr = (uintptr_t)rinfo->buf + offset; + if (is_thumb) fixed_addr = SH_UTIL_SET_BIT0(fixed_addr); + + SH_LOG_INFO("txx rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr); + return fixed_addr; + } + + if (is_thumb) addr = SH_UTIL_SET_BIT0(addr); + return addr; +} diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_txx.h b/app/src/main/cpp/shadowhook/arch/arm/sh_txx.h new file mode 100644 index 0000000000000000000000000000000000000000..f5b9fae76ec80894da26502d65693dbb57d2268d --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_txx.h @@ -0,0 +1,39 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +typedef struct { + uintptr_t start_addr; + uintptr_t end_addr; + uint16_t *buf; + size_t buf_offset; + size_t inst_lens[13]; // 26 / 2 = 13 + size_t inst_lens_cnt; +} sh_txx_rewrite_info_t; + +bool sh_txx_is_addr_need_fix(uintptr_t addr, sh_txx_rewrite_info_t *rinfo); +uintptr_t sh_txx_fix_addr(uintptr_t addr, sh_txx_rewrite_info_t *rinfo); diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.c b/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.c new file mode 100644 index 0000000000000000000000000000000000000000..cd9546bdf7202c4706f81df020e06ca53c26b5e6 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.c @@ -0,0 +1,310 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_a64.h" + +#include <inttypes.h> +#include <sh_util.h> +#include <stdint.h> + +#include "sh_log.h" + +// https://developer.arm.com/documentation/ddi0487/latest +// https://developer.arm.com/documentation/ddi0602/latest + +typedef enum { + IGNORED = 0, + B, + B_COND, + BL, + ADR, + ADRP, + LDR_LIT_32, + LDR_LIT_64, + LDRSW_LIT, + PRFM_LIT, + LDR_SIMD_LIT_32, + LDR_SIMD_LIT_64, + LDR_SIMD_LIT_128, + CBZ, + CBNZ, + TBZ, + TBNZ +} sh_a64_type_t; + +static sh_a64_type_t sh_a64_get_type(uint32_t inst) { + if ((inst & 0xFC000000) == 0x14000000) + return B; + else if ((inst & 0xFF000010) == 0x54000000) + return B_COND; + else if ((inst & 0xFC000000) == 0x94000000) + return BL; + else if ((inst & 0x9F000000) == 0x10000000) + return ADR; + else if ((inst & 0x9F000000) == 0x90000000) + return ADRP; + else if ((inst & 0xFF000000) == 0x18000000) + return LDR_LIT_32; + else if ((inst & 0xFF000000) == 0x58000000) + return LDR_LIT_64; + else if ((inst & 0xFF000000) == 0x98000000) + return LDRSW_LIT; + else if ((inst & 0xFF000000) == 0xD8000000) + return PRFM_LIT; + else if ((inst & 0xFF000000) == 0x1C000000) + return LDR_SIMD_LIT_32; + else if ((inst & 0xFF000000) == 0x5C000000) + return LDR_SIMD_LIT_64; + else if ((inst & 0xFF000000) == 0x9C000000) + return LDR_SIMD_LIT_128; + else if ((inst & 0x7F000000u) == 0x34000000) + return CBZ; + else if ((inst & 0x7F000000u) == 0x35000000) + return CBNZ; + else if ((inst & 0x7F000000u) == 0x36000000) + return TBZ; + else if ((inst & 0x7F000000u) == 0x37000000) + return TBNZ; + else + return IGNORED; +} + +size_t sh_a64_get_rewrite_inst_len(uint32_t inst) { + static uint8_t map[] = { + 4, // IGNORED + 20, // B + 28, // B_COND + 20, // BL + 16, // ADR + 16, // ADRP + 20, // LDR_LIT_32 + 20, // LDR_LIT_64 + 20, // LDRSW_LIT + 28, // PRFM_LIT + 28, // LDR_SIMD_LIT_32 + 28, // LDR_SIMD_LIT_64 + 28, // LDR_SIMD_LIT_128 + 24, // CBZ + 24, // CBNZ + 24, // TBZ + 24 // TBNZ + }; + + return (size_t)(map[sh_a64_get_type(inst)]); +} + +static bool sh_a64_is_addr_need_fix(uintptr_t addr, sh_a64_rewrite_info_t *rinfo) { + return (rinfo->start_addr <= addr && addr < rinfo->end_addr); +} + +static uintptr_t sh_a64_fix_addr(uintptr_t addr, sh_a64_rewrite_info_t *rinfo) { + if (rinfo->start_addr <= addr && addr < rinfo->end_addr) { + uintptr_t cursor_addr = rinfo->start_addr; + size_t offset = 0; + for (size_t i = 0; i < rinfo->inst_lens_cnt; i++) { + if (cursor_addr >= addr) break; + cursor_addr += 4; + offset += rinfo->inst_lens[i]; + } + uintptr_t fixed_addr = (uintptr_t)rinfo->buf + offset; + SH_LOG_INFO("a64 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr); + return fixed_addr; + } + + return addr; +} + +static size_t sh_a64_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type, + sh_a64_rewrite_info_t *rinfo) { + uint64_t imm64; + if (type == B_COND) { + uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5); + imm64 = SH_UTIL_SIGN_EXTEND_64(imm19 << 2u, 21u); + } else { + uint64_t imm26 = SH_UTIL_GET_BITS_32(inst, 25, 0); + imm64 = SH_UTIL_SIGN_EXTEND_64(imm26 << 2u, 28u); + } + uint64_t addr = pc + imm64; + addr = sh_a64_fix_addr(addr, rinfo); + + size_t idx = 0; + if (type == B_COND) { + buf[idx++] = (inst & 0xFF00001F) | 0x40u; // B.<cond> #8 + buf[idx++] = 0x14000006; // B #24 + } + buf[idx++] = 0x58000051; // LDR X17, #8 + buf[idx++] = 0x14000003; // B #12 + buf[idx++] = addr & 0xFFFFFFFF; + buf[idx++] = addr >> 32u; + if (type == BL) + buf[idx++] = 0xD63F0220; // BLR X17 + else + buf[idx++] = 0xD61F0220; // BR X17 + return idx * 4; // 20 or 28 +} + +static size_t sh_a64_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type, + sh_a64_rewrite_info_t *rinfo) { + uint32_t xd = SH_UTIL_GET_BITS_32(inst, 4, 0); + uint64_t immlo = SH_UTIL_GET_BITS_32(inst, 30, 29); + uint64_t immhi = SH_UTIL_GET_BITS_32(inst, 23, 5); + uint64_t addr; + if (type == ADR) + addr = pc + SH_UTIL_SIGN_EXTEND_64((immhi << 2u) | immlo, 21u); + else // ADRP + addr = (pc & 0xFFFFFFFFFFFFF000) + SH_UTIL_SIGN_EXTEND_64((immhi << 14u) | (immlo << 12u), 33u); + if (sh_a64_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + buf[0] = 0x58000040u | xd; // LDR Xd, #8 + buf[1] = 0x14000003; // B #12 + buf[2] = addr & 0xFFFFFFFF; + buf[3] = addr >> 32u; + return 16; +} + +static size_t sh_a64_rewrite_ldr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_type_t type, + sh_a64_rewrite_info_t *rinfo) { + uint32_t rt = SH_UTIL_GET_BITS_32(inst, 4, 0); + uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5); + uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm19 << 2u), 21u); + uint64_t addr = pc + offset; + + if (sh_a64_is_addr_need_fix(addr, rinfo)) { + if (type != PRFM_LIT) return 0; // rewrite failed + addr = sh_a64_fix_addr(addr, rinfo); + } + + if (type == LDR_LIT_32 || type == LDR_LIT_64 || type == LDRSW_LIT) { + buf[0] = 0x58000060u | rt; // LDR Xt, #12 + if (type == LDR_LIT_32) + buf[1] = 0xB9400000 | rt | (rt << 5u); // LDR Wt, [Xt] + else if (type == LDR_LIT_64) + buf[1] = 0xF9400000 | rt | (rt << 5u); // LDR Xt, [Xt] + else + // LDRSW_LIT + buf[1] = 0xB9800000 | rt | (rt << 5u); // LDRSW Xt, [Xt] + buf[2] = 0x14000003; // B #12 + buf[3] = addr & 0xFFFFFFFF; + buf[4] = addr >> 32u; + return 20; + } else { + buf[0] = 0xA93F47F0; // STP X16, X17, [SP, -0x10] + buf[1] = 0x58000091; // LDR X17, #16 + if (type == PRFM_LIT) + buf[2] = 0xF9800220 | rt; // PRFM Rt, [X17] + else if (type == LDR_SIMD_LIT_32) + buf[2] = 0xBD400220 | rt; // LDR St, [X17] + else if (type == LDR_SIMD_LIT_64) + buf[2] = 0xFD400220 | rt; // LDR Dt, [X17] + else + // LDR_SIMD_LIT_128 + buf[2] = 0x3DC00220u | rt; // LDR Qt, [X17] + buf[3] = 0xF85F83F1; // LDR X17, [SP, -0x8] + buf[4] = 0x14000003; // B #12 + buf[5] = addr & 0xFFFFFFFF; + buf[6] = addr >> 32u; + return 28; + } +} + +static size_t sh_a64_rewrite_cb(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) { + uint64_t imm19 = SH_UTIL_GET_BITS_32(inst, 23, 5); + uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm19 << 2u), 21u); + uint64_t addr = pc + offset; + addr = sh_a64_fix_addr(addr, rinfo); + + buf[0] = (inst & 0xFF00001F) | 0x40u; // CB(N)Z Rt, #8 + buf[1] = 0x14000005; // B #20 + buf[2] = 0x58000051; // LDR X17, #8 + buf[3] = 0xd61f0220; // BR X17 + buf[4] = addr & 0xFFFFFFFF; + buf[5] = addr >> 32u; + return 24; +} + +static size_t sh_a64_rewrite_tb(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) { + uint64_t imm14 = SH_UTIL_GET_BITS_32(inst, 18, 5); + uint64_t offset = SH_UTIL_SIGN_EXTEND_64((imm14 << 2u), 16u); + uint64_t addr = pc + offset; + addr = sh_a64_fix_addr(addr, rinfo); + + buf[0] = (inst & 0xFFF8001F) | 0x40u; // TB(N)Z Rt, #<imm>, #8 + buf[1] = 0x14000005; // B #20 + buf[2] = 0x58000051; // LDR X17, #8 + buf[3] = 0xd61f0220; // BR X17 + buf[4] = addr & 0xFFFFFFFF; + buf[5] = addr >> 32u; + return 24; +} + +size_t sh_a64_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo) { + sh_a64_type_t type = sh_a64_get_type(inst); + SH_LOG_INFO("a64 rewrite: type %d, inst %" PRIx32, type, inst); + + if (type == B || type == B_COND || type == BL) + return sh_a64_rewrite_b(buf, inst, pc, type, rinfo); + else if (type == ADR || type == ADRP) + return sh_a64_rewrite_adr(buf, inst, pc, type, rinfo); + else if (type == LDR_LIT_32 || type == LDR_LIT_64 || type == LDRSW_LIT || type == PRFM_LIT || + type == LDR_SIMD_LIT_32 || type == LDR_SIMD_LIT_64 || type == LDR_SIMD_LIT_128) + return sh_a64_rewrite_ldr(buf, inst, pc, type, rinfo); + else if (type == CBZ || type == CBNZ) + return sh_a64_rewrite_cb(buf, inst, pc, rinfo); + else if (type == TBZ || type == TBNZ) + return sh_a64_rewrite_tb(buf, inst, pc, rinfo); + else { + // IGNORED + buf[0] = inst; + return 4; + } +} + +size_t sh_a64_absolute_jump_with_br(uint32_t *buf, uintptr_t addr) { + buf[0] = 0x58000051; // LDR X17, #8 + buf[1] = 0xd61f0220; // BR X17 + buf[2] = addr & 0xFFFFFFFF; + buf[3] = addr >> 32u; + return 16; +} + +// Use RET instead of BR to bypass arm64 BTI. +// +// ref: +// https://developer.arm.com/documentation/102433/0100/Jump-oriented-programming +// https://developer.arm.com/documentation/ddi0602/2023-06/Base-Instructions/BTI--Branch-Target-Identification-?lang=en +// https://github.com/torvalds/linux/commit/8ef8f360cf30be12382f89ff48a57fbbd9b31c14 +// https://android-review.googlesource.com/c/platform/bionic/+/1242754 +// https://developer.android.com/ndk/guides/abis#armv9_enabling_pac_and_bti_for_cc +// https://developer.arm.com/documentation/100067/0612/armclang-Command-line-Options/-mbranch-protection +size_t sh_a64_absolute_jump_with_ret(uint32_t *buf, uintptr_t addr) { + buf[0] = 0x58000051; // LDR X17, #8 + buf[1] = 0xd65f0220; // RET X17 + buf[2] = addr & 0xFFFFFFFF; + buf[3] = addr >> 32u; + return 16; +} + +size_t sh_a64_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) { + buf[0] = 0x14000000u | (((addr - pc) & 0x0FFFFFFFu) >> 2u); // B <label> + return 4; +} diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h b/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h new file mode 100644 index 0000000000000000000000000000000000000000..a51cd2e2fb575edc1e8de87faf45b51bc34ea3df --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h @@ -0,0 +1,44 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stddef.h> +#include <stdint.h> + +#include "sh_inst.h" + +typedef struct { + uintptr_t start_addr; + uintptr_t end_addr; + uint32_t *buf; + size_t buf_offset; + size_t inst_lens[4]; + size_t inst_lens_cnt; +} sh_a64_rewrite_info_t; + +size_t sh_a64_get_rewrite_inst_len(uint32_t inst); +size_t sh_a64_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo); + +size_t sh_a64_absolute_jump_with_br(uint32_t *buf, uintptr_t addr); +size_t sh_a64_absolute_jump_with_ret(uint32_t *buf, uintptr_t addr); +size_t sh_a64_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc); diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.c b/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.c new file mode 100644 index 0000000000000000000000000000000000000000..7474a6c811ccf3cc9910adc680a1ba57ae300b0a --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.c @@ -0,0 +1,203 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_inst.h" + +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> + +#include "sh_a64.h" +#include "sh_config.h" +#include "sh_enter.h" +#include "sh_exit.h" +#include "sh_log.h" +#include "sh_sig.h" +#include "sh_util.h" +#include "shadowhook.h" +#include "xdl.h" + +static int sh_inst_hook_rewrite(sh_inst_t *self, uintptr_t target_addr, uintptr_t *orig_addr, + uintptr_t *orig_addr2) { + // backup original instructions (length: 4 or 16) + memcpy((void *)(self->backup), (void *)target_addr, self->backup_len); + + // package the information passed to rewrite + sh_a64_rewrite_info_t rinfo; + rinfo.start_addr = target_addr; + rinfo.end_addr = target_addr + self->backup_len; + rinfo.buf = (uint32_t *)self->enter_addr; + rinfo.buf_offset = 0; + rinfo.inst_lens_cnt = self->backup_len / 4; + for (uintptr_t i = 0; i < self->backup_len; i += 4) + rinfo.inst_lens[i / 4] = sh_a64_get_rewrite_inst_len(*((uint32_t *)(target_addr + i))); + + // rewrite original instructions (fill in enter) + uintptr_t pc = target_addr; + for (uintptr_t i = 0; i < self->backup_len; i += 4, pc += 4) { + size_t offset = sh_a64_rewrite((uint32_t *)(self->enter_addr + rinfo.buf_offset), + *((uint32_t *)(target_addr + i)), pc, &rinfo); + if (0 == offset) return SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED; + rinfo.buf_offset += offset; + } + + // absolute jump back to remaining original instructions (fill in enter) + rinfo.buf_offset += sh_a64_absolute_jump_with_ret((uint32_t *)(self->enter_addr + rinfo.buf_offset), + target_addr + self->backup_len); + sh_util_clear_cache(self->enter_addr, rinfo.buf_offset); + + // save original function address + if (NULL != orig_addr) __atomic_store_n(orig_addr, self->enter_addr, __ATOMIC_SEQ_CST); + if (NULL != orig_addr2) __atomic_store_n(orig_addr2, self->enter_addr, __ATOMIC_SEQ_CST); + return 0; +} + +#ifdef SH_CONFIG_TRY_WITH_EXIT + +// B: [-128M, +128M - 4] +#define SH_INST_A64_B_RANGE_LOW (134217728) +#define SH_INST_A64_B_RANGE_HIGH (134217724) + +static int sh_inst_hook_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, + uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) { + int r; + uintptr_t pc = target_addr; + self->backup_len = 4; + + if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ; + + // alloc an exit for absolute jump + sh_a64_absolute_jump_with_br(self->exit, new_addr); + if (0 != + (r = sh_exit_alloc(&self->exit_addr, (uint16_t *)&self->exit_type, pc, dlinfo, (uint8_t *)(self->exit), + sizeof(self->exit), SH_INST_A64_B_RANGE_LOW, SH_INST_A64_B_RANGE_HIGH))) + return r; + + // rewrite + if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) { + r = SHADOWHOOK_ERRNO_MPROT; + goto err; + } + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = sh_inst_hook_rewrite(self, target_addr, orig_addr, orig_addr2); + } + SH_SIG_CATCH() { + r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH; + goto err; + } + SH_SIG_EXIT + if (0 != r) goto err; + + // relative jump to the exit by overwriting the head of original function + sh_a64_relative_jump(self->trampo, self->exit_addr, pc); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err; + + SH_LOG_INFO("a64: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR + " -> enter %" PRIxPTR " -> remaining %" PRIxPTR, + target_addr, self->exit_addr, new_addr, self->enter_addr, target_addr + self->backup_len); + return 0; + +err: + sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit)); + self->exit_addr = 0; // this is a flag for with-exit or without-exit + return r; +} +#endif + +static int sh_inst_hook_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, + uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) { + int r; + self->backup_len = 16; + + if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ; + + // rewrite + if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) + return SHADOWHOOK_ERRNO_MPROT; + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = sh_inst_hook_rewrite(self, target_addr, orig_addr, orig_addr2); + } + SH_SIG_CATCH() { + return SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH; + } + SH_SIG_EXIT + if (0 != r) return r; + + // absolute jump to new function address by overwriting the head of original function + sh_a64_absolute_jump_with_br(self->trampo, new_addr); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) return r; + + SH_LOG_INFO("a64: hook (WITHOUT EXIT) OK. target %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR + " -> remaining %" PRIxPTR, + target_addr, new_addr, self->enter_addr, target_addr + self->backup_len); + return 0; +} + +int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr, + uintptr_t *orig_addr, uintptr_t *orig_addr2) { + self->enter_addr = sh_enter_alloc(); + if (0 == self->enter_addr) return SHADOWHOOK_ERRNO_HOOK_ENTER; + + int r; +#ifdef SH_CONFIG_TRY_WITH_EXIT + if (0 == (r = sh_inst_hook_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) return r; +#endif + if (0 == (r = sh_inst_hook_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) + return r; + + // hook failed + if (NULL != orig_addr) *orig_addr = 0; + if (NULL != orig_addr2) *orig_addr2 = 0; + sh_enter_free(self->enter_addr); + return r; +} + +int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr) { + int r; + + // restore the instructions at the target address + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = memcmp((void *)target_addr, self->trampo, self->backup_len); + } + SH_SIG_CATCH() { + return SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH; + } + SH_SIG_EXIT + if (0 != r) return SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH; + if (0 != (r = sh_util_write_inst(target_addr, self->backup, self->backup_len))) return r; + __atomic_thread_fence(__ATOMIC_SEQ_CST); + + // free memory space for exit + if (0 != self->exit_addr) + if (0 != (r = sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit), + sizeof(self->exit)))) + return r; + + // free memory space for enter + sh_enter_free(self->enter_addr); + + SH_LOG_INFO("a64: unhook OK. target %" PRIxPTR, target_addr); + return 0; +} diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.h b/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.h new file mode 100644 index 0000000000000000000000000000000000000000..5988b68938dbe644b1460636c78fdd5539106027 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm64/sh_inst.h @@ -0,0 +1,42 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdbool.h> +#include <stdint.h> + +#include "xdl.h" + +typedef struct { + uint32_t trampo[4]; // align 16 // length == backup_len + uint8_t backup[16]; // align 16 + uint32_t backup_len; // == 4 or 16 + uint32_t exit_type; + uintptr_t exit_addr; + uint32_t exit[4]; + uintptr_t enter_addr; +} sh_inst_t; + +int sh_inst_hook(sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr, + uintptr_t *orig_addr, uintptr_t *orig_addr2); +int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr); diff --git a/app/src/main/cpp/shadowhook/common/bytesig.c b/app/src/main/cpp/shadowhook/common/bytesig.c new file mode 100644 index 0000000000000000000000000000000000000000..8568279f4987369df0465c776eddecc6d9ddaf04 --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/bytesig.c @@ -0,0 +1,291 @@ +// Copyright (c) 2021-2023 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +// version 1.0.4 + +#include "bytesig.h" + +#include <dlfcn.h> +#include <errno.h> +#include <pthread.h> +#include <setjmp.h> +#include <signal.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <sys/syscall.h> +#include <unistd.h> + +#define BYTESIG_DEBUG 0 + +#if BYTESIG_DEBUG +#include <android/log.h> +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#define BYTESIG_LOG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "bytesig_tag", fmt, ##__VA_ARGS__) +#pragma clang diagnostic pop +#else +#define BYTESIG_LOG(fmt, ...) +#endif + +typedef enum { + BYTESIG_STATUS_UNAVAILABLE = 0, + BYTESIG_STATUS_SIG32 = 1, // use sigset_t + BYTESIG_STATUS_SIG64 = 2 // use sigset64_t +} bytesig_status_t; +static bytesig_status_t bytesig_status = BYTESIG_STATUS_UNAVAILABLE; + +extern __attribute((weak)) int sigfillset64(sigset64_t *); +extern __attribute((weak)) int sigemptyset64(sigset64_t *); +extern __attribute((weak)) int sigaddset64(sigset64_t *, int); +extern __attribute((weak)) int sigismember64(const sigset64_t *, int); + +typedef int (*bytesig_sigaction64_t)(int, const struct sigaction64 *, struct sigaction64 *); +typedef int (*bytesig_sigaction_t)(int, const struct sigaction *, struct sigaction *); +typedef int (*bytesig_sigprocmask64_t)(int, const sigset64_t *, sigset64_t *); +typedef int (*bytesig_sigprocmask_t)(int, const sigset_t *, sigset_t *); + +static void *bytesig_sigaction; // point to libc's sigaction64() or libc's sigaction() +static void *bytesig_sigprocmask; // point to libc's sigprocmask() or libc's sigprocmask64() + +__attribute__((constructor)) static void bytesig_ctor(void) { + void *libc = dlopen("libc.so", RTLD_LOCAL); + if (__predict_false(NULL == libc)) return; + + if (__predict_true(NULL != sigfillset64 && NULL != sigemptyset64 && NULL != sigaddset64 && + NULL != sigismember64)) { + if (__predict_true(NULL != (bytesig_sigaction = dlsym(libc, "sigaction64")) && + NULL != (bytesig_sigprocmask = dlsym(libc, "sigprocmask64")))) { + bytesig_status = BYTESIG_STATUS_SIG64; + goto end; + } + } + + if (__predict_true(NULL != (bytesig_sigaction = dlsym(libc, "sigaction")) && + NULL != (bytesig_sigprocmask = dlsym(libc, "sigprocmask")))) { + bytesig_status = BYTESIG_STATUS_SIG32; + } + +end: + dlclose(libc); +} + +#define BYTESIG_PROTECTED_THREADS_MAX 256 + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct { + pid_t tids[BYTESIG_PROTECTED_THREADS_MAX]; + sigjmp_buf *jbufs[BYTESIG_PROTECTED_THREADS_MAX]; + union { + struct sigaction64 prev_action64; + struct sigaction prev_action; + }; +} bytesig_signal_t; +#pragma clang diagnostic pop + +// array index is signal number, corresponds to signals 1 to 31, except 9 and 19 +static bytesig_signal_t *bytesig_signal_array[__SIGRTMIN]; + +static void bytesig_sigorset64(sigset64_t *dest, sigset64_t *left, sigset64_t *right) { + sigemptyset64(dest); + for (size_t i = 1; i < sizeof(sigset64_t) * CHAR_BIT; i++) + if (sigismember64(left, (int)i) == 1 || sigismember64(right, (int)i) == 1) sigaddset64(dest, (int)i); +} + +static void bytesig_sigorset(sigset_t *dest, sigset_t *left, sigset_t *right) { + sigemptyset(dest); + for (size_t i = 1; i < sizeof(sigset_t) * CHAR_BIT; i++) + if (sigismember(left, (int)i) == 1 || sigismember(right, (int)i) == 1) sigaddset(dest, (int)i); +} + +__attribute__((noinline)) static void bytesig_handler_internal(int signum, siginfo_t *siginfo, + void *context) { + bytesig_signal_t *sig = bytesig_signal_array[signum]; + + // check protect info & do siglongjmp + pid_t tid = gettid(); + if (__predict_false(0 == tid)) tid = (pid_t)syscall(SYS_gettid); + for (size_t i = 0; i < BYTESIG_PROTECTED_THREADS_MAX; i++) { + if (__predict_false(tid == __atomic_load_n(&(sig->tids[i]), __ATOMIC_RELAXED))) { + BYTESIG_LOG("siglongjmp signal %d (code %d) at %zu", signum, siginfo->si_code, i); + + unsigned int ret_signum = (((unsigned int)signum & 0xFFU) << 16U); + unsigned int ret_code = 0U; + if (siginfo->si_code > 0) + ret_code = (((unsigned int)(siginfo->si_code) & 0xFFU) << 8U); + else if (siginfo->si_code < 0) + ret_code = (unsigned int)(-(siginfo->si_code)) & 0xFFU; + int ret_val = (int)(ret_signum | ret_code); + + siglongjmp(*(__atomic_load_n(&(sig->jbufs[i]), __ATOMIC_RELAXED)), ret_val); + } + } + +#define SET_THREAD_SIGNAL_MASK(suffix) \ + do { \ + sigset##suffix##_t prev_mask; \ + bytesig_sigorset##suffix(&prev_mask, &(((ucontext_t *)context)->uc_sigmask##suffix), \ + &(sig->prev_action##suffix.sa_mask)); \ + if (0 == ((unsigned int)(sig->prev_action##suffix.sa_flags) & (unsigned int)SA_NODEFER)) \ + sigaddset##suffix(&prev_mask, signum); \ + sigaddset##suffix(&prev_mask, SIGPIPE); \ + sigaddset##suffix(&prev_mask, SIGUSR1); \ + sigaddset##suffix(&prev_mask, SIGQUIT); \ + ((bytesig_sigprocmask##suffix##_t)bytesig_sigprocmask)(SIG_SETMASK, &prev_mask, NULL); \ + } while (0) + + // set thread signal mask + if (BYTESIG_STATUS_SIG64 == bytesig_status) + SET_THREAD_SIGNAL_MASK(64); + else + SET_THREAD_SIGNAL_MASK(); +} + +// https://llvm.org/docs/CodeGenerator.html#tail-call-optimization +// https://clang.llvm.org/docs/AttributeReference.html#disable-tail-calls +//__attribute__((disable_tail_calls)) +static void bytesig_handler(int signum, siginfo_t *siginfo, void *context) { + bytesig_handler_internal(signum, siginfo, context); + +#define CALL_PREVIOUS_SIGNAL_HANDLER(suffix) \ + do { \ + if (__predict_true(sig->prev_action##suffix.sa_flags & SA_SIGINFO)) \ + sig->prev_action##suffix.sa_sigaction(signum, siginfo, context); \ + else if (SIG_DFL != sig->prev_action##suffix.sa_handler && \ + SIG_IGN != sig->prev_action##suffix.sa_handler) \ + sig->prev_action##suffix.sa_handler(signum); \ + } while (0) + + // call previous signal handler + bytesig_signal_t *sig = bytesig_signal_array[signum]; + if (BYTESIG_STATUS_SIG64 == bytesig_status) + CALL_PREVIOUS_SIGNAL_HANDLER(64); + else + CALL_PREVIOUS_SIGNAL_HANDLER(); +} + +int bytesig_init(int signum) { + if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP)) + return -1; + if (__predict_false(BYTESIG_STATUS_UNAVAILABLE == bytesig_status)) return -1; + if (__predict_false(NULL != bytesig_signal_array[signum])) return -1; + + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&lock); + int ret = -1; + if (__predict_false(NULL != bytesig_signal_array[signum])) goto end; + + bytesig_signal_t *sig = calloc(1, sizeof(bytesig_signal_t)); + if (__predict_false(NULL == sig)) goto end; + +#define SA_EXPOSE_TAGBITS 0x00000800 + +#define REGISTER_SIGNAL_HANDLER(suffix) \ + do { \ + struct sigaction##suffix act; \ + memset(&act, 0, sizeof(struct sigaction##suffix)); \ + sigfillset##suffix(&act.sa_mask); \ + act.sa_sigaction = bytesig_handler; \ + act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART | SA_EXPOSE_TAGBITS; \ + if (__predict_false( \ + 0 != \ + ((bytesig_sigaction##suffix##_t)bytesig_sigaction)(signum, &act, &sig->prev_action##suffix))) { \ + free(sig); \ + goto end; \ + } \ + } while (0) + + // register the signal handler, we start off with all signals blocked + if (BYTESIG_STATUS_SIG64 == bytesig_status) + REGISTER_SIGNAL_HANDLER(64); + else + REGISTER_SIGNAL_HANDLER(); + + bytesig_signal_array[signum] = sig; + ret = 0; // OK + +end: + pthread_mutex_unlock(&lock); + return ret; +} + +void bytesig_protect(pid_t tid, sigjmp_buf *jbuf, const int signums[], size_t signums_cnt) { + for (size_t i = 0; i < signums_cnt; i++) { + int signum = signums[i]; + if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP)) + continue; + + bytesig_signal_t *sig = bytesig_signal_array[signum]; + if (__predict_false(NULL == sig)) continue; + + // check repeated thread + bool repeated = false; + for (size_t j = 0; j < BYTESIG_PROTECTED_THREADS_MAX; j++) { + if (__predict_false(tid == sig->tids[j])) { + repeated = true; + break; + } + } + if (__predict_false(repeated)) continue; + + // save thread-ID and jump buffer + size_t j = 0; + while (1) { + if (0 == sig->tids[j]) { + pid_t expected = 0; + if (__atomic_compare_exchange_n(&sig->tids[j], &expected, tid, false, __ATOMIC_ACQUIRE, + __ATOMIC_RELAXED)) { + sig->jbufs[j] = jbuf; + BYTESIG_LOG("protect_start signal %d at %zu", signum, j); + break; // finished + } + } + + j++; + if (__predict_false(BYTESIG_PROTECTED_THREADS_MAX == j)) j = 0; + } + } +} + +void bytesig_unprotect(pid_t tid, const int signums[], size_t signums_cnt) { + for (size_t i = 0; i < signums_cnt; i++) { + int signum = signums[i]; + if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP)) + continue; + + bytesig_signal_t *sig = bytesig_signal_array[signum]; + if (__predict_false(NULL == sig)) continue; + + // free thread-ID and jump buffer + for (size_t j = 0; j < BYTESIG_PROTECTED_THREADS_MAX; j++) { + if (tid == sig->tids[j]) { + sig->jbufs[j] = NULL; + __atomic_store_n(&sig->tids[j], 0, __ATOMIC_RELEASE); + BYTESIG_LOG("protect_end signal %d at %zu", signum, j); + break; // finished + } + } + } +} diff --git a/app/src/main/cpp/shadowhook/common/bytesig.h b/app/src/main/cpp/shadowhook/common/bytesig.h new file mode 100644 index 0000000000000000000000000000000000000000..a7de52682ef43a49b3900c0c53ba6902682b21e6 --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/bytesig.h @@ -0,0 +1,157 @@ +// Copyright (c) 2021-2023 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +// version 1.0.4 + +/* + * #include "bytesig.h" + * + * void init_once(void) + * { + * bytesig_init(SIGSEGV); + * bytesig_init(SIGBUS); + * bytesig_init(SIGILL); + * bytesig_init(SIGABRT); + * // ...... + * } + * + * void unstable_func(void) + * { + * int *p = NULL; + * + * // + * // usage 1 + * // + * BYTESIG_TRY(SIGSEGV, SIGBUS) + * { + * *p = 1; + * } + * BYTESIG_CATCH(signum, code) + * { + * LOG("signum %d (code %d)", signum, code); + * } + * BYTESIG_EXIT + * + * // + * // usage 2 + * // + * BYTESIG_TRY(SIGSEGV, SIGBUS) + * { + * *p = 2; + * } + * BYTESIG_CATCH(signum) + * { + * LOG("signum %d", signum); + * } + * BYTESIG_EXIT + * + * // + * // usage 3 + * // + * BYTESIG_TRY(SIGILL) + * { + * func_maybe_illed(); + * } + * BYTESIG_CATCH() + * { + * do_something(); + * } + * BYTESIG_EXIT + * + * // + * // usage 4 + * // + * BYTESIG_TRY(SIGABRT) + * { + * func_maybe_aborted(); + * } + * BYTESIG_EXIT + * } + */ + +#ifndef BYTEDANCE_BYTESIG +#define BYTEDANCE_BYTESIG + +#include <setjmp.h> +#include <signal.h> +#include <stddef.h> +#include <sys/syscall.h> +#include <unistd.h> + +#define BYTESIG_TRY(...) \ + do { \ + pid_t _bytesig_tid_ = gettid(); \ + if (0 == _bytesig_tid_) _bytesig_tid_ = (pid_t)syscall(SYS_gettid); \ + sigjmp_buf _bytesig_jbuf_; \ + int _bytesig_sigs_[] = {__VA_ARGS__}; \ + bytesig_protect(_bytesig_tid_, &_bytesig_jbuf_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \ + int _bytesig_protected_ = 1; \ + int _bytesig_ex_ = sigsetjmp(_bytesig_jbuf_, 1); \ + if (0 == _bytesig_ex_) { +#define BYTESIG_CATCH_2(signum_, code_) \ + } \ + else { \ + bytesig_unprotect(_bytesig_tid_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \ + _bytesig_protected_ = 0; \ + int signum_ = (int)(((unsigned int)_bytesig_ex_ & 0xFF0000U) >> 16U); \ + int code_ = 0; \ + if (((unsigned int)_bytesig_ex_ & 0xFF00U) > 0) \ + code_ = (int)(((unsigned int)_bytesig_ex_ & 0xFF00U) >> 8U); \ + else if (((unsigned int)_bytesig_ex_ & 0xFFU) > 0) \ + code_ = -((int)((unsigned int)_bytesig_ex_ & 0xFFU)); \ + (void)signum_; \ + (void)code_; + +#define BYTESIG_CATCH_1(signum_) BYTESIG_CATCH_2(signum_, _bytesig_code_) +#define BYTESIG_CATCH_0() BYTESIG_CATCH_1(_bytesig_signum_) + +#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3 +#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses +#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, BYTESIG_CATCH_2, BYTESIG_CATCH_1, )) +#define NO_ARG_EXPANDER() , , BYTESIG_CATCH_0 +#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__()) + +#define BYTESIG_CATCH(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__) + +#define BYTESIG_EXIT \ + } \ + if (1 == _bytesig_protected_) \ + bytesig_unprotect(_bytesig_tid_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \ + } \ + while (0) \ + ; + +#ifdef __cplusplus +extern "C" { +#endif + +int bytesig_init(int signum); + +void bytesig_protect(pid_t tid, sigjmp_buf *jbuf, const int signums[], size_t signums_cnt); +void bytesig_unprotect(pid_t tid, const int signums[], size_t signums_cnt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/shadowhook/common/sh_config.h b/app/src/main/cpp/shadowhook/common/sh_config.h new file mode 100644 index 0000000000000000000000000000000000000000..464dea95519c0c77da1567cba0718518726bc155 --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_config.h @@ -0,0 +1,55 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once + +// Global debugging. +// +// Note that in some cases these logs themselves can cause a crash. +// +//#define SH_CONFIG_DEBUG + +// Operation record of hook and unhook. +// +// Disabling it can reduce hook/unhook latency, memory footprint and file size. +// +#define SH_CONFIG_OPERATION_RECORDS + +// Crash signal protection. +// +// Do not disable it in a production environment. +// +#define SH_CONFIG_SIG_PROT + +// When hooking, try to find an exit for the relative jump for the dynamic library. +// +// Do not disable it in a production environment. +// +#define SH_CONFIG_TRY_WITH_EXIT + +// When hooking the function of the thumb instruction, if the function is too short, +// try to use the gap aligned at the end of the function. +// +// Do not disable it in a production environment. +// +#define SH_CONFIG_DETECT_THUMB_TAIL_ALIGNED diff --git a/app/src/main/cpp/shadowhook/common/sh_errno.c b/app/src/main/cpp/shadowhook/common/sh_errno.c new file mode 100644 index 0000000000000000000000000000000000000000..e55891e40bd73e7fd3d9581eeffe7b48044f1bbd --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_errno.c @@ -0,0 +1,107 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_errno.h" + +#include <pthread.h> +#include <stdbool.h> + +#include "shadowhook.h" + +static int sh_errno_global = SHADOWHOOK_ERRNO_UNINIT; +static pthread_key_t sh_errno_tls_key; + +int sh_errno_init(void) { + if (__predict_false(0 != pthread_key_create(&sh_errno_tls_key, NULL))) { + sh_errno_global = SHADOWHOOK_ERRNO_INIT_ERRNO; + return -1; + } + sh_errno_global = SHADOWHOOK_ERRNO_OK; + return 0; +} + +void sh_errno_reset(void) { + sh_errno_set(0); +} + +void sh_errno_set(int error_number) { + if (sh_errno_global == SHADOWHOOK_ERRNO_UNINIT || sh_errno_global == SHADOWHOOK_ERRNO_INIT_ERRNO) return; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" + pthread_setspecific(sh_errno_tls_key, (void *)error_number); +#pragma clang diagnostic pop +} + +int sh_errno_get(void) { + if (sh_errno_global == SHADOWHOOK_ERRNO_UNINIT || sh_errno_global == SHADOWHOOK_ERRNO_INIT_ERRNO) + return sh_errno_global; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvoid-pointer-to-int-cast" + return (int)(pthread_getspecific(sh_errno_tls_key)); +#pragma clang diagnostic pop +} + +const char *sh_errno_to_errmsg(int error_number) { + static const char *msg[] = {/* 0 */ "OK", + /* 1 */ "Pending task", + /* 2 */ "Not initialized", + /* 3 */ "Invalid argument", + /* 4 */ "Out of memory", + /* 5 */ "MProtect failed", + /* 6 */ "Write to arbitrary address crashed", + /* 7 */ "Init errno mod failed", + /* 8 */ "Init bytesig SIGSEGV mod failed", + /* 9 */ "Init bytesig SIGBUS mod failed", + /* 10 */ "Init enter mod failed", + /* 11 */ "Init safe mod failed", + /* 12 */ "Init linker mod failed", + /* 13 */ "Init hub mod failed", + /* 14 */ "Create hub failed", + /* 15 */ "Monitor dlopen failed", + /* 16 */ "Create monitor thread failed", + /* 17 */ "Open ELF crashed", + /* 18 */ "Find symbol in ELF failed", + /* 19 */ "Find symbol in ELF crashed", + /* 20 */ "Duplicate hook", + /* 21 */ "Dladdr crashed", + /* 22 */ "Find dlinfo failed", + /* 23 */ "Symbol size too small", + /* 24 */ "Alloc enter failed", + /* 25 */ "Instruction rewrite crashed", + /* 26 */ "Instruction rewrite failed", + /* 27 */ "Switch not found", + /* 28 */ "Verify original instruction crashed", + /* 29 */ "Verify original instruction failed", + /* 30 */ "Exit instruction mismatch", + /* 31 */ "Free exit crashed", + /* 32 */ "Unhook on an error status task", + /* 33 */ "Unhook on an unfinished task", + /* 34 */ "ELF with an unsupported architecture", + /* 35 */ "Linker with an unsupported architecture"}; + + if (error_number < 0 || error_number >= (int)(sizeof(msg) / sizeof(msg[0]))) return "Unknown error number"; + + return msg[error_number]; +} diff --git a/app/src/main/cpp/shadowhook/common/sh_errno.h b/app/src/main/cpp/shadowhook/common/sh_errno.h new file mode 100644 index 0000000000000000000000000000000000000000..10f017b988e1a0d9251d02e4bfe3c9c6a8023543 --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_errno.h @@ -0,0 +1,40 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include "sh_log.h" + +#define SH_ERRNO_SET_RET_ERRNUM(errnum) SH_ERRNO_SET_RET((errnum), (errnum)) +#define SH_ERRNO_SET_RET_FAIL(errnum) SH_ERRNO_SET_RET((errnum), -1) +#define SH_ERRNO_SET_RET_NULL(errnum) SH_ERRNO_SET_RET((errnum), NULL) +#define SH_ERRNO_SET_RET(errnum, ret) \ + do { \ + sh_errno_set((errnum)); \ + return (ret); \ + } while (0) + +int sh_errno_init(void); +void sh_errno_reset(void); +void sh_errno_set(int error_number); +int sh_errno_get(void); +const char *sh_errno_to_errmsg(int error_number); diff --git a/app/src/main/cpp/shadowhook/common/sh_log.c b/app/src/main/cpp/shadowhook/common/sh_log.c new file mode 100644 index 0000000000000000000000000000000000000000..26327233341e1df448f9a1c5f4a720a64ab01479 --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_log.c @@ -0,0 +1,53 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_log.h" + +#include <android/log.h> +#include <stdbool.h> + +#include "sh_config.h" + +android_LogPriority sh_log_priority = +#ifdef SH_CONFIG_DEBUG + ANDROID_LOG_INFO +#else + ANDROID_LOG_SILENT +#endif + ; + +bool sh_log_get_debuggable(void) { + return sh_log_priority <= ANDROID_LOG_INFO; +} + +void sh_log_set_debuggable(bool debuggable) { +#ifdef SH_CONFIG_DEBUG + (void)debuggable; + sh_log_priority = ANDROID_LOG_INFO; +#else + if (__predict_false(debuggable)) + sh_log_priority = ANDROID_LOG_INFO; + else + sh_log_priority = ANDROID_LOG_SILENT; +#endif +} diff --git a/app/src/main/cpp/shadowhook/common/sh_log.h b/app/src/main/cpp/shadowhook/common/sh_log.h new file mode 100644 index 0000000000000000000000000000000000000000..fff786f6cddf48bb4189e670e730f5dce7f5d4cf --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_log.h @@ -0,0 +1,70 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <android/log.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> + +extern android_LogPriority sh_log_priority; + +#define SH_LOG_TAG "shadowhook_tag" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" + +// Notice: +// We don't use ANDROID_LOG_DEBUG, because some Android devices will filter out ANDROID_LOG_DEBUG. +#ifdef SH_CONFIG_DEBUG +#define SH_LOG_DEBUG(fmt, ...) \ + do { \ + if (sh_log_priority <= ANDROID_LOG_INFO) \ + __android_log_print(ANDROID_LOG_INFO, SH_LOG_TAG, fmt, ##__VA_ARGS__); \ + } while (0) +#else +#define SH_LOG_DEBUG(fmt, ...) +#endif + +#define SH_LOG_INFO(fmt, ...) \ + do { \ + if (__predict_false(sh_log_priority <= ANDROID_LOG_INFO)) \ + __android_log_print(ANDROID_LOG_INFO, SH_LOG_TAG, fmt, ##__VA_ARGS__); \ + } while (0) +#define SH_LOG_WARN(fmt, ...) \ + do { \ + if (__predict_false(sh_log_priority <= ANDROID_LOG_WARN)) \ + __android_log_print(ANDROID_LOG_WARN, SH_LOG_TAG, fmt, ##__VA_ARGS__); \ + } while (0) +#define SH_LOG_ERROR(fmt, ...) \ + do { \ + if (__predict_false(sh_log_priority <= ANDROID_LOG_ERROR)) \ + __android_log_print(ANDROID_LOG_ERROR, SH_LOG_TAG, fmt, ##__VA_ARGS__); \ + } while (0) +#define SH_LOG_ALWAYS_SHOW(fmt, ...) __android_log_print(ANDROID_LOG_WARN, SH_LOG_TAG, fmt, ##__VA_ARGS__) + +#pragma clang diagnostic pop + +bool sh_log_get_debuggable(void); +void sh_log_set_debuggable(bool debuggable); diff --git a/app/src/main/cpp/shadowhook/common/sh_sig.h b/app/src/main/cpp/shadowhook/common/sh_sig.h new file mode 100644 index 0000000000000000000000000000000000000000..5b926ae15913c04e699fe4fbf1d6df72654ce6c5 --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_sig.h @@ -0,0 +1,49 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once + +#include "sh_config.h" + +#ifdef SH_CONFIG_SIG_PROT + +#include "bytesig.h" +#define SH_SIG_TRY BYTESIG_TRY +#define SH_SIG_CATCH BYTESIG_CATCH +#define SH_SIG_EXIT BYTESIG_EXIT + +#else + +#define SH_SIG_TRY(...) \ + do { \ + if (0 == 0) { +#define SH_SIG_CATCH(...) \ + } \ + else { +#define SH_SIG_EXIT \ + } \ + } \ + while (0) \ + ; + +#endif diff --git a/app/src/main/cpp/shadowhook/common/sh_trampo.c b/app/src/main/cpp/shadowhook/common/sh_trampo.c new file mode 100644 index 0000000000000000000000000000000000000000..894273cdf5cdd8c8f3aa5f435dac456459e00c6a --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_trampo.c @@ -0,0 +1,172 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_trampo.h" + +#include <pthread.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <sys/time.h> + +#include "queue.h" +#include "sh_util.h" + +#define SH_TRAMPO_PAGE_SZ 4096 +#define SH_TRAMPO_ALIGN 4 + +void sh_trampo_init_mgr(sh_trampo_mgr_t *mem_mgr, const char *page_name, size_t trampo_size, + time_t delay_sec) { + SLIST_INIT(&mem_mgr->pages); + pthread_mutex_init(&mem_mgr->pages_lock, NULL); + mem_mgr->page_name = page_name; + mem_mgr->trampo_size = SH_UTIL_ALIGN_END(trampo_size, SH_TRAMPO_ALIGN); + mem_mgr->delay_sec = delay_sec; +} + +uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mem_mgr, uintptr_t hint, uintptr_t range_low, + uintptr_t range_high) { + uintptr_t mem = 0; + uintptr_t new_ptr; + uintptr_t new_ptr_prctl = (uintptr_t)MAP_FAILED; + size_t count = SH_TRAMPO_PAGE_SZ / mem_mgr->trampo_size; + + if (range_low > hint) range_low = hint; + if (range_high > UINTPTR_MAX - hint) range_high = UINTPTR_MAX - hint; + + struct timeval now; + if (mem_mgr->delay_sec > 0) gettimeofday(&now, NULL); + + pthread_mutex_lock(&mem_mgr->pages_lock); + + // try to find an unused mem + sh_trampo_page_t *page; + SLIST_FOREACH(page, &mem_mgr->pages, link) { + // check hit range + uintptr_t page_trampo_start = page->ptr; + uintptr_t page_trampo_end = page->ptr + SH_TRAMPO_PAGE_SZ - mem_mgr->trampo_size; + if (hint > 0 && ((page_trampo_end < hint - range_low) || (hint + range_high < page_trampo_start))) + continue; + + for (uintptr_t i = 0; i < count; i++) { + size_t flags_idx = i / 32; + uint32_t mask = (uint32_t)1 << (i % 32); + if (0 == (page->flags[flags_idx] & mask)) // check flag + { + // check timestamp + if (mem_mgr->delay_sec > 0 && + (now.tv_sec <= page->timestamps[i] || now.tv_sec - page->timestamps[i] <= mem_mgr->delay_sec)) + continue; + + // check hit range + uintptr_t cur = page->ptr + (mem_mgr->trampo_size * i); + if (hint > 0 && ((cur < hint - range_low) || (hint + range_high < cur))) continue; + + // OK + page->flags[flags_idx] |= mask; + mem = cur; + memset((void *)mem, 0, mem_mgr->trampo_size); + goto end; + } + } + } + + // alloc a new mem page + new_ptr = (uintptr_t)(mmap(hint > 0 ? (void *)(hint - range_low) : NULL, SH_TRAMPO_PAGE_SZ, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + if ((uintptr_t)MAP_FAILED == new_ptr) goto err; + new_ptr_prctl = new_ptr; + + // check hit range + if (hint > 0 && ((hint - range_low >= new_ptr + SH_TRAMPO_PAGE_SZ - mem_mgr->trampo_size) || + (hint + range_high < new_ptr))) + goto err; + + // create a new trampo-page info + if (NULL == (page = calloc(1, sizeof(sh_trampo_page_t)))) goto err; + memset((void *)new_ptr, 0, SH_TRAMPO_PAGE_SZ); + page->ptr = new_ptr; + new_ptr = (uintptr_t)MAP_FAILED; + if (NULL == (page->flags = calloc(1, SH_UTIL_ALIGN_END(count, 32) / 8))) goto err; + page->timestamps = NULL; + if (mem_mgr->delay_sec > 0) { + if (NULL == (page->timestamps = calloc(1, count * sizeof(time_t)))) goto err; + } + SLIST_INSERT_HEAD(&mem_mgr->pages, page, link); + + // alloc mem from the new mem page + for (uintptr_t i = 0; i < count; i++) { + size_t flags_idx = i / 32; + uint32_t mask = (uint32_t)1 << (i % 32); + + // check hit range + uintptr_t find = page->ptr + (mem_mgr->trampo_size * i); + if (hint > 0 && ((find < hint - range_low) || (hint + range_high < find))) continue; + + // OK + page->flags[flags_idx] |= mask; + mem = find; + break; + } + if (0 == mem) abort(); + +end: + pthread_mutex_unlock(&mem_mgr->pages_lock); + if ((uintptr_t)MAP_FAILED != new_ptr_prctl) + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, new_ptr_prctl, SH_TRAMPO_PAGE_SZ, mem_mgr->page_name); + return mem; + +err: + pthread_mutex_unlock(&mem_mgr->pages_lock); + if (NULL != page) { + if (0 != page->ptr) munmap((void *)page->ptr, SH_TRAMPO_PAGE_SZ); + if (NULL != page->flags) free(page->flags); + if (NULL != page->timestamps) free(page->timestamps); + free(page); + } + if ((uintptr_t)MAP_FAILED != new_ptr) munmap((void *)new_ptr, SH_TRAMPO_PAGE_SZ); + return 0; +} + +void sh_trampo_free(sh_trampo_mgr_t *mem_mgr, uintptr_t mem) { + struct timeval now; + if (mem_mgr->delay_sec > 0) gettimeofday(&now, NULL); + + pthread_mutex_lock(&mem_mgr->pages_lock); + + sh_trampo_page_t *page; + SLIST_FOREACH(page, &mem_mgr->pages, link) { + if (page->ptr <= mem && mem < page->ptr + SH_TRAMPO_PAGE_SZ) { + uintptr_t i = (mem - page->ptr) / mem_mgr->trampo_size; + size_t flags_idx = i / 32; + uint32_t mask = (uint32_t)1 << (i % 32); + if (mem_mgr->delay_sec > 0) page->timestamps[i] = now.tv_sec; + page->flags[flags_idx] &= ~mask; + break; + } + } + + pthread_mutex_unlock(&mem_mgr->pages_lock); +} diff --git a/app/src/main/cpp/shadowhook/common/sh_trampo.h b/app/src/main/cpp/shadowhook/common/sh_trampo.h new file mode 100644 index 0000000000000000000000000000000000000000..0a20964f4a12d3a9ae97186dee90d084155e0903 --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_trampo.h @@ -0,0 +1,52 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stddef.h> +#include <stdint.h> +#include <sys/time.h> + +#include "queue.h" + +typedef struct sh_trampo_page { + uintptr_t ptr; + uint32_t *flags; + time_t *timestamps; + SLIST_ENTRY(sh_trampo_page, ) link; +} sh_trampo_page_t; +typedef SLIST_HEAD(sh_trampo_page_list, sh_trampo_page, ) sh_trampo_page_list_t; + +typedef struct sh_trampo_mgr { + sh_trampo_page_list_t pages; + pthread_mutex_t pages_lock; + const char *page_name; + size_t trampo_size; + time_t delay_sec; +} sh_trampo_mgr_t; + +void sh_trampo_init_mgr(sh_trampo_mgr_t *mem_mgr, const char *page_name, size_t trampo_size, + time_t delay_sec); + +uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mem_mgr, uintptr_t hint, uintptr_t low_offset, + uintptr_t high_offset); +void sh_trampo_free(sh_trampo_mgr_t *mem_mgr, uintptr_t mem); diff --git a/app/src/main/cpp/shadowhook/common/sh_util.c b/app/src/main/cpp/shadowhook/common/sh_util.c new file mode 100644 index 0000000000000000000000000000000000000000..b8604c8bad609e314a8b3419d78b8afd6fee09ff --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_util.c @@ -0,0 +1,538 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_util.h" + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <time.h> +#include <unistd.h> + +#include "sh_log.h" +#include "sh_sig.h" +#include "shadowhook.h" + +int sh_util_mprotect(uintptr_t addr, size_t len, int prot) { + uintptr_t start = SH_UTIL_PAGE_START(addr); + uintptr_t end = SH_UTIL_PAGE_END(addr + len - 1); + + return mprotect((void *)start, end - start, prot); +} + +void sh_util_clear_cache(uintptr_t addr, size_t len) { + __builtin___clear_cache((char *)addr, (char *)(addr + len)); +} + +bool sh_util_is_thumb32(uintptr_t target_addr) { + uint16_t opcode = *((uint16_t *)target_addr); + int tmp = opcode >> 11u; + return (tmp == 0x1d) || (tmp == 0x1e) || (tmp == 0x1f); +} + +static uint32_t sh_util_ror(uint32_t val, uint32_t n, uint32_t shift) { + uint32_t m = shift % n; + return (val >> m) | (val << (n - m)); +} + +uint32_t sh_util_arm_expand_imm(uint32_t opcode) { + uint32_t imm = SH_UTIL_GET_BITS_32(opcode, 7, 0); + uint32_t amt = 2 * SH_UTIL_GET_BITS_32(opcode, 11, 8); + + return amt == 0 ? imm : sh_util_ror(imm, 32, amt); +} + +int sh_util_write_inst(uintptr_t target_addr, void *inst, size_t inst_len) { + if (0 != sh_util_mprotect(target_addr, inst_len, PROT_READ | PROT_WRITE | PROT_EXEC)) + return SHADOWHOOK_ERRNO_MPROT; + + SH_SIG_TRY(SIGSEGV, SIGBUS) { + if ((4 == inst_len) && (0 == target_addr % 4)) + __atomic_store_n((uint32_t *)target_addr, *((uint32_t *)inst), __ATOMIC_SEQ_CST); + else if ((8 == inst_len) && (0 == target_addr % 8)) + __atomic_store_n((uint64_t *)target_addr, *((uint64_t *)inst), __ATOMIC_SEQ_CST); +#ifdef __LP64__ + else if ((16 == inst_len) && (0 == target_addr % 16)) + __atomic_store_n((__int128 *)target_addr, *((__int128 *)inst), __ATOMIC_SEQ_CST); +#endif + else + memcpy((void *)target_addr, inst, inst_len); + + sh_util_clear_cache(target_addr, inst_len); + } + SH_SIG_CATCH() { + return SHADOWHOOK_ERRNO_WRITE_CRASH; + } + SH_SIG_EXIT + + return 0; // OK +} + +static bool sh_util_starts_with(const char *str, const char *start) { + while (*str && *str == *start) { + str++; + start++; + } + + return '\0' == *start; +} + +static int sh_util_get_api_level_from_build_prop(void) { + char buf[128]; + int api_level = -1; + + FILE *fp = fopen("/system/build.prop", "r"); + if (__predict_false(NULL == fp)) goto end; + + while (fgets(buf, sizeof(buf), fp)) { + if (__predict_false(sh_util_starts_with(buf, "ro.build.version.sdk="))) { + api_level = atoi(buf + 21); + break; + } + } + fclose(fp); + +end: + return (api_level > 0) ? api_level : -1; +} + +int sh_util_get_api_level(void) { + static int xdl_util_api_level = -1; + + if (__predict_false(xdl_util_api_level < 0)) { + int api_level = android_get_device_api_level(); + if (__predict_false(api_level < 0)) + api_level = sh_util_get_api_level_from_build_prop(); // compatible with unusual models + if (__predict_false(api_level < __ANDROID_API_J__)) api_level = __ANDROID_API_J__; + + __atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST); + } + + return xdl_util_api_level; +} + +int sh_util_write(int fd, const char *buf, size_t buf_len) { + if (fd < 0) return -1; + + const char *ptr = buf; + size_t nleft = buf_len; + + while (nleft > 0) { + errno = 0; + ssize_t nwritten = write(fd, ptr, nleft); + if (nwritten <= 0) { + if (nwritten < 0 && errno == EINTR) + nwritten = 0; // call write() again + else + return -1; // error + } + nleft -= (size_t)nwritten; + ptr += nwritten; + } + + return 0; +} + +/* Nonzero if YEAR is a leap year (every 4 years, + except every 100th isn't, and every 400th is). */ +#define SH_UTIL_ISLEAP(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + +#define SH_UTIL_SECS_PER_HOUR (60 * 60) +#define SH_UTIL_SECS_PER_DAY (SH_UTIL_SECS_PER_HOUR * 24) +#define SH_UTIL_DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define SH_UTIL_LEAPS_THRU_END_OF(y) (SH_UTIL_DIV(y, 4) - SH_UTIL_DIV(y, 100) + SH_UTIL_DIV(y, 400)) + +static const unsigned short int sh_util_mon_yday[2][13] = { + /* Normal years. */ + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + /* Leap years. */ + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}}; + +/* Compute the `struct tm' representation of *T, + offset GMTOFF seconds east of UTC, + and store year, yday, mon, mday, wday, hour, min, sec into *RESULT. + Return RESULT if successful. */ +struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result) { + time_t days, rem, y; + const unsigned short int *ip; + + if (NULL == result) return NULL; + + result->tm_gmtoff = gmtoff; + + days = ((*timep) / SH_UTIL_SECS_PER_DAY); + rem = ((*timep) % SH_UTIL_SECS_PER_DAY); + rem += gmtoff; + while (rem < 0) { + rem += SH_UTIL_SECS_PER_DAY; + --days; + } + while (rem >= SH_UTIL_SECS_PER_DAY) { + rem -= SH_UTIL_SECS_PER_DAY; + ++days; + } + result->tm_hour = (int)(rem / SH_UTIL_SECS_PER_HOUR); + rem %= SH_UTIL_SECS_PER_HOUR; + result->tm_min = (int)(rem / 60L); + result->tm_sec = (int)(rem % 60L); + /* January 1, 1970 was a Thursday. */ + result->tm_wday = (int)(4 + days) % 7; + if (result->tm_wday < 0) result->tm_wday += 7; + y = 1970; + + while (days < 0 || days >= (SH_UTIL_ISLEAP(y) ? 366 : 365)) { + /* Guess a corrected year, assuming 365 days per year. */ + time_t yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + SH_UTIL_LEAPS_THRU_END_OF(yg - 1) - SH_UTIL_LEAPS_THRU_END_OF(y - 1)); + + y = yg; + } + result->tm_year = (int)(y - 1900); + if (result->tm_year != y - 1900) { + /* The year cannot be represented due to overflow. */ + errno = EOVERFLOW; + return NULL; + } + result->tm_yday = (int)days; + ip = sh_util_mon_yday[SH_UTIL_ISLEAP(y)]; + for (y = 11; days < (long int)ip[y]; --y) continue; + days -= ip[y]; + result->tm_mon = (int)y; + result->tm_mday = (int)(days + 1); + return result; +} + +static unsigned sh_util_parse_decimal(const char *format, int *ppos) { + const char *p = format + *ppos; + unsigned result = 0; + for (;;) { + int ch = *p; + unsigned d = (unsigned)(ch - '0'); + if (d >= 10U) { + break; + } + result = result * 10 + d; + p++; + } + *ppos = (int)(p - format); + return result; +} + +static void sh_util_format_unsigned(char *buf, size_t buf_size, uint64_t value, int base, int caps) { + char *p = buf; + char *end = buf + buf_size - 1; + + // Generate digit string in reverse order. + while (value) { + unsigned d = (unsigned)(value % (uint64_t)base); + value /= (uint64_t)base; + if (p != end) { + char ch; + if (d < 10) { + ch = '0' + (char)d; + } else { + ch = (caps ? 'A' : 'a') + (char)(d - 10); + } + *p++ = ch; + } + } + + // Special case for 0. + if (p == buf) { + if (p != end) { + *p++ = '0'; + } + } + *p = '\0'; + + // Reverse digit string in-place. + size_t length = (size_t)(p - buf); + for (size_t i = 0, j = length - 1; i < j; ++i, --j) { + char ch = buf[i]; + buf[i] = buf[j]; + buf[j] = ch; + } +} + +static void sh_util_format_integer(char *buf, size_t buf_size, uint64_t value, char conversion) { + // Decode the conversion specifier. + int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o'); + int base = 10; + if (conversion == 'x' || conversion == 'X') { + base = 16; + } else if (conversion == 'o') { + base = 8; + } + int caps = (conversion == 'X'); + if (is_signed && (int64_t)(value) < 0) { + buf[0] = '-'; + buf += 1; + buf_size -= 1; + value = (uint64_t)(-(int64_t)(value)); + } + sh_util_format_unsigned(buf, buf_size, value, base, caps); +} + +// format stream +typedef struct { + size_t total; + char *pos; + size_t avail; +} sh_util_stream_t; + +static void sh_util_stream_init(sh_util_stream_t *self, char *buffer, size_t buffer_size) { + self->total = 0; + self->pos = buffer; + self->avail = buffer_size; + + if (self->avail > 0) self->pos[0] = '\0'; +} + +static size_t sh_util_stream_total(sh_util_stream_t *self) { + return self->total; +} + +static void sh_util_stream_send(sh_util_stream_t *self, const char *data, int len) { + if (len < 0) { + len = (int)strlen(data); + } + self->total += (size_t)len; + + if (self->avail <= 1) { + // no space to put anything else + return; + } + + if ((size_t)len >= self->avail) { + len = (int)(self->avail - 1); + } + + memcpy(self->pos, data, (size_t)len); + self->pos += len; + self->pos[0] = '\0'; + self->avail -= (size_t)len; +} + +static void sh_util_stream_send_repeat(sh_util_stream_t *self, char ch, int count) { + char pad[8]; + memset(pad, ch, sizeof(pad)); + + const int pad_size = (int)(sizeof(pad)); + while (count > 0) { + int avail = count; + if (avail > pad_size) { + avail = pad_size; + } + sh_util_stream_send(self, pad, avail); + count -= avail; + } +} + +static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, va_list args) { + int nn = 0; + + for (;;) { + int mm; + int padZero = 0; + int padLeft = 0; + char sign = '\0'; + int width = -1; + int prec = -1; + size_t bytelen = sizeof(int); + int slen; + char buffer[32]; // temporary buffer used to format numbers + char c; + + // first, find all characters that are not 0 or '%', then send them to the output directly + mm = nn; + do { + c = format[mm]; + if (c == '\0' || c == '%') break; + mm++; + } while (1); + if (mm > nn) { + sh_util_stream_send(self, format + nn, mm - nn); + nn = mm; + } + + // is this it ? then exit + if (c == '\0') break; + + // nope, we are at a '%' modifier + nn++; // skip it + + // parse flags + for (;;) { + c = format[nn++]; + if (c == '\0') { + // single trailing '%' ? + c = '%'; + sh_util_stream_send(self, &c, 1); + return; + } else if (c == '0') { + padZero = 1; + continue; + } else if (c == '-') { + padLeft = 1; + continue; + } else if (c == ' ' || c == '+') { + sign = c; + continue; + } + break; + } + + // parse field width + if ((c >= '0' && c <= '9')) { + nn--; + width = (int)(sh_util_parse_decimal(format, &nn)); + c = format[nn++]; + } + + // parse precision + if (c == '.') { + prec = (int)(sh_util_parse_decimal(format, &nn)); + c = format[nn++]; + } + + // length modifier + switch (c) { + case 'h': + bytelen = sizeof(short); + if (format[nn] == 'h') { + bytelen = sizeof(char); + nn += 1; + } + c = format[nn++]; + break; + case 'l': + bytelen = sizeof(long); + if (format[nn] == 'l') { + bytelen = sizeof(long long); + nn += 1; + } + c = format[nn++]; + break; + case 'z': + bytelen = sizeof(size_t); + c = format[nn++]; + break; + case 't': + bytelen = sizeof(ptrdiff_t); + c = format[nn++]; + break; + default:; + } + + // conversion specifier + const char *str = buffer; + if (c == 's') { + // string + str = va_arg(args, const char *); + if (str == NULL) { + str = "(null)"; + } + } else if (c == 'c') { + // character + // NOTE: char is promoted to int when passed through the stack + buffer[0] = (char)(va_arg(args, int)); + buffer[1] = '\0'; + } else if (c == 'p') { + uint64_t value = (uintptr_t)(va_arg(args, void *)); + buffer[0] = '0'; + buffer[1] = 'x'; + sh_util_format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x'); + } else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') { + // integers - first read value from stack + uint64_t value; + int is_signed = (c == 'd' || c == 'i' || c == 'o'); + // NOTE: int8_t and int16_t are promoted to int when passed through the stack + switch (bytelen) { + case 1: + value = (uint8_t)(va_arg(args, int)); + break; + case 2: + value = (uint16_t)(va_arg(args, int)); + break; + case 4: + value = va_arg(args, uint32_t); + break; + case 8: + value = va_arg(args, uint64_t); + break; + default: + return; // should not happen + } + // sign extension, if needed + if (is_signed) { + int shift = (int)(64 - 8 * bytelen); + value = (uint64_t)(((int64_t)(value << shift)) >> shift); + } + // format the number properly into our buffer + sh_util_format_integer(buffer, sizeof(buffer), value, c); + } else if (c == '%') { + buffer[0] = '%'; + buffer[1] = '\0'; + } else { + //__assert(__FILE__, __LINE__, "conversion specifier unsupported"); + return; + } + + // if we are here, 'str' points to the content that must be outputted. + // handle padding and alignment now + slen = (int)strlen(str); + if (sign != '\0' || prec != -1) { + //__assert(__FILE__, __LINE__, "sign/precision unsupported"); + return; + } + if (slen < width && !padLeft) { + char padChar = padZero ? '0' : ' '; + sh_util_stream_send_repeat(self, padChar, width - slen); + } + sh_util_stream_send(self, str, slen); + if (slen < width && padLeft) { + char padChar = padZero ? '0' : ' '; + sh_util_stream_send_repeat(self, padChar, width - slen); + } + } +} + +size_t sh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args) { + sh_util_stream_t stream; + sh_util_stream_init(&stream, buffer, buffer_size); + sh_util_stream_vformat(&stream, format, args); + return sh_util_stream_total(&stream); +} + +size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...) { + va_list args; + va_start(args, format); + size_t buffer_len = sh_util_vsnprintf(buffer, buffer_size, format, args); + va_end(args); + return buffer_len; +} diff --git a/app/src/main/cpp/shadowhook/common/sh_util.h b/app/src/main/cpp/shadowhook/common/sh_util.h new file mode 100644 index 0000000000000000000000000000000000000000..c8d0515cf4ccdb11ed0b57f4759046d1cf6fada2 --- /dev/null +++ b/app/src/main/cpp/shadowhook/common/sh_util.h @@ -0,0 +1,96 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <android/api-level.h> +#include <errno.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <time.h> + +#define SH_UTIL_ALIGN_START(x, align) ((uintptr_t)(x) & ~((uintptr_t)(align)-1)) +#define SH_UTIL_ALIGN_END(x, align) (((uintptr_t)(x) + (uintptr_t)(align)-1) & ~((uintptr_t)(align)-1)) + +#define SH_UTIL_PAGE_START(x) SH_UTIL_ALIGN_START(x, 0x1000) +#define SH_UTIL_PAGE_END(x) SH_UTIL_ALIGN_END(x, 0x1000) + +#define SH_UTIL_IS_THUMB(addr) ((addr)&1u) +#define SH_UTIL_CLEAR_BIT0(addr) ((addr)&0xFFFFFFFE) +#define SH_UTIL_SET_BIT0(addr) ((addr) | 1u) + +#define SH_UTIL_ALIGN_4(pc) ((pc)&0xFFFFFFFC) +#define SH_UTIL_SIGN_EXTEND_32(n, len) \ + ((SH_UTIL_GET_BIT_32(n, len - 1) > 0) ? ((n) | (0xFFFFFFFF << (len))) : n) +#define SH_UTIL_SIGN_EXTEND_64(n, len) \ + ((SH_UTIL_GET_BIT_64(n, len - 1) > 0) ? ((n) | (0xFFFFFFFFFFFFFFFF << (len))) : n) + +#define SH_UTIL_GET_BIT_16(n, idx) ((uint16_t)((n) << (15u - (idx))) >> 15u) +#define SH_UTIL_GET_BITS_16(n, high, low) ((uint16_t)((n) << (15u - (high))) >> (15u - (high) + (low))) +#define SH_UTIL_GET_BIT_32(n, idx) ((uint32_t)((n) << (31u - (idx))) >> 31u) +#define SH_UTIL_GET_BITS_32(n, high, low) ((uint32_t)((n) << (31u - (high))) >> (31u - (high) + (low))) +#define SH_UTIL_GET_BIT_64(n, idx) ((uint64_t)((n) << (63u - (idx))) >> 63u) +#define SH_UTIL_GET_BITS_64(n, high, low) ((uint64_t)((n) << (63u - (high))) >> (63u - (high) + (low))) + +#define SH_UTIL_TEMP_FAILURE_RETRY(exp) \ + ({ \ + __typeof__(exp) _rc; \ + do { \ + errno = 0; \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; \ + }) + +#define SH_UTIL_MAX(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + +#define SH_UTIL_MIN(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) + +int sh_util_mprotect(uintptr_t addr, size_t len, int prot); +void sh_util_clear_cache(uintptr_t addr, size_t len); + +bool sh_util_is_thumb32(uintptr_t target_addr); + +uint32_t sh_util_arm_expand_imm(uint32_t opcode); + +int sh_util_write_inst(uintptr_t target_addr, void *inst, size_t inst_len); + +int sh_util_get_api_level(void); + +int sh_util_write(int fd, const char *buf, size_t buf_len); + +struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result); + +size_t sh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args); +size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...); diff --git a/app/src/main/cpp/shadowhook/include/shadowhook.h b/app/src/main/cpp/shadowhook/include/shadowhook.h new file mode 100644 index 0000000000000000000000000000000000000000..0aa7f1a8b63adf0b71648de849a59437a39dba1d --- /dev/null +++ b/app/src/main/cpp/shadowhook/include/shadowhook.h @@ -0,0 +1,191 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// shadowhook is now credited to all its contributors. +// +// Before shadowhook was open sourced in February 2022, it was developed by +// the following developers from ByteDance's App Health Team: +// +// Kelun Cai (caikelun@bytedance.com) +// Pengying Xu (xupengying@bytedance.com) +// + +#ifndef BYTEDANCE_SHADOWHOOK_H +#define BYTEDANCE_SHADOWHOOK_H + +#include <stdbool.h> +#include <stdint.h> + +#define SHADOWHOOK_VERSION "1.0.8" + +#define SHADOWHOOK_ERRNO_OK 0 +#define SHADOWHOOK_ERRNO_PENDING 1 +#define SHADOWHOOK_ERRNO_UNINIT 2 +#define SHADOWHOOK_ERRNO_INVALID_ARG 3 +#define SHADOWHOOK_ERRNO_OOM 4 +#define SHADOWHOOK_ERRNO_MPROT 5 +#define SHADOWHOOK_ERRNO_WRITE_CRASH 6 +#define SHADOWHOOK_ERRNO_INIT_ERRNO 7 +#define SHADOWHOOK_ERRNO_INIT_SIGSEGV 8 +#define SHADOWHOOK_ERRNO_INIT_SIGBUS 9 +#define SHADOWHOOK_ERRNO_INIT_ENTER 10 +#define SHADOWHOOK_ERRNO_INIT_SAFE 11 +#define SHADOWHOOK_ERRNO_INIT_LINKER 12 +#define SHADOWHOOK_ERRNO_INIT_HUB 13 +#define SHADOWHOOK_ERRNO_HUB_CREAT 14 +#define SHADOWHOOK_ERRNO_MONITOR_DLOPEN 15 +#define SHADOWHOOK_ERRNO_MONITOR_THREAD 16 +#define SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH 17 +#define SHADOWHOOK_ERRNO_HOOK_DLSYM 18 +#define SHADOWHOOK_ERRNO_HOOK_DLSYM_CRASH 19 +#define SHADOWHOOK_ERRNO_HOOK_DUP 20 +#define SHADOWHOOK_ERRNO_HOOK_DLADDR_CRASH 21 +#define SHADOWHOOK_ERRNO_HOOK_DLINFO 22 +#define SHADOWHOOK_ERRNO_HOOK_SYMSZ 23 +#define SHADOWHOOK_ERRNO_HOOK_ENTER 24 +#define SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH 25 +#define SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED 26 +#define SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND 27 +#define SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH 28 +#define SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH 29 +#define SHADOWHOOK_ERRNO_UNHOOK_EXIT_MISMATCH 30 +#define SHADOWHOOK_ERRNO_UNHOOK_EXIT_CRASH 31 +#define SHADOWHOOK_ERRNO_UNHOOK_ON_ERROR 32 +#define SHADOWHOOK_ERRNO_UNHOOK_ON_UNFINISHED 33 +#define SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH 34 +#define SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH 35 + +#ifdef __cplusplus +extern "C" { +#endif + +const char *shadowhook_get_version(void); + +// init +typedef enum { + SHADOWHOOK_MODE_SHARED = 0, // a function can be hooked multiple times + SHADOWHOOK_MODE_UNIQUE = 1 // a function can only be hooked once, and hooking again will report an error +} shadowhook_mode_t; +int shadowhook_init(shadowhook_mode_t mode, bool debuggable); +int shadowhook_get_init_errno(void); + +// get and set attributes +#define SHADOWHOOK_IS_SHARED_MODE (SHADOWHOOK_MODE_SHARED == shadowhook_get_mode()) +#define SHADOWHOOK_IS_UNIQUE_MODE (SHADOWHOOK_MODE_UNIQUE == shadowhook_get_mode()) +shadowhook_mode_t shadowhook_get_mode(void); +bool shadowhook_get_debuggable(void); +void shadowhook_set_debuggable(bool debuggable); +bool shadowhook_get_recordable(void); +void shadowhook_set_recordable(bool recordable); + +// get error-number and error message +int shadowhook_get_errno(void); +const char *shadowhook_to_errmsg(int error_number); + +// hook and unhook +typedef void (*shadowhook_hooked_t)(int error_number, const char *lib_name, const char *sym_name, + void *sym_addr, void *new_addr, void *orig_addr, void *arg); +void *shadowhook_hook_func_addr(void *func_addr, void *new_addr, void **orig_addr); +void *shadowhook_hook_sym_addr(void *sym_addr, void *new_addr, void **orig_addr); +void *shadowhook_hook_sym_name(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr); +void *shadowhook_hook_sym_name_callback(const char *lib_name, const char *sym_name, void *new_addr, + void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg); +int shadowhook_unhook(void *stub); + +// get operation records +#define SHADOWHOOK_RECORD_ITEM_ALL 0x3FF // 0b1111111111 +#define SHADOWHOOK_RECORD_ITEM_TIMESTAMP (1 << 0) +#define SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME (1 << 1) +#define SHADOWHOOK_RECORD_ITEM_OP (1 << 2) +#define SHADOWHOOK_RECORD_ITEM_LIB_NAME (1 << 3) +#define SHADOWHOOK_RECORD_ITEM_SYM_NAME (1 << 4) +#define SHADOWHOOK_RECORD_ITEM_SYM_ADDR (1 << 5) +#define SHADOWHOOK_RECORD_ITEM_NEW_ADDR (1 << 6) +#define SHADOWHOOK_RECORD_ITEM_BACKUP_LEN (1 << 7) +#define SHADOWHOOK_RECORD_ITEM_ERRNO (1 << 8) +#define SHADOWHOOK_RECORD_ITEM_STUB (1 << 9) +char *shadowhook_get_records(uint32_t item_flags); +void shadowhook_dump_records(int fd, uint32_t item_flags); + +// helper functions for "get symbol-address from library-name and symbol-name" +void *shadowhook_dlopen(const char *lib_name); +void shadowhook_dlclose(void *handle); +void *shadowhook_dlsym(void *handle, const char *sym_name); +void *shadowhook_dlsym_dynsym(void *handle, const char *sym_name); +void *shadowhook_dlsym_symtab(void *handle, const char *sym_name); + +// for internal use +void *shadowhook_get_prev_func(void *func); +void shadowhook_pop_stack(void *return_address); +void shadowhook_allow_reentrant(void *return_address); +void shadowhook_disallow_reentrant(void *return_address); +void *shadowhook_get_return_address(void); + +#ifdef __cplusplus +} +#endif + +// call previous function in proxy-function +#ifdef __cplusplus +#define SHADOWHOOK_CALL_PREV(func, ...) \ + ((decltype(&(func)))shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__) +#else +#define SHADOWHOOK_CALL_PREV(func, func_sig, ...) \ + ((func_sig)shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__) +#endif +// pop stack in proxy-function (for C/C++) +#define SHADOWHOOK_POP_STACK() \ + do { \ + if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack(__builtin_return_address(0)); \ + } while (0) + +// pop stack in proxy-function (for C++ only) +#ifdef __cplusplus +class ShadowhookStackScope { + public: + ShadowhookStackScope(void *return_address) : return_address_(return_address) {} + ~ShadowhookStackScope() { + if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack(return_address_); + } + + private: + void *return_address_; +}; +#define SHADOWHOOK_STACK_SCOPE() ShadowhookStackScope shadowhook_stack_scope_obj(__builtin_return_address(0)) +#endif + +// allow reentrant of the current proxy-function +#define SHADOWHOOK_ALLOW_REENTRANT() \ + do { \ + if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_allow_reentrant(__builtin_return_address(0)); \ + } while (0) + +// disallow reentrant of the current proxy-function +#define SHADOWHOOK_DISALLOW_REENTRANT() \ + do { \ + if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_disallow_reentrant(__builtin_return_address(0)); \ + } while (0) + +// get return address in proxy-function +#define SHADOWHOOK_RETURN_ADDRESS() \ + ((void *)(SHADOWHOOK_IS_SHARED_MODE ? shadowhook_get_return_address() : __builtin_return_address(0))) + +#endif diff --git a/app/src/main/cpp/shadowhook/sh_enter.c b/app/src/main/cpp/shadowhook/sh_enter.c new file mode 100644 index 0000000000000000000000000000000000000000..f30b8119861ec5066b34fefa40e3b3dcd0cc69e0 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_enter.c @@ -0,0 +1,47 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_enter.h" + +#include <stdint.h> + +#include "sh_trampo.h" + +#define SH_ENTER_PAGE_NAME "shadowhook-enter" +#define SH_ENTER_SZ 256 +#define SH_ENTER_DELAY_SEC 10 + +static sh_trampo_mgr_t sh_enter_trampo_mgr; + +int sh_enter_init(void) { + sh_trampo_init_mgr(&sh_enter_trampo_mgr, SH_ENTER_PAGE_NAME, SH_ENTER_SZ, SH_ENTER_DELAY_SEC); + return 0; +} + +uintptr_t sh_enter_alloc(void) { + return sh_trampo_alloc(&sh_enter_trampo_mgr, 0, 0, 0); +} + +void sh_enter_free(uintptr_t enter) { + sh_trampo_free(&sh_enter_trampo_mgr, enter); +} diff --git a/app/src/main/cpp/shadowhook/sh_enter.h b/app/src/main/cpp/shadowhook/sh_enter.h new file mode 100644 index 0000000000000000000000000000000000000000..89ba8e59f1afa8e4c5abb03752690c10e6e3b198 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_enter.h @@ -0,0 +1,30 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdint.h> + +int sh_enter_init(void); + +uintptr_t sh_enter_alloc(void); +void sh_enter_free(uintptr_t enter); diff --git a/app/src/main/cpp/shadowhook/sh_exit.c b/app/src/main/cpp/shadowhook/sh_exit.c new file mode 100644 index 0000000000000000000000000000000000000000..2ae905d9f85fb07bdc7b521ed7c67d3d50fbdf7d --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_exit.c @@ -0,0 +1,420 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_exit.h" + +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> + +#include "sh_config.h" +#include "sh_log.h" +#include "sh_sig.h" +#include "sh_trampo.h" +#include "sh_util.h" +#include "shadowhook.h" +#include "xdl.h" + +#define SH_EXIT_PAGE_NAME "shadowhook-exit" +#if defined(__arm__) +#define SH_EXIT_SZ 8 +#elif defined(__aarch64__) +#define SH_EXIT_SZ 16 +#endif +#define SH_EXIT_DELAY_SEC 2 +#define SH_EXIT_GAPS_CAP 16 + +// Used to identify whether the ELF gap has been zero-filled, +// and also serves as a guard instruction. +#if defined(__arm__) +#define SH_EXIT_CLEAR_FLAG 0xE1200070BE00BE00 // BKPT #0(thumb) + BKPT #0(thumb) + BKPT #0(arm) +#elif defined(__aarch64__) +#define SH_EXIT_CLEAR_FLAG 0xD4200000D4200000 // BRK #0 + BRK #0 +#endif + +#define SH_EXIT_TYPE_OUT_LIBRARY 0 +#define SH_EXIT_TYPE_IN_LIBRARY 1 + +extern __attribute((weak)) unsigned long int getauxval(unsigned long int); + +static pthread_mutex_t sh_exit_lock = PTHREAD_MUTEX_INITIALIZER; +static sh_trampo_mgr_t sh_exit_trampo_mgr; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct { + uintptr_t load_bias; + const ElfW(Phdr) *dlpi_phdr; + ElfW(Half) dlpi_phnum; +} sh_exit_elfinfo_t; +#pragma clang diagnostic pop + +static sh_exit_elfinfo_t sh_exit_app_process_info; +static sh_exit_elfinfo_t sh_exit_linker_info; +static sh_exit_elfinfo_t sh_exit_vdso_info; // vdso may not exist + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-statement-expression" + +static void sh_exit_init_elfinfo(unsigned long type, sh_exit_elfinfo_t *info) { + if (__predict_false(NULL == getauxval)) goto err; + + uintptr_t val = (uintptr_t)getauxval(type); + if (__predict_false(0 == val)) goto err; + + // get base + uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val); + if (__predict_false(0 != memcmp((void *)base, ELFMAG, SELFMAG))) goto err; + + // ELF info + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; + const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); + ElfW(Half) dlpi_phnum = ehdr->e_phnum; + + // get load_bias + uintptr_t min_vaddr = UINTPTR_MAX; + for (size_t i = 0; i < dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(dlpi_phdr[i]); + if (PT_LOAD == phdr->p_type) { + if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; + } + } + if (__predict_false(UINTPTR_MAX == min_vaddr || base < min_vaddr)) goto err; + uintptr_t load_bias = base - min_vaddr; + + info->load_bias = load_bias; + info->dlpi_phdr = dlpi_phdr; + info->dlpi_phnum = dlpi_phnum; + return; + +err: + info->load_bias = 0; + info->dlpi_phdr = NULL; + info->dlpi_phnum = 0; +} + +void sh_exit_init(void) { + // init for out-library mode + sh_trampo_init_mgr(&sh_exit_trampo_mgr, SH_EXIT_PAGE_NAME, SH_EXIT_SZ, SH_EXIT_DELAY_SEC); + + // init for in-library mode + sh_exit_init_elfinfo(AT_PHDR, &sh_exit_app_process_info); + sh_exit_init_elfinfo(AT_BASE, &sh_exit_linker_info); + sh_exit_init_elfinfo(AT_SYSINFO_EHDR, &sh_exit_vdso_info); +} + +// out-library mode: +// +// We store the shellcode for exit in mmaped memory near the PC. +// + +static int sh_exit_alloc_out_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, + size_t exit_len, size_t range_low, size_t range_high) { + (void)dlinfo; + + uintptr_t addr = sh_trampo_alloc(&sh_exit_trampo_mgr, pc, range_low, range_high); + if (0 == addr) return -1; + + memcpy((void *)addr, exit, exit_len); + sh_util_clear_cache(addr, exit_len); + *exit_addr = addr; + return 0; +} + +static void sh_exit_free_out_library(uintptr_t exit_addr) { + sh_trampo_free(&sh_exit_trampo_mgr, exit_addr); +} + +// in-library mode: +// +// We store the shellcode for exit in the memory gaps in the ELF. + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct { + uintptr_t start; + uintptr_t end; + bool need_fill_zero; + bool readable; +} sh_exit_gap_t; +#pragma clang diagnostic pop + +static size_t sh_exit_get_gaps(xdl_info_t *dlinfo, sh_exit_gap_t *gaps, size_t gaps_cap, + bool elf_loaded_by_kernel) { + size_t gaps_used = 0; + + for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) { + // current LOAD segment + const ElfW(Phdr) *cur_phdr = &(dlinfo->dlpi_phdr[i]); + if (PT_LOAD != cur_phdr->p_type) continue; + + // next LOAD segment + const ElfW(Phdr) *next_phdr = NULL; + if (!elf_loaded_by_kernel) { + for (size_t j = i + 1; j < dlinfo->dlpi_phnum; j++) { + if (PT_LOAD == dlinfo->dlpi_phdr[j].p_type) { + next_phdr = &(dlinfo->dlpi_phdr[j]); + break; + } + } + } + + uintptr_t cur_end = (uintptr_t)dlinfo->dli_fbase + cur_phdr->p_vaddr + cur_phdr->p_memsz; + uintptr_t cur_page_end = SH_UTIL_PAGE_END(cur_end); + uintptr_t cur_file_end = (uintptr_t)dlinfo->dli_fbase + cur_phdr->p_vaddr + cur_phdr->p_filesz; + uintptr_t cur_file_page_end = SH_UTIL_PAGE_END(cur_file_end); + uintptr_t next_page_start = + (NULL == next_phdr ? cur_page_end + : SH_UTIL_PAGE_START((uintptr_t)dlinfo->dli_fbase + next_phdr->p_vaddr)); + + sh_exit_gap_t gap = {0, 0, false, false}; + if (cur_phdr->p_flags & PF_X) { + // From: last PF_X page's unused memory tail space. + // To: next page start. + gap.start = SH_UTIL_ALIGN_END(cur_end, 0x10); + gap.end = next_page_start; + gap.need_fill_zero = true; + gap.readable = (cur_phdr->p_flags & PF_R && cur_page_end == next_page_start); + } else if (cur_page_end > cur_file_page_end) { + // From: last .bss page(which must NOT be file backend)'s unused memory tail space. + // To: next page start. + gap.start = SH_UTIL_ALIGN_END(cur_end, 0x10); + gap.end = next_page_start; + gap.need_fill_zero = false; + gap.readable = (cur_phdr->p_flags & PF_R && cur_page_end == next_page_start); + } else if (next_page_start > cur_page_end) { + // Entire unused memory pages. + gap.start = cur_page_end; + gap.end = next_page_start; + gap.need_fill_zero = true; + gap.readable = false; + } + + if ((gap.need_fill_zero && gap.end > gap.start + 0x10) || (!gap.need_fill_zero && gap.end > gap.start)) { + SH_LOG_INFO("exit: gap, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR " - %" PRIxPTR + "), NFZ %d, READABLE %d", + gap.start, gap.end, (uintptr_t)dlinfo->dli_fbase, gap.start - (uintptr_t)dlinfo->dli_fbase, + gap.end - (uintptr_t)dlinfo->dli_fbase, gap.need_fill_zero ? 1 : 0, gap.readable ? 1 : 0); + gaps[gaps_used].start = gap.start; + gaps[gaps_used].end = gap.end; + gaps[gaps_used].need_fill_zero = gap.need_fill_zero; + gaps[gaps_used].readable = gap.readable; + gaps_used++; + } + + if (gaps_used >= gaps_cap) break; + } + + return gaps_used; +} + +static bool sh_exit_is_in_elf_range(uintptr_t pc, sh_exit_elfinfo_t *info) { + if (pc < info->load_bias) return false; + + uintptr_t vaddr = pc - info->load_bias; + for (size_t i = 0; i < info->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); + if (PT_LOAD != phdr->p_type) continue; + + if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true; + } + + return false; +} + +static bool sh_exit_is_elf_loaded_by_kernel(uintptr_t pc) { + if (NULL == sh_exit_app_process_info.dlpi_phdr) return true; + if (sh_exit_is_in_elf_range(pc, &sh_exit_app_process_info)) return true; + + if (NULL == sh_exit_linker_info.dlpi_phdr) return true; + if (sh_exit_is_in_elf_range(pc, &sh_exit_linker_info)) return true; + + // vdso may not exist + if (NULL != sh_exit_vdso_info.dlpi_phdr) + if (sh_exit_is_in_elf_range(pc, &sh_exit_vdso_info)) return true; + + return false; +} + +static bool sh_exit_is_zero(uintptr_t buf, size_t buf_len) { + for (uintptr_t i = buf; i < buf + buf_len; i += sizeof(uintptr_t)) + if (*((uintptr_t *)i) != 0) return false; + + return true; +} + +static int sh_exit_fill_zero(uintptr_t start, uintptr_t end, bool readable, xdl_info_t *dlinfo) { + size_t size = end - start; + bool set_prot_rwx = false; + + if (!readable) { + if (0 != sh_util_mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1; + set_prot_rwx = true; + } + + if (*((uint64_t *)start) != SH_EXIT_CLEAR_FLAG) { + SH_LOG_INFO("exit: gap fill zero, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR + " - %" PRIxPTR "), READABLE %d", + start, end, (uintptr_t)dlinfo->dli_fbase, start - (uintptr_t)dlinfo->dli_fbase, + end - (uintptr_t)dlinfo->dli_fbase, readable ? 1 : 0); + if (!set_prot_rwx) + if (0 != sh_util_mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1; + memset((void *)start, 0, size); + *((uint64_t *)(start)) = SH_EXIT_CLEAR_FLAG; + sh_util_clear_cache(start, size); + } + + return 0; +} + +static int sh_exit_try_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, + size_t exit_len, size_t range_low, size_t range_high, uintptr_t start, + uintptr_t end) { + if (pc >= range_low) start = SH_UTIL_MAX(start, pc - range_low); + start = SH_UTIL_ALIGN_END(start, exit_len); + + if (range_high <= UINTPTR_MAX - pc) end = SH_UTIL_MIN(end - exit_len, pc + range_high); + end = SH_UTIL_ALIGN_START(end, exit_len); + + if (end < start) return -1; + SH_LOG_INFO("exit: gap resize, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR + " - %" PRIxPTR ")", + start, end, (uintptr_t)dlinfo->dli_fbase, start - (uintptr_t)dlinfo->dli_fbase, + end - (uintptr_t)dlinfo->dli_fbase); + + for (uintptr_t cur = start; cur <= end; cur += exit_len) { + // check if the current space has been used + if (!sh_exit_is_zero(cur, exit_len)) continue; + + // write shellcode to the current location + if (0 != sh_util_mprotect(cur, exit_len, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1; + memcpy((void *)cur, exit, exit_len); + sh_util_clear_cache(cur, exit_len); + *exit_addr = cur; + SH_LOG_INFO("exit: in-library alloc, at %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR "), len %zu", + cur, (uintptr_t)dlinfo->dli_fbase, cur - (uintptr_t)dlinfo->dli_fbase, exit_len); + return 0; + } + return -1; +} + +static int sh_exit_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, + size_t exit_len, size_t range_low, size_t range_high) { + int r = -1; + *exit_addr = 0; + + bool elf_loaded_by_kernel = sh_exit_is_elf_loaded_by_kernel(pc); + + pthread_mutex_lock(&sh_exit_lock); + + SH_SIG_TRY(SIGSEGV, SIGBUS) { + sh_exit_gap_t gaps[SH_EXIT_GAPS_CAP]; + size_t gaps_used = sh_exit_get_gaps(dlinfo, gaps, SH_EXIT_GAPS_CAP, elf_loaded_by_kernel); + for (size_t i = 0; i < gaps_used; i++) { + // fill zero + if (gaps[i].need_fill_zero) { + if (0 != sh_exit_fill_zero(gaps[i].start, gaps[i].end, gaps[i].readable, dlinfo)) return -1; + } + + if (0 == (r = sh_exit_try_alloc_in_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high, + gaps[i].start, gaps[i].end))) + break; + } + } + SH_SIG_CATCH() { + r = -1; + *exit_addr = 0; + SH_LOG_WARN("exit: alloc crashed"); + } + SH_SIG_EXIT + + pthread_mutex_unlock(&sh_exit_lock); + return r; +} + +static int sh_exit_free_in_library(uintptr_t exit_addr, uint8_t *exit, size_t exit_len) { + int r; + + pthread_mutex_lock(&sh_exit_lock); + + SH_SIG_TRY(SIGSEGV, SIGBUS) { + if (0 != memcmp((void *)exit_addr, exit, exit_len)) { + r = SHADOWHOOK_ERRNO_UNHOOK_EXIT_MISMATCH; + goto err; + } + if (0 != sh_util_mprotect((uintptr_t)exit_addr, exit_len, PROT_READ | PROT_WRITE | PROT_EXEC)) { + r = SHADOWHOOK_ERRNO_MPROT; + goto err; + } + memset((void *)exit_addr, 0, exit_len); + sh_util_clear_cache((uintptr_t)exit_addr, exit_len); + r = 0; + err:; + } + SH_SIG_CATCH() { + r = SHADOWHOOK_ERRNO_UNHOOK_EXIT_CRASH; + SH_LOG_WARN("exit: free crashed"); + } + SH_SIG_EXIT + + pthread_mutex_unlock(&sh_exit_lock); + return r; +} + +int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, + size_t exit_len, size_t range_low, size_t range_high) { + int r; + + // (1) try out-library mode first. Because ELF gaps are a valuable non-renewable resource. + *exit_type = SH_EXIT_TYPE_OUT_LIBRARY; + r = sh_exit_alloc_out_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high); + if (0 == r) goto ok; + + // (2) try in-library mode. + *exit_type = SH_EXIT_TYPE_IN_LIBRARY; + r = sh_exit_alloc_in_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high); + if (0 == r) goto ok; + + return r; + +ok: + SH_LOG_INFO("exit: alloc %s library, exit %" PRIxPTR ", pc %" PRIxPTR ", distance %" PRIxPTR + ", range [-%zx, %zx]", + (*exit_type == SH_EXIT_TYPE_OUT_LIBRARY ? "out" : "in"), *exit_addr, pc, + (pc > *exit_addr ? pc - *exit_addr : *exit_addr - pc), range_low, range_high); + return 0; +} + +int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit, size_t exit_len) { + if (SH_EXIT_TYPE_OUT_LIBRARY == exit_type) { + (void)exit, (void)exit_len; + sh_exit_free_out_library(exit_addr); + return 0; + } else + return sh_exit_free_in_library(exit_addr, exit, exit_len); +} + +#pragma clang diagnostic pop diff --git a/app/src/main/cpp/shadowhook/sh_exit.h b/app/src/main/cpp/shadowhook/sh_exit.h new file mode 100644 index 0000000000000000000000000000000000000000..e0f46b60c898972dcf5db9755beac8e42a2acf48 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_exit.h @@ -0,0 +1,34 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stddef.h> +#include <stdint.h> + +#include "xdl.h" + +void sh_exit_init(void); + +int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, + size_t exit_len, size_t range_low, size_t range_high); +int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit, size_t exit_len); diff --git a/app/src/main/cpp/shadowhook/sh_hub.c b/app/src/main/cpp/shadowhook/sh_hub.c new file mode 100644 index 0000000000000000000000000000000000000000..eef9a3462d9ba38ee645b60698ed8c301239f351 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_hub.c @@ -0,0 +1,538 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_hub.h" + +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/prctl.h> + +#include "queue.h" +#include "sh_log.h" +#include "sh_safe.h" +#include "sh_sig.h" +#include "sh_trampo.h" +#include "sh_util.h" +#include "shadowhook.h" +#include "tree.h" + +#define SH_HUB_TRAMPO_PAGE_NAME "shadowhook-hub-trampo" +#define SH_HUB_TRAMPO_DELAY_SEC 5 +#define SH_HUB_STACK_NAME "shadowhook-hub-stack" +#define SH_HUB_STACK_SIZE 4096 +#define SH_HUB_STACK_FRAME_MAX 127 // keep sizeof(sh_hub_stack_t) < 4K +#define SH_HUB_THREAD_MAX 1024 +#define SH_HUB_DELAY_SEC 10 + +#define SH_HUB_FRAME_FLAG_NONE ((uintptr_t)0) +#define SH_HUB_FRAME_FLAG_ALLOW_REENTRANT ((uintptr_t)(1 << 0)) + +// proxy for each hook-task in the same target-address +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct sh_hub_proxy { + void *func; + bool enabled; + SLIST_ENTRY(sh_hub_proxy, ) link; +} sh_hub_proxy_t; +#pragma clang diagnostic pop + +// proxy list for each hub +typedef SLIST_HEAD(sh_hub_proxy_list, sh_hub_proxy, ) sh_hub_proxy_list_t; + +// frame in the stack +typedef struct { + sh_hub_proxy_list_t proxies; + uintptr_t orig_addr; + void *return_address; + uintptr_t flags; +} sh_hub_frame_t; + +// stack for each thread +typedef struct { + size_t frames_cnt; + sh_hub_frame_t frames[SH_HUB_STACK_FRAME_MAX]; +} sh_hub_stack_t; + +// hub for each target-address +struct sh_hub { + sh_hub_proxy_list_t proxies; + pthread_mutex_t proxies_lock; + uintptr_t orig_addr; + uintptr_t trampo; + time_t destroy_ts; + LIST_ENTRY(sh_hub, ) link; +}; + +// hub list for delayed-destroy staging +typedef LIST_HEAD(sh_hub_list, sh_hub, ) sh_hub_list_t; + +// global data for hub delayed-destroy staging +static sh_hub_list_t sh_hub_delayed_destroy; +static pthread_mutex_t sh_hub_delayed_destroy_lock; + +// global data for trampo +static sh_trampo_mgr_t sh_hub_trampo_mgr; + +// global data for stack +static pthread_key_t sh_hub_stack_tls_key; +static sh_hub_stack_t *sh_hub_stack_cache; +static uint8_t *sh_hub_stack_cache_used; + +// hub trampoline template +extern void *sh_hub_trampo_template_data __attribute__((visibility("hidden"))); +__attribute__((naked)) static void sh_hub_trampo_template(void) { +#if defined(__arm__) + __asm__( + // Save parameter registers, LR + "push { r0 - r3, lr } \n" + + // Call sh_hub_push_stack() + "ldr r0, hub_ptr \n" + "mov r1, lr \n" + "ldr ip, push_stack \n" + "blx ip \n" + + // Save the hook function's address to IP register + "mov ip, r0 \n" + + // Restore parameter registers, LR + "pop { r0 - r3, lr } \n" + + // Call hook function + "bx ip \n" + + "sh_hub_trampo_template_data:" + ".global sh_hub_trampo_template_data;" + "push_stack:" + ".word 0;" + "hub_ptr:" + ".word 0;"); +#elif defined(__aarch64__) + __asm__( + // Save parameter registers, XR(X8), LR + "stp x0, x1, [sp, #-0xd0]! \n" + "stp x2, x3, [sp, #0x10] \n" + "stp x4, x5, [sp, #0x20] \n" + "stp x6, x7, [sp, #0x30] \n" + "stp x8, lr, [sp, #0x40] \n" + "stp q0, q1, [sp, #0x50] \n" + "stp q2, q3, [sp, #0x70] \n" + "stp q4, q5, [sp, #0x90] \n" + "stp q6, q7, [sp, #0xb0] \n" + + // Call sh_hub_push_stack() + "ldr x0, hub_ptr \n" + "mov x1, lr \n" + "ldr x16, push_stack \n" + "blr x16 \n" + + // Save the hook function's address to IP register + "mov x16, x0 \n" + + // Restore parameter registers, XR(X8), LR + "ldp q6, q7, [sp, #0xb0] \n" + "ldp q4, q5, [sp, #0x90] \n" + "ldp q2, q3, [sp, #0x70] \n" + "ldp q0, q1, [sp, #0x50] \n" + "ldp x8, lr, [sp, #0x40] \n" + "ldp x6, x7, [sp, #0x30] \n" + "ldp x4, x5, [sp, #0x20] \n" + "ldp x2, x3, [sp, #0x10] \n" + "ldp x0, x1, [sp], #0xd0 \n" + + // Call hook function + "br x16 \n" + + "sh_hub_trampo_template_data:" + ".global sh_hub_trampo_template_data;" + "push_stack:" + ".quad 0;" + "hub_ptr:" + ".quad 0;"); +#endif +} + +static void *sh_hub_trampo_template_start(void) { +#if defined(__arm__) && defined(__thumb__) + return (void *)((uintptr_t)&sh_hub_trampo_template - 1); +#else + return (void *)&sh_hub_trampo_template; +#endif +} + +static sh_hub_stack_t *sh_hub_stack_create(void) { + // get stack from global cache + for (size_t i = 0; i < SH_HUB_THREAD_MAX; i++) { + uint8_t *used = &(sh_hub_stack_cache_used[i]); + if (0 == *used) { + uint8_t expected = 0; + if (__atomic_compare_exchange_n(used, &expected, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { + sh_hub_stack_t *stack = &(sh_hub_stack_cache[i]); + stack->frames_cnt = 0; + SH_LOG_DEBUG("hub: get stack from global cache[%zu] %p", i, (void *)stack); + return stack; // OK + } + } + } + + // create new stack by mmap + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + void *buf = sh_safe_mmap(NULL, SH_HUB_STACK_SIZE, prot, flags, -1, 0); + if (MAP_FAILED == buf) return NULL; // failed + sh_safe_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)buf, SH_HUB_STACK_SIZE, + (unsigned long)SH_HUB_STACK_NAME); + sh_hub_stack_t *stack = (sh_hub_stack_t *)buf; + stack->frames_cnt = 0; + return stack; // OK +} + +static void sh_hub_stack_destroy(void *buf) { + if (NULL == buf) return; + + if ((uintptr_t)sh_hub_stack_cache <= (uintptr_t)buf && + (uintptr_t)buf < ((uintptr_t)sh_hub_stack_cache + SH_HUB_THREAD_MAX * sizeof(sh_hub_stack_t))) { + // return stack to global cache + size_t i = ((uintptr_t)buf - (uintptr_t)sh_hub_stack_cache) / sizeof(sh_hub_stack_t); + uint8_t *used = &(sh_hub_stack_cache_used[i]); + if (1 != *used) abort(); + __atomic_store_n(used, 0, __ATOMIC_RELEASE); + SH_LOG_DEBUG("hub: return stack to global cache[%zu] %p", i, buf); + } else { + // munmap stack + munmap(buf, SH_HUB_STACK_SIZE); + } +} + +int sh_hub_init(void) { + LIST_INIT(&sh_hub_delayed_destroy); + pthread_mutex_init(&sh_hub_delayed_destroy_lock, NULL); + + // init TLS key + if (__predict_false(0 != pthread_key_create(&sh_hub_stack_tls_key, sh_hub_stack_destroy))) return -1; + + // init hub's stack cache + if (__predict_false(NULL == (sh_hub_stack_cache = malloc(SH_HUB_THREAD_MAX * sizeof(sh_hub_stack_t))))) + return -1; + if (__predict_false(NULL == (sh_hub_stack_cache_used = calloc(SH_HUB_THREAD_MAX, sizeof(uint8_t))))) + return -1; + + // init hub's trampoline manager + size_t code_size = (uintptr_t)(&sh_hub_trampo_template_data) - (uintptr_t)(sh_hub_trampo_template_start()); + size_t data_size = sizeof(void *) + sizeof(void *); + sh_trampo_init_mgr(&sh_hub_trampo_mgr, SH_HUB_TRAMPO_PAGE_NAME, code_size + data_size, + SH_HUB_TRAMPO_DELAY_SEC); + + return 0; +} + +static void *sh_hub_push_stack(sh_hub_t *self, void *return_address) { + sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key); + + // create stack, only once + if (__predict_false(NULL == stack)) { + if (__predict_false(NULL == (stack = sh_hub_stack_create()))) goto end; + sh_safe_pthread_setspecific(sh_hub_stack_tls_key, (void *)stack); + } + + // check whether a recursive call occurred + bool recursive = false; + for (size_t i = stack->frames_cnt; i > 0; i--) { + sh_hub_frame_t *frame = &stack->frames[i - 1]; + if (0 == (frame->flags & SH_HUB_FRAME_FLAG_ALLOW_REENTRANT) && (frame->orig_addr == self->orig_addr)) { + // recursive call found + recursive = true; + break; + } + } + + // find and return the first enabled proxy's function in the proxy-list + // (does not include the original function) + if (!recursive) { + sh_hub_proxy_t *proxy; + SLIST_FOREACH(proxy, &self->proxies, link) { + if (proxy->enabled) { + // push a new frame for the current proxy + if (stack->frames_cnt >= SH_HUB_STACK_FRAME_MAX) goto end; + stack->frames_cnt++; + SH_LOG_DEBUG("hub: frames_cnt++ = %zu", stack->frames_cnt); + sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + frame->proxies = self->proxies; + frame->orig_addr = self->orig_addr; + frame->return_address = return_address; + frame->flags = SH_HUB_FRAME_FLAG_NONE; + + // return the first enabled proxy's function + SH_LOG_DEBUG("hub: push_stack() return first enabled proxy %p", proxy->func); + return proxy->func; + } + } + } + + // if not found enabled proxy in the proxy-list, or recursive call found, + // just return the original-function +end: + SH_LOG_DEBUG("hub: push_stack() return orig_addr %p", (void *)self->orig_addr); + return (void *)self->orig_addr; +} + +void sh_hub_pop_stack(void *return_address) { + sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key); + if (0 == stack->frames_cnt) return; + sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + + // only the first proxy will actually execute pop-stack() + if (frame->return_address == return_address) { + stack->frames_cnt--; + SH_LOG_DEBUG("hub: frames_cnt-- = %zu", stack->frames_cnt); + } +} + +sh_hub_t *sh_hub_create(uintptr_t target_addr, uintptr_t *trampo) { + size_t code_size = (uintptr_t)(&sh_hub_trampo_template_data) - (uintptr_t)(sh_hub_trampo_template_start()); + size_t data_size = sizeof(void *) + sizeof(void *); + + sh_hub_t *self = malloc(sizeof(sh_hub_t)); + if (NULL == self) return NULL; + SLIST_INIT(&self->proxies); + pthread_mutex_init(&self->proxies_lock, NULL); + self->orig_addr = 0; + + // alloc memory for trampoline + if (0 == (self->trampo = sh_trampo_alloc(&sh_hub_trampo_mgr, 0, 0, 0))) { + free(self); + return NULL; + } + + // fill in code + SH_SIG_TRY(SIGSEGV, SIGBUS) { + memcpy((void *)self->trampo, sh_hub_trampo_template_start(), code_size); + } + SH_SIG_CATCH() { + sh_trampo_free(&sh_hub_trampo_mgr, self->trampo); + free(self); + SH_LOG_WARN("hub: fill in code crashed"); + return NULL; + } + SH_SIG_EXIT + + // file in data + void **data = (void **)(self->trampo + code_size); + *data++ = (void *)sh_hub_push_stack; + *data = (void *)self; + + // clear CPU cache + sh_util_clear_cache(self->trampo, code_size + data_size); + +#if defined(__arm__) && defined(__thumb__) + *trampo = self->trampo + 1; +#else + *trampo = self->trampo; +#endif + + SH_LOG_INFO("hub: create trampo for target_addr %" PRIxPTR " at %" PRIxPTR ", size %zu + %zu = %zu", + target_addr, *trampo, code_size, data_size, code_size + data_size); + return self; +} + +static void sh_hub_destroy_inner(sh_hub_t *self) { + pthread_mutex_destroy(&self->proxies_lock); + + if (0 != self->trampo) sh_trampo_free(&sh_hub_trampo_mgr, self->trampo); + + while (!SLIST_EMPTY(&self->proxies)) { + sh_hub_proxy_t *proxy = SLIST_FIRST(&self->proxies); + SLIST_REMOVE_HEAD(&self->proxies, link); + free(proxy); + } + + free(self); +} + +void sh_hub_destroy(sh_hub_t *self, bool with_delay) { + if (SHADOWHOOK_IS_SHARED_MODE) { + struct timeval now; + gettimeofday(&now, NULL); + + if (!LIST_EMPTY(&sh_hub_delayed_destroy)) { + pthread_mutex_lock(&sh_hub_delayed_destroy_lock); + sh_hub_t *hub, *hub_tmp; + LIST_FOREACH_SAFE(hub, &sh_hub_delayed_destroy, link, hub_tmp) + if (now.tv_sec - hub->destroy_ts > SH_HUB_DELAY_SEC) { + LIST_REMOVE(hub, link); + sh_hub_destroy_inner(hub); + } + pthread_mutex_unlock(&sh_hub_delayed_destroy_lock); + } + + if (with_delay) { + self->destroy_ts = now.tv_sec; + sh_trampo_free(&sh_hub_trampo_mgr, self->trampo); + self->trampo = 0; + + pthread_mutex_lock(&sh_hub_delayed_destroy_lock); + LIST_INSERT_HEAD(&sh_hub_delayed_destroy, self, link); + pthread_mutex_unlock(&sh_hub_delayed_destroy_lock); + } else + sh_hub_destroy_inner(self); + } else + sh_hub_destroy_inner(self); +} + +uintptr_t sh_hub_get_orig_addr(sh_hub_t *self) { + return self->orig_addr; +} + +uintptr_t *sh_hub_get_orig_addr_addr(sh_hub_t *self) { + return &self->orig_addr; +} + +int sh_hub_add_proxy(sh_hub_t *self, uintptr_t func) { + int r = SHADOWHOOK_ERRNO_OK; + + pthread_mutex_lock(&self->proxies_lock); + + // check repeated funcion + sh_hub_proxy_t *proxy; + SLIST_FOREACH(proxy, &self->proxies, link) { + if (proxy->enabled && proxy->func == (void *)func) { + r = SHADOWHOOK_ERRNO_HOOK_DUP; + goto end; + } + } + + // try to re-enable an exists item + SLIST_FOREACH(proxy, &self->proxies, link) { + if (proxy->func == (void *)func) { + if (!proxy->enabled) __atomic_store_n((bool *)&proxy->enabled, true, __ATOMIC_SEQ_CST); + + SH_LOG_INFO("hub: add(re-enable) func %" PRIxPTR, func); + goto end; + } + } + + // create new item + if (NULL == (proxy = malloc(sizeof(sh_hub_proxy_t)))) { + r = SHADOWHOOK_ERRNO_OOM; + goto end; + } + proxy->func = (void *)func; + proxy->enabled = true; + + // insert to the head of the proxy-list + // equivalent to: SLIST_INSERT_HEAD(&self->proxies, proxy, link); + // but: __ATOMIC_RELEASE ensures readers see only fully-constructed item + SLIST_NEXT(proxy, link) = SLIST_FIRST(&self->proxies); + __atomic_store_n((uintptr_t *)(&SLIST_FIRST(&self->proxies)), (uintptr_t)proxy, __ATOMIC_RELEASE); + SH_LOG_INFO("hub: add(new) func %" PRIxPTR, func); + +end: + pthread_mutex_unlock(&self->proxies_lock); + return r; +} + +int sh_hub_del_proxy(sh_hub_t *self, uintptr_t func, bool *have_enabled_proxy) { + *have_enabled_proxy = false; + + pthread_mutex_lock(&self->proxies_lock); + + sh_hub_proxy_t *proxy; + bool deleted = false; + SLIST_FOREACH(proxy, &self->proxies, link) { + if (proxy->func == (void *)func) { + if (proxy->enabled) __atomic_store_n((bool *)&proxy->enabled, false, __ATOMIC_SEQ_CST); + + deleted = true; + SH_LOG_INFO("hub: del func %" PRIxPTR, func); + } + + if (proxy->enabled && !*have_enabled_proxy) *have_enabled_proxy = true; + + if (deleted && *have_enabled_proxy) break; + } + + pthread_mutex_unlock(&self->proxies_lock); + + return deleted ? 0 : -1; +} + +static sh_hub_frame_t *sh_hub_get_current_frame(void *return_address) { + sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key); + if (0 == stack->frames_cnt) return NULL; + sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + return frame->return_address == return_address ? frame : NULL; +} + +void sh_hub_allow_reentrant(void *return_address) { + sh_hub_frame_t *frame = sh_hub_get_current_frame(return_address); + if (NULL != frame) { + frame->flags |= SH_HUB_FRAME_FLAG_ALLOW_REENTRANT; + SH_LOG_DEBUG("hub: allow reentrant frame %p", return_address); + } +} + +void sh_hub_disallow_reentrant(void *return_address) { + sh_hub_frame_t *frame = sh_hub_get_current_frame(return_address); + if (NULL != frame) { + frame->flags &= ~SH_HUB_FRAME_FLAG_ALLOW_REENTRANT; + SH_LOG_DEBUG("hub: disallow reentrant frame %p", return_address); + } +} + +void *sh_hub_get_prev_func(void *func) { + sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key); + if (0 == stack->frames_cnt) sh_safe_abort(); // called in a non-hook status? + sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + + // find and return the next enabled hook-function in the hook-chain + bool found = false; + sh_hub_proxy_t *proxy; + SLIST_FOREACH(proxy, &(frame->proxies), link) { + if (!found) { + if (proxy->func == func) found = true; + } else { + if (proxy->enabled) break; + } + } + if (NULL != proxy) { + SH_LOG_DEBUG("hub: get_prev_func() return next enabled proxy %p", proxy->func); + return proxy->func; + } + + SH_LOG_DEBUG("hub: get_prev_func() return orig_addr %p", (void *)frame->orig_addr); + // did not find, return the original-function + return (void *)frame->orig_addr; +} + +void *sh_hub_get_return_address(void) { + sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key); + if (0 == stack->frames_cnt) sh_safe_abort(); // called in a non-hook status? + sh_hub_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + + return frame->return_address; +} diff --git a/app/src/main/cpp/shadowhook/sh_hub.h b/app/src/main/cpp/shadowhook/sh_hub.h new file mode 100644 index 0000000000000000000000000000000000000000..accbfb5f2c2c2dc588e81d9c530c296636704012 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_hub.h @@ -0,0 +1,45 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdbool.h> +#include <stdint.h> + +int sh_hub_init(void); + +typedef struct sh_hub sh_hub_t; + +sh_hub_t *sh_hub_create(uintptr_t target_addr, uintptr_t *trampo); +void sh_hub_destroy(sh_hub_t *self, bool with_delay); + +uintptr_t sh_hub_get_orig_addr(sh_hub_t *self); +uintptr_t *sh_hub_get_orig_addr_addr(sh_hub_t *self); + +int sh_hub_add_proxy(sh_hub_t *self, uintptr_t func); +int sh_hub_del_proxy(sh_hub_t *self, uintptr_t func, bool *have_enabled_proxy); + +void *sh_hub_get_prev_func(void *func); +void sh_hub_pop_stack(void *return_address); +void sh_hub_allow_reentrant(void *return_address); +void sh_hub_disallow_reentrant(void *return_address); +void *sh_hub_get_return_address(void); diff --git a/app/src/main/cpp/shadowhook/sh_jni.c b/app/src/main/cpp/shadowhook/sh_jni.c new file mode 100644 index 0000000000000000000000000000000000000000..051f6d992ac692eb05fd8e7c262e8085773e10f8 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_jni.c @@ -0,0 +1,139 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include <errno.h> +#include <jni.h> +#include <stdlib.h> +#include <unistd.h> + +#include "sh_log.h" +#include "shadowhook.h" + +#define SH_JNI_VERSION JNI_VERSION_1_6 +#define SH_JNI_CLASS_NAME "com/bytedance/shadowhook/ShadowHook" + +static jstring sh_jni_get_version(JNIEnv *env, jobject thiz) { + (void)thiz; + return (*env)->NewStringUTF(env, shadowhook_get_version()); +} + +static jint sh_jni_init(JNIEnv *env, jobject thiz, jint mode, jboolean debuggable) { + (void)env, (void)thiz; + + return shadowhook_init(0 == mode ? SHADOWHOOK_MODE_SHARED : SHADOWHOOK_MODE_UNIQUE, (bool)debuggable); +} + +static jint sh_jni_get_init_errno(JNIEnv *env, jobject thiz) { + (void)env, (void)thiz; + + return shadowhook_get_init_errno(); +} + +static jint sh_jni_get_mode(JNIEnv *env, jobject thiz) { + (void)env, (void)thiz; + + return SHADOWHOOK_MODE_SHARED == shadowhook_get_mode() ? 0 : 1; +} + +static jboolean sh_jni_get_debuggable(JNIEnv *env, jobject thiz) { + (void)env, (void)thiz; + + return shadowhook_get_debuggable(); +} + +static void sh_jni_set_debuggable(JNIEnv *env, jobject thiz, jboolean debuggable) { + (void)env, (void)thiz; + + shadowhook_set_debuggable((bool)debuggable); +} + +static jboolean sh_jni_get_recordable(JNIEnv *env, jobject thiz) { + (void)env, (void)thiz; + + return shadowhook_get_recordable(); +} + +static void sh_jni_set_recordable(JNIEnv *env, jobject thiz, jboolean recordable) { + (void)env, (void)thiz; + + shadowhook_set_recordable((bool)recordable); +} + +static jstring sh_jni_to_errmsg(JNIEnv *env, jobject thiz, jint error_number) { + (void)thiz; + + return (*env)->NewStringUTF(env, shadowhook_to_errmsg(error_number)); +} + +static jstring sh_jni_get_records(JNIEnv *env, jobject thiz, jint item_flags) { + (void)thiz; + + char *str = shadowhook_get_records((uint32_t)item_flags); + if (NULL == str) return NULL; + + jstring jstr = (*env)->NewStringUTF(env, str); + free(str); + return jstr; +} + +static jstring sh_jni_get_arch(JNIEnv *env, jobject thiz) { + (void)thiz; + +#if defined(__arm__) + char *arch = "arm"; +#elif defined(__aarch64__) + char *arch = "arm64"; +#else + char *arch = "unsupported"; +#endif + + return (*env)->NewStringUTF(env, arch); +} + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + (void)reserved; + + if (__predict_false(NULL == vm)) return JNI_ERR; + + JNIEnv *env; + if (__predict_false(JNI_OK != (*vm)->GetEnv(vm, (void **)&env, SH_JNI_VERSION))) return JNI_ERR; + if (__predict_false(NULL == env || NULL == *env)) return JNI_ERR; + + jclass cls; + if (__predict_false(NULL == (cls = (*env)->FindClass(env, SH_JNI_CLASS_NAME)))) return JNI_ERR; + + JNINativeMethod m[] = {{"nativeGetVersion", "()Ljava/lang/String;", (void *)sh_jni_get_version}, + {"nativeInit", "(IZ)I", (void *)sh_jni_init}, + {"nativeGetInitErrno", "()I", (void *)sh_jni_get_init_errno}, + {"nativeGetMode", "()I", (void *)sh_jni_get_mode}, + {"nativeGetDebuggable", "()Z", (void *)sh_jni_get_debuggable}, + {"nativeSetDebuggable", "(Z)V", (void *)sh_jni_set_debuggable}, + {"nativeGetRecordable", "()Z", (void *)sh_jni_get_recordable}, + {"nativeSetRecordable", "(Z)V", (void *)sh_jni_set_recordable}, + {"nativeToErrmsg", "(I)Ljava/lang/String;", (void *)sh_jni_to_errmsg}, + {"nativeGetRecords", "(I)Ljava/lang/String;", (void *)sh_jni_get_records}, + {"nativeGetArch", "()Ljava/lang/String;", (void *)sh_jni_get_arch}}; + if (__predict_false(0 != (*env)->RegisterNatives(env, cls, m, sizeof(m) / sizeof(m[0])))) return JNI_ERR; + + return SH_JNI_VERSION; +} diff --git a/app/src/main/cpp/shadowhook/sh_linker.c b/app/src/main/cpp/shadowhook/sh_linker.c new file mode 100644 index 0000000000000000000000000000000000000000..2cf3a2ebd5328ed51e645a3a099103e56769d82f --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_linker.c @@ -0,0 +1,409 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_linker.h" + +#include <dlfcn.h> +#include <pthread.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "sh_log.h" +#include "sh_recorder.h" +#include "sh_sig.h" +#include "sh_switch.h" +#include "sh_util.h" +#include "shadowhook.h" +#include "xdl.h" + +#ifndef __LP64__ +#define SH_LINKER_BASENAME "linker" +#else +#define SH_LINKER_BASENAME "linker64" +#endif + +#define SH_LINKER_SYM_G_DL_MUTEX "__dl__ZL10g_dl_mutex" +#define SH_LINKER_SYM_DO_DLOPEN_L "__dl__Z9do_dlopenPKciPK17android_dlextinfo" +#define SH_LINKER_SYM_DO_DLOPEN_N "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv" +#define SH_LINKER_SYM_DO_DLOPEN_O "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv" + +static bool sh_linker_dlopen_hooked = false; + +static sh_linker_post_dlopen_t sh_linker_post_dlopen; +static void *sh_linker_post_dlopen_arg; + +static pthread_mutex_t *sh_linker_g_dl_mutex; +static uintptr_t sh_linker_dlopen_addr; // save address of dlopen(==4.x) or do_dlopen(>=5.0) +static xdl_info_t sh_linker_dlopen_dlinfo; + +#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__ +static uintptr_t sh_linker_dlfcn[6]; +static const char *sh_linker_dlfcn_name[6] = {"dlopen", "dlerror", "dlsym", + "dladdr", "dlclose", "dl_unwind_find_exidx"}; +#endif + +__attribute__((constructor)) static void sh_linker_ctor(void) { + sh_linker_dlopen_addr = (uintptr_t)dlopen; +#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__ + sh_linker_dlfcn[0] = (uintptr_t)dlopen; + sh_linker_dlfcn[1] = (uintptr_t)dlerror; + sh_linker_dlfcn[2] = (uintptr_t)dlsym; + sh_linker_dlfcn[3] = (uintptr_t)dladdr; + sh_linker_dlfcn[4] = (uintptr_t)dlclose; + sh_linker_dlfcn[5] = (uintptr_t)dl_unwind_find_exidx; +#endif +} + +static void *sh_linker_get_base_addr(xdl_info_t *dlinfo) { + uintptr_t vaddr_min = UINTPTR_MAX; + for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(dlinfo->dlpi_phdr[i]); + if (PT_LOAD == phdr->p_type && vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr; + } + + if (UINTPTR_MAX == vaddr_min) + return dlinfo->dli_fbase; // should not happen + else + return (void *)((uintptr_t)dlinfo->dli_fbase + SH_UTIL_PAGE_START(vaddr_min)); +} + +static bool sh_linker_check_arch(xdl_info_t *dlinfo) { + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)sh_linker_get_base_addr(dlinfo); + +#if defined(__LP64__) +#define SH_LINKER_ELF_CLASS ELFCLASS64 +#define SH_LINKER_ELF_MACHINE EM_AARCH64 +#else +#define SH_LINKER_ELF_CLASS ELFCLASS32 +#define SH_LINKER_ELF_MACHINE EM_ARM +#endif + + if (0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return false; + if (SH_LINKER_ELF_CLASS != ehdr->e_ident[EI_CLASS]) return false; + if (SH_LINKER_ELF_MACHINE != ehdr->e_machine) return false; + + return true; +} + +int sh_linker_init(void) { + memset(&sh_linker_dlopen_dlinfo, 0, sizeof(sh_linker_dlopen_dlinfo)); + + int api_level = sh_util_get_api_level(); + if (__predict_true(api_level >= __ANDROID_API_L__)) { + sh_linker_dlopen_addr = 0; + + void *handle = xdl_open(SH_LINKER_BASENAME, XDL_DEFAULT); + if (__predict_false(NULL == handle)) return -1; + xdl_info(handle, XDL_DI_DLINFO, (void *)&sh_linker_dlopen_dlinfo); + sh_linker_dlopen_dlinfo.dli_fname = SH_LINKER_BASENAME; + + // get g_dl_mutex + sh_linker_g_dl_mutex = (pthread_mutex_t *)(xdl_dsym(handle, SH_LINKER_SYM_G_DL_MUTEX, NULL)); + + // get do_dlopen + if (api_level >= __ANDROID_API_O__) + sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_O; + else if (api_level >= __ANDROID_API_N__) + sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_N; + else + sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_L; + sh_linker_dlopen_dlinfo.dli_saddr = + xdl_dsym(handle, sh_linker_dlopen_dlinfo.dli_sname, &(sh_linker_dlopen_dlinfo.dli_ssize)); + sh_linker_dlopen_addr = (uintptr_t)sh_linker_dlopen_dlinfo.dli_saddr; + + xdl_close(handle); + } + + return (0 != sh_linker_dlopen_addr && (0 != sh_linker_g_dl_mutex || api_level < __ANDROID_API_L__)) ? 0 + : -1; +} + +const char *sh_linker_match_dlfcn(uintptr_t target_addr) { +#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__ + if (sh_util_get_api_level() < __ANDROID_API_L__) + for (size_t i = 0; i < sizeof(sh_linker_dlfcn) / sizeof(sh_linker_dlfcn[0]); i++) + if (sh_linker_dlfcn[i] == target_addr) return sh_linker_dlfcn_name[i]; +#else + (void)target_addr; +#endif + + return NULL; +} + +bool sh_linker_need_to_hook_dlopen(uintptr_t target_addr) { + return SHADOWHOOK_IS_UNIQUE_MODE && !sh_linker_dlopen_hooked && target_addr == sh_linker_dlopen_addr; +} + +typedef void *(*sh_linker_proxy_dlopen_t)(const char *, int); +static sh_linker_proxy_dlopen_t sh_linker_orig_dlopen; +static void *sh_linker_proxy_dlopen(const char *filename, int flag) { + void *handle; + if (SHADOWHOOK_IS_SHARED_MODE) + handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_dlopen, sh_linker_proxy_dlopen_t, filename, flag); + else + handle = sh_linker_orig_dlopen(filename, flag); + + if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg); + + if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK(); + return handle; +} + +typedef void *(*sh_linker_proxy_do_dlopen_l_t)(const char *, int, const void *); +static sh_linker_proxy_do_dlopen_l_t sh_linker_orig_do_dlopen_l; +static void *sh_linker_proxy_do_dlopen_l(const char *name, int flags, const void *extinfo) { + void *handle; + if (SHADOWHOOK_IS_SHARED_MODE) + handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_do_dlopen_l, sh_linker_proxy_do_dlopen_l_t, name, flags, + extinfo); + else + handle = sh_linker_orig_do_dlopen_l(name, flags, extinfo); + + if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg); + + if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK(); + return handle; +} + +typedef void *(*sh_linker_proxy_do_dlopen_n_t)(const char *, int, const void *, void *); +static sh_linker_proxy_do_dlopen_n_t sh_linker_orig_do_dlopen_n; +static void *sh_linker_proxy_do_dlopen_n(const char *name, int flags, const void *extinfo, + void *caller_addr) { + void *handle; + if (SHADOWHOOK_IS_SHARED_MODE) + handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_do_dlopen_n, sh_linker_proxy_do_dlopen_n_t, name, flags, + extinfo, caller_addr); + else + handle = sh_linker_orig_do_dlopen_n(name, flags, extinfo, caller_addr); + + if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg); + + if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK(); + return handle; +} + +int sh_linker_hook_dlopen(sh_linker_post_dlopen_t post_dlopen, void *post_dlopen_arg) { + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static int result = SHADOWHOOK_ERRNO_MONITOR_DLOPEN; + + if (sh_linker_dlopen_hooked) return result; + pthread_mutex_lock(&lock); + if (sh_linker_dlopen_hooked) goto end; + + // try hooking-dlopen only once + sh_linker_dlopen_hooked = true; + + // do init for SHARED mode + if (SHADOWHOOK_IS_SHARED_MODE) + if (0 != sh_linker_init()) goto end; + + // save post callback ptr before hooking + sh_linker_post_dlopen = post_dlopen; + sh_linker_post_dlopen_arg = post_dlopen_arg; + + // hook for dlopen() or do_dlopen() + int (*hook)(uintptr_t, uintptr_t, uintptr_t *, size_t *, xdl_info_t *) = + SHADOWHOOK_IS_SHARED_MODE ? sh_switch_hook : sh_switch_hook_invisible; + int api_level = sh_util_get_api_level(); + size_t backup_len = 0; + int r; + if (api_level < __ANDROID_API_L__) { + // get & check dlinfo + r = sh_linker_get_dlinfo_by_addr((void *)sh_linker_dlopen_addr, &sh_linker_dlopen_dlinfo, NULL, 0, NULL, + 0, false); + if (SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH == r) result = SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH; + if (0 != r) goto end; + + // hook + r = hook(sh_linker_dlopen_addr, (uintptr_t)sh_linker_proxy_dlopen, (uintptr_t *)&sh_linker_orig_dlopen, + &backup_len, &sh_linker_dlopen_dlinfo); + + // record + sh_recorder_add_hook(r, true, sh_linker_dlopen_addr, SH_LINKER_BASENAME, "dlopen", + (uintptr_t)sh_linker_proxy_dlopen, backup_len, UINTPTR_MAX, + (uintptr_t)(__builtin_return_address(0))); + + if (0 != r) goto end; + } else { + // check dlinfo + if (!sh_linker_check_arch(&sh_linker_dlopen_dlinfo)) { + result = SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH; + goto end; + } + + uintptr_t proxy; + uintptr_t *orig; + if (api_level >= __ANDROID_API_N__) { + proxy = (uintptr_t)sh_linker_proxy_do_dlopen_n; + orig = (uintptr_t *)&sh_linker_orig_do_dlopen_n; + } else { + proxy = (uintptr_t)sh_linker_proxy_do_dlopen_l; + orig = (uintptr_t *)&sh_linker_orig_do_dlopen_l; + } + + // hook + pthread_mutex_lock(sh_linker_g_dl_mutex); + r = hook(sh_linker_dlopen_addr, proxy, orig, &backup_len, &sh_linker_dlopen_dlinfo); + pthread_mutex_unlock(sh_linker_g_dl_mutex); + + // record + sh_recorder_add_hook(r, true, sh_linker_dlopen_addr, SH_LINKER_BASENAME, + sh_linker_dlopen_dlinfo.dli_sname, proxy, backup_len, UINTPTR_MAX, + (uintptr_t)(__builtin_return_address(0))); + + if (0 != r) goto end; + } + + // OK + result = 0; + +end: + pthread_mutex_unlock(&lock); + SH_LOG_INFO("linker: hook dlopen %s, return: %d", 0 == result ? "OK" : "FAILED", result); + return result; +} + +int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name, size_t lib_name_sz, + char *sym_name, size_t sym_name_sz, bool ignore_symbol_check) { + // dladdr() + bool crashed = false; + void *dlcache = NULL; + int r = 0; + if (sh_util_get_api_level() >= __ANDROID_API_L__) { + r = xdl_addr((void *)addr, dlinfo, &dlcache); + } else { + SH_SIG_TRY(SIGSEGV, SIGBUS) { + r = xdl_addr((void *)addr, dlinfo, &dlcache); + } + SH_SIG_CATCH() { + crashed = true; + } + SH_SIG_EXIT + } + SH_LOG_INFO("task: get dlinfo by target addr: target_addr %p, sym_name %s, sym_sz %zu, load_bias %" PRIxPTR + ", pathname %s", + addr, NULL == dlinfo->dli_sname ? "(NULL)" : dlinfo->dli_sname, dlinfo->dli_ssize, + (uintptr_t)dlinfo->dli_fbase, NULL == dlinfo->dli_fname ? "(NULL)" : dlinfo->dli_fname); + + // check error + if (crashed) { + r = SHADOWHOOK_ERRNO_HOOK_DLADDR_CRASH; + goto end; + } + if (0 == r || NULL == dlinfo->dli_fname) { + r = SHADOWHOOK_ERRNO_HOOK_DLINFO; + goto end; + } + if (!sh_linker_check_arch(dlinfo)) { + r = SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH; + goto end; + } + + if (NULL == dlinfo->dli_sname) { + if (ignore_symbol_check) { + dlinfo->dli_saddr = addr; + dlinfo->dli_sname = "unknown"; + dlinfo->dli_ssize = 1024; // big enough + } else { + const char *matched_dlfcn_name = NULL; + if (NULL == (matched_dlfcn_name = sh_linker_match_dlfcn((uintptr_t)addr))) { + r = SHADOWHOOK_ERRNO_HOOK_DLINFO; + goto end; + } else { + dlinfo->dli_saddr = addr; + dlinfo->dli_sname = matched_dlfcn_name; + dlinfo->dli_ssize = 4; // safe length, only relative jumps are allowed + SH_LOG_INFO("task: match dlfcn, target_addr %p, sym_name %s", addr, matched_dlfcn_name); + } + } + } + if (0 == dlinfo->dli_ssize) { + r = SHADOWHOOK_ERRNO_HOOK_SYMSZ; + goto end; + } + + if (NULL != lib_name) strlcpy(lib_name, dlinfo->dli_fname, lib_name_sz); + if (NULL != sym_name) strlcpy(sym_name, dlinfo->dli_sname, sym_name_sz); + r = 0; + +end: + xdl_addr_clean(&dlcache); + return r; +} + +int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name, xdl_info_t *dlinfo, + char *real_lib_name, size_t real_lib_name_sz) { + // open library + bool crashed = false; + void *handle = NULL; + if (sh_util_get_api_level() >= __ANDROID_API_L__) { + handle = xdl_open(lib_name, XDL_DEFAULT); + } else { + SH_SIG_TRY(SIGSEGV, SIGBUS) { + handle = xdl_open(lib_name, XDL_DEFAULT); + } + SH_SIG_CATCH() { + crashed = true; + } + SH_SIG_EXIT + } + if (crashed) return SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH; + if (NULL == handle) return SHADOWHOOK_ERRNO_PENDING; + + // get dlinfo + xdl_info(handle, XDL_DI_DLINFO, (void *)dlinfo); + + // check error + if (!sh_linker_check_arch(dlinfo)) { + xdl_close(handle); + return SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH; + } + + // lookup symbol address + crashed = false; + void *addr = NULL; + size_t sym_size = 0; + SH_SIG_TRY(SIGSEGV, SIGBUS) { + // do xdl_sym() or xdl_dsym() in an dlclosed-ELF will cause a crash + addr = xdl_sym(handle, sym_name, &sym_size); + if (NULL == addr) addr = xdl_dsym(handle, sym_name, &sym_size); + } + SH_SIG_CATCH() { + crashed = true; + } + SH_SIG_EXIT + + // close library + xdl_close(handle); + + if (crashed) return SHADOWHOOK_ERRNO_HOOK_DLSYM_CRASH; + if (NULL == addr) return SHADOWHOOK_ERRNO_HOOK_DLSYM; + + dlinfo->dli_fname = lib_name; + dlinfo->dli_sname = sym_name; + dlinfo->dli_saddr = addr; + dlinfo->dli_ssize = sym_size; + if (NULL != real_lib_name) strlcpy(real_lib_name, dlinfo->dli_fname, real_lib_name_sz); + return 0; +} diff --git a/app/src/main/cpp/shadowhook/sh_linker.h b/app/src/main/cpp/shadowhook/sh_linker.h new file mode 100644 index 0000000000000000000000000000000000000000..749e0a6d61a732945a85aabc8af3893683d68646 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_linker.h @@ -0,0 +1,41 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdbool.h> +#include <stdint.h> + +#include "xdl.h" + +int sh_linker_init(void); + +const char *sh_linker_match_dlfcn(uintptr_t target_addr); +bool sh_linker_need_to_hook_dlopen(uintptr_t target_addr); + +typedef void (*sh_linker_post_dlopen_t)(void *arg); +int sh_linker_hook_dlopen(sh_linker_post_dlopen_t post_dlopen, void *post_dlopen_arg); + +int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name, size_t lib_name_sz, + char *sym_name, size_t sym_name_sz, bool ignore_symbol_check); +int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name, xdl_info_t *dlinfo, + char *real_lib_name, size_t real_lib_name_sz); diff --git a/app/src/main/cpp/shadowhook/sh_recorder.c b/app/src/main/cpp/shadowhook/sh_recorder.c new file mode 100644 index 0000000000000000000000000000000000000000..22cf39df74b53224fc013370629e35866264e6f5 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_recorder.c @@ -0,0 +1,517 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_recorder.h" + +#include "sh_config.h" + +#ifdef SH_CONFIG_OPERATION_RECORDS + +#include <inttypes.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> + +#include "sh_sig.h" +#include "sh_util.h" +#include "shadowhook.h" +#include "xdl.h" + +#define SH_RECORDER_OP_HOOK_SYM_ADDR 0 +#define SH_RECORDER_OP_HOOK_SYM_NAME 1 +#define SH_RECORDER_OP_UNHOOK 2 + +#define SH_RECORDER_LIB_NAME_MAX 512 +#define SH_RECORDER_SYM_NAME_MAX 1024 + +#define SH_RECORDER_STRINGS_BUF_EXPAND_STEP (1024 * 16) +#define SH_RECORDER_STRINGS_BUF_MAX (1024 * 128) +#define SH_RECORDER_RECORDS_BUF_EXPAND_STEP (1024 * 32) +#define SH_RECORDER_RECORDS_BUF_MAX (1024 * 384) +#define SH_RECORDER_OUTPUT_BUF_EXPAND_STEP (1024 * 128) +#define SH_RECORDER_OUTPUT_BUF_MAX (1024 * 1024) + +static bool sh_recorder_recordable = false; + +bool sh_recorder_get_recordable(void) { + return sh_recorder_recordable; +} + +void sh_recorder_set_recordable(bool recordable) { + sh_recorder_recordable = recordable; +} + +typedef struct { + void *ptr; + size_t cap; + size_t sz; + pthread_mutex_t lock; +} sh_recorder_buf_t; + +static int sh_recorder_buf_append(sh_recorder_buf_t *buf, size_t step, size_t max, const void *header, + size_t header_sz, const void *body, size_t body_sz) { + size_t needs = (header_sz + (NULL != body ? body_sz : 0)); + if (needs > step) return -1; + + if (buf->cap - buf->sz < needs) { + size_t new_cap = buf->cap + step; + if (new_cap > max) return -1; + void *new_ptr = realloc(buf->ptr, new_cap); + if (NULL == new_ptr) return -1; + buf->ptr = new_ptr; + buf->cap = new_cap; + } + + memcpy((void *)((uintptr_t)buf->ptr + buf->sz), header, header_sz); + if (NULL != body) memcpy((void *)((uintptr_t)buf->ptr + buf->sz + header_sz), body, body_sz); + buf->sz += needs; + return 0; +} + +static void sh_recorder_buf_free(sh_recorder_buf_t *buf) { + if (NULL != buf->ptr) { + free(buf->ptr); + buf->ptr = NULL; + } +} + +static sh_recorder_buf_t sh_recorder_strings = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER}; +static sh_recorder_buf_t sh_recorder_records = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER}; +static bool sh_recorder_error = false; + +typedef struct { + uint16_t str_len; // body length, in order to speed up the search +} __attribute__((packed)) sh_recorder_str_header_t; +// +body: string, including the terminating null byte ('\0') + +typedef struct { + uint64_t op : 8; + uint64_t error_number : 8; + uint64_t ts_ms : 48; + uintptr_t stub; + uint16_t caller_lib_name_idx; + uint8_t backup_len; + uint16_t lib_name_idx; + uint16_t sym_name_idx; + uintptr_t sym_addr; + uintptr_t new_addr; +} __attribute__((packed)) sh_recorder_record_hook_header_t; +// no body + +typedef struct { + uint64_t op : 8; + uint64_t error_number : 8; + uint64_t ts_ms : 48; + uintptr_t stub; + uint16_t caller_lib_name_idx; +} __attribute__((packed)) sh_recorder_record_unhook_header_t; +// no body + +static int sh_recorder_add_str(const char *str, size_t str_len, uint16_t *str_idx) { + uint16_t idx = 0; + bool ok = false; + + pthread_mutex_lock(&sh_recorder_strings.lock); + + // find in existing strings + size_t i = 0; + while (i < sh_recorder_strings.sz) { + sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i); + if (header->str_len == str_len) { + void *tmp = (void *)((uintptr_t)sh_recorder_strings.ptr + i + sizeof(header->str_len)); + if (0 == memcmp(tmp, str, str_len)) { + *str_idx = idx; + ok = true; + break; // OK + } + } + i += (sizeof(sh_recorder_str_header_t) + header->str_len + 1); + idx++; + if (idx == UINT16_MAX) break; // failed + } + + // insert a new string + if (!ok && idx < UINT16_MAX) { + // append new string + sh_recorder_str_header_t header = {(uint16_t)str_len}; + if (0 == sh_recorder_buf_append(&sh_recorder_strings, SH_RECORDER_STRINGS_BUF_EXPAND_STEP, + SH_RECORDER_STRINGS_BUF_MAX, &header, sizeof(header), str, str_len + 1)) { + *str_idx = idx; + ok = true; // OK + } + } + + pthread_mutex_unlock(&sh_recorder_strings.lock); + + return ok ? 0 : -1; +} + +static char *sh_recorder_find_str(uint16_t idx) { + uint16_t cur_idx = 0; + + size_t i = 0; + while (i < sh_recorder_strings.sz && cur_idx < idx) { + sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i); + i += (sizeof(sh_recorder_str_header_t) + header->str_len + 1); + cur_idx++; + } + if (cur_idx != idx) return "error"; + + sh_recorder_str_header_t *header = (sh_recorder_str_header_t *)((uintptr_t)sh_recorder_strings.ptr + i); + return (char *)((uintptr_t)header + sizeof(sh_recorder_str_header_t)); +} + +static long sh_recorder_tz = LONG_MAX; + +static uint64_t sh_recorder_get_timestamp_ms(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + + if (LONG_MAX == sh_recorder_tz) { + // localtime_r() will call getenv() without lock protection, + // and will crash when encountering concurrent setenv() calls. + // We really encountered. + sh_recorder_tz = 0; + // struct tm tm; + // if (NULL != localtime_r((time_t *)(&(tv.tv_sec)), &tm)) sh_recorder_tz = tm.tm_gmtoff; + } + + return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000; +} + +static size_t sh_recorder_format_timestamp_ms(uint64_t ts_ms, char *buf, size_t buf_len) { + time_t sec = (time_t)(ts_ms / 1000); + time_t msec = (time_t)(ts_ms % 1000); + + struct tm tm; + sh_util_localtime_r(&sec, sh_recorder_tz, &tm); + + return sh_util_snprintf(buf, buf_len, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld%c%02ld:%02ld,", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + msec, sh_recorder_tz < 0 ? '-' : '+', labs(sh_recorder_tz / 3600), + labs(sh_recorder_tz % 3600)); +} + +static const char *sh_recorder_get_base_name(const char *lib_name) { + const char *p = strrchr(lib_name, '/'); + if (NULL != p && '\0' != *(p + 1)) + return p + 1; + else + return lib_name; +} + +static int sh_recorder_get_base_name_by_addr_iterator(struct dl_phdr_info *info, size_t size, void *arg) { + (void)size; + + uintptr_t *pkg = (uintptr_t *)arg; + uintptr_t addr = *pkg++; + char *base_name = (char *)*pkg++; + size_t base_name_sz = (size_t)*pkg; + + for (size_t i = 0; i < info->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); + if (PT_LOAD != phdr->p_type) continue; + if (addr < (uintptr_t)(info->dlpi_addr + phdr->p_vaddr) || + addr >= (uintptr_t)(info->dlpi_addr + phdr->p_vaddr + phdr->p_memsz)) + continue; + + // get lib_name from path_name + const char *p; + if (NULL == info->dlpi_name || '\0' == info->dlpi_name[0]) + p = "unknown"; + else { + p = strrchr(info->dlpi_name, '/'); + if (NULL == p || '\0' == *(p + 1)) + p = info->dlpi_name; + else + p++; + } + + strlcpy(base_name, p, base_name_sz); + return 1; // OK + } + + return 0; // continue +} + +static void sh_recorder_get_base_name_by_addr(uintptr_t addr, char *base_name, size_t base_name_sz) { + base_name[0] = '\0'; + uintptr_t pkg[3] = {addr, (uintptr_t)base_name, (uintptr_t)base_name_sz}; + + if (sh_util_get_api_level() >= __ANDROID_API_L__) { + xdl_iterate_phdr(sh_recorder_get_base_name_by_addr_iterator, pkg, XDL_DEFAULT); + } else { + SH_SIG_TRY(SIGSEGV, SIGBUS) { + xdl_iterate_phdr(sh_recorder_get_base_name_by_addr_iterator, pkg, XDL_DEFAULT); + } + SH_SIG_EXIT + } + + if ('\0' == base_name[0]) strlcpy(base_name, "unknown", base_name_sz); +} + +int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name, + const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub, + uintptr_t caller_addr) { + if (!sh_recorder_recordable) return 0; + if (sh_recorder_error) return -1; + + // lib_name + if (NULL == lib_name) return -1; + lib_name = sh_recorder_get_base_name(lib_name); + size_t lib_name_len = strlen(lib_name); + if (0 == lib_name_len || lib_name_len > SH_RECORDER_LIB_NAME_MAX) return -1; + + // sym_name + if (NULL == sym_name) return -1; + size_t sym_name_len = strlen(sym_name); + if (0 == sym_name_len || sym_name_len > SH_RECORDER_SYM_NAME_MAX) return -1; + + // caller_lib_name + char caller_lib_name[SH_RECORDER_LIB_NAME_MAX]; + sh_recorder_get_base_name_by_addr(caller_addr, caller_lib_name, sizeof(caller_lib_name)); + size_t caller_lib_name_len = strlen(caller_lib_name); + + // add strings to strings-pool + uint16_t lib_name_idx, sym_name_idx, caller_lib_name_idx; + if (0 != sh_recorder_add_str(lib_name, lib_name_len, &lib_name_idx)) goto err; + if (0 != sh_recorder_add_str(sym_name, sym_name_len, &sym_name_idx)) goto err; + if (0 != sh_recorder_add_str(caller_lib_name, caller_lib_name_len, &caller_lib_name_idx)) goto err; + + // append new hook record + sh_recorder_record_hook_header_t header = { + is_hook_sym_addr ? SH_RECORDER_OP_HOOK_SYM_ADDR : SH_RECORDER_OP_HOOK_SYM_NAME, + (uint8_t)error_number, + sh_recorder_get_timestamp_ms(), + stub, + caller_lib_name_idx, + (uint8_t)backup_len, + lib_name_idx, + sym_name_idx, + sym_addr, + new_addr}; + pthread_mutex_lock(&sh_recorder_records.lock); + int r = sh_recorder_buf_append(&sh_recorder_records, SH_RECORDER_RECORDS_BUF_EXPAND_STEP, + SH_RECORDER_RECORDS_BUF_MAX, &header, sizeof(header), NULL, 0); + pthread_mutex_unlock(&sh_recorder_records.lock); + if (0 != r) goto err; + + return 0; + +err: + sh_recorder_error = true; + return -1; +} + +int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr) { + if (!sh_recorder_recordable) return 0; + if (sh_recorder_error) return -1; + + char caller_lib_name[SH_RECORDER_LIB_NAME_MAX]; + sh_recorder_get_base_name_by_addr(caller_addr, caller_lib_name, sizeof(caller_lib_name)); + size_t caller_lib_name_len = strlen(caller_lib_name); + + uint16_t caller_lib_name_idx; + if (0 != sh_recorder_add_str(caller_lib_name, caller_lib_name_len, &caller_lib_name_idx)) goto err; + + sh_recorder_record_unhook_header_t header = {SH_RECORDER_OP_UNHOOK, (uint8_t)error_number, + sh_recorder_get_timestamp_ms(), stub, caller_lib_name_idx}; + pthread_mutex_lock(&sh_recorder_records.lock); + int r = sh_recorder_buf_append(&sh_recorder_records, SH_RECORDER_RECORDS_BUF_EXPAND_STEP, + SH_RECORDER_RECORDS_BUF_MAX, &header, sizeof(header), NULL, 0); + pthread_mutex_unlock(&sh_recorder_records.lock); + if (0 != r) goto err; + + return 0; + +err: + sh_recorder_error = true; + return -1; +} + +static const char *sh_recorder_get_op_name(uint8_t op) { + switch (op) { + case SH_RECORDER_OP_HOOK_SYM_ADDR: + return "hook_sym_addr"; + case SH_RECORDER_OP_HOOK_SYM_NAME: + return "hook_sym_name"; + case SH_RECORDER_OP_UNHOOK: + return "unhook"; + default: + return "error"; + } +} + +static void sh_recorder_output(char **str, int fd, uint32_t item_flags) { + if (NULL == sh_recorder_records.ptr || 0 == sh_recorder_records.sz) return; + + sh_recorder_buf_t output = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER}; + + pthread_mutex_lock(&sh_recorder_records.lock); + pthread_mutex_lock(&sh_recorder_strings.lock); + + char line[SH_RECORDER_LIB_NAME_MAX * 2 + SH_RECORDER_SYM_NAME_MAX + 256]; + size_t line_sz; + size_t i = 0; + while (i < sh_recorder_records.sz) { + line_sz = 0; + sh_recorder_record_hook_header_t *header = + (sh_recorder_record_hook_header_t *)((uintptr_t)sh_recorder_records.ptr + i); + + if (item_flags & SHADOWHOOK_RECORD_ITEM_TIMESTAMP) + line_sz += sh_recorder_format_timestamp_ms(header->ts_ms, line + line_sz, sizeof(line) - line_sz); + if (item_flags & SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,", + sh_recorder_find_str(header->caller_lib_name_idx)); + if (item_flags & SHADOWHOOK_RECORD_ITEM_OP) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,", + sh_recorder_get_op_name(header->op)); + if ((item_flags & SHADOWHOOK_RECORD_ITEM_LIB_NAME) && header->op != SH_RECORDER_OP_UNHOOK) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,", + sh_recorder_find_str(header->lib_name_idx)); + if ((item_flags & SHADOWHOOK_RECORD_ITEM_SYM_NAME) && header->op != SH_RECORDER_OP_UNHOOK) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,", + sh_recorder_find_str(header->sym_name_idx)); + if ((item_flags & SHADOWHOOK_RECORD_ITEM_SYM_ADDR) && header->op != SH_RECORDER_OP_UNHOOK) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->sym_addr); + if ((item_flags & SHADOWHOOK_RECORD_ITEM_NEW_ADDR) && header->op != SH_RECORDER_OP_UNHOOK) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->new_addr); + if ((item_flags & SHADOWHOOK_RECORD_ITEM_BACKUP_LEN) && header->op != SH_RECORDER_OP_UNHOOK) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIu8 ",", header->backup_len); + if (item_flags & SHADOWHOOK_RECORD_ITEM_ERRNO) + line_sz += + sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIu8 ",", header->error_number); + if (item_flags & SHADOWHOOK_RECORD_ITEM_STUB) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->stub); + line[line_sz - 1] = '\n'; + + if (NULL != str) { + // append to string + if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX, + line, line_sz, NULL, 0)) { + sh_recorder_buf_free(&output); + break; // failed + } + } else { + // write to FD + if (0 != sh_util_write(fd, line, line_sz)) break; // failed + } + + i += (SH_RECORDER_OP_UNHOOK == header->op ? sizeof(sh_recorder_record_unhook_header_t) + : sizeof(sh_recorder_record_hook_header_t)); + } + + pthread_mutex_unlock(&sh_recorder_strings.lock); + pthread_mutex_unlock(&sh_recorder_records.lock); + + // error message + if (sh_recorder_error) { + line_sz = 0; + + if (item_flags & SHADOWHOOK_RECORD_ITEM_TIMESTAMP) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "9999-99-99T00:00:00.000+00:00,"); + if (item_flags & SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,"); + if (item_flags & SHADOWHOOK_RECORD_ITEM_OP) + line_sz += sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,"); + + if (0 == line_sz) line_sz = sh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,"); + + line[line_sz - 1] = '\n'; + + if (NULL != str) { + // append to string + if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX, + line, line_sz, NULL, 0)) { + sh_recorder_buf_free(&output); + return; // failed + } + } else { + // write to FD + if (0 != sh_util_write(fd, line, line_sz)) return; // failed + } + } + + // return string + if (NULL != str) { + if (0 != sh_recorder_buf_append(&output, SH_RECORDER_OUTPUT_BUF_EXPAND_STEP, SH_RECORDER_OUTPUT_BUF_MAX, + "", 1, NULL, 0)) { + sh_recorder_buf_free(&output); + return; // failed + } + *str = output.ptr; + } +} + +char *sh_recorder_get(uint32_t item_flags) { + if (!sh_recorder_recordable) return NULL; + if (0 == (item_flags & SHADOWHOOK_RECORD_ITEM_ALL)) return NULL; + + char *str = NULL; + sh_recorder_output(&str, -1, item_flags); + return str; +} + +void sh_recorder_dump(int fd, uint32_t item_flags) { + if (!sh_recorder_recordable) return; + if (0 == (item_flags & SHADOWHOOK_RECORD_ITEM_ALL)) return; + if (fd < 0) return; + sh_recorder_output(NULL, fd, item_flags); +} + +#else + +bool sh_recorder_get_recordable(void) { + return false; +} + +void sh_recorder_set_recordable(bool recordable) { + (void)recordable; +} + +int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name, + const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub, + uintptr_t caller_addr) { + (void)error_number, (void)is_hook_sym_addr, (void)sym_addr, (void)lib_name, (void)sym_name, (void)new_addr, + (void)backup_len, (void)stub, (void)caller_addr; + return 0; +} + +int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr) { + (void)error_number, (void)stub, (void)caller_addr; + return 0; +} + +char *sh_recorder_get(uint32_t item_flags) { + (void)item_flags; + return NULL; +} + +void sh_recorder_dump(int fd, uint32_t item_flags) { + (void)fd, (void)item_flags; +} + +#endif diff --git a/app/src/main/cpp/shadowhook/sh_recorder.h b/app/src/main/cpp/shadowhook/sh_recorder.h new file mode 100644 index 0000000000000000000000000000000000000000..91ce8435476fac0df3599fd88f23c3eeb54ad7be --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_recorder.h @@ -0,0 +1,37 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdbool.h> +#include <stdint.h> + +bool sh_recorder_get_recordable(void); +void sh_recorder_set_recordable(bool recordable); + +int sh_recorder_add_hook(int error_number, bool is_hook_sym_addr, uintptr_t sym_addr, const char *lib_name, + const char *sym_name, uintptr_t new_addr, size_t backup_len, uintptr_t stub, + uintptr_t caller_addr); +int sh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr); + +char *sh_recorder_get(uint32_t item_flags); +void sh_recorder_dump(int fd, uint32_t item_flags); diff --git a/app/src/main/cpp/shadowhook/sh_safe.c b/app/src/main/cpp/shadowhook/sh_safe.c new file mode 100644 index 0000000000000000000000000000000000000000..eacc14dfa10b7e5c0b17e66bf72980fd5d83b795 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_safe.c @@ -0,0 +1,133 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_safe.h" + +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> + +#include "sh_util.h" +#include "xdl.h" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshorten-64-to-32" +#include "linux_syscall_support.h" +#pragma clang diagnostic pop + +#define SH_SAFE_IDX_PTHREAD_GETSPECIFIC 0 +#define SH_SAFE_IDX_PTHREAD_SETSPECIFIC 1 +#define SH_SAFE_IDX_ABORT 2 + +#define SH_SAFE_IDX_SZ 3 +typedef struct { + uintptr_t target_addr; + uintptr_t orig_addr; +} sh_safe_addr_t; +static sh_safe_addr_t sh_safe_addrs[SH_SAFE_IDX_SZ]; +static int sh_safe_api_level; + +static int sh_safe_init_func(void *handle, const char *symbol, size_t idx) { + sh_safe_addrs[idx].target_addr = (uintptr_t)(xdl_sym(handle, symbol, NULL)); + if (__predict_false(0 == sh_safe_addrs[idx].target_addr)) return -1; + sh_safe_addrs[idx].orig_addr = 0; + return 0; +} + +int sh_safe_init(void) { + sh_safe_api_level = sh_util_get_api_level(); + + void *handle = xdl_open("libc.so", XDL_DEFAULT); + if (NULL == handle) return -1; + + int r = -1; + if (__predict_false(0 != sh_safe_init_func(handle, "pthread_getspecific", SH_SAFE_IDX_PTHREAD_GETSPECIFIC))) + goto end; + if (__predict_false(0 != sh_safe_init_func(handle, "pthread_setspecific", SH_SAFE_IDX_PTHREAD_SETSPECIFIC))) + goto end; + if (__predict_false(0 != sh_safe_init_func(handle, "abort", SH_SAFE_IDX_ABORT))) goto end; + r = 0; + +end: + xdl_close(handle); + return r; +} + +uintptr_t *sh_safe_get_orig_addr_addr(uintptr_t target_addr) { + for (size_t i = 0; i < SH_SAFE_IDX_SZ; i++) { + if (sh_safe_addrs[i].target_addr == target_addr) { + return &sh_safe_addrs[i].orig_addr; + } + } + return NULL; +} + +static uintptr_t sh_safe_get_orig_addr(size_t idx) { + sh_safe_addr_t *addr = &sh_safe_addrs[idx]; + return 0 != addr->orig_addr ? addr->orig_addr : addr->target_addr; +} + +void *sh_safe_pthread_getspecific(pthread_key_t key) { + uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_PTHREAD_GETSPECIFIC); + return ((void *(*)(pthread_key_t))addr)(key); +} + +int sh_safe_pthread_setspecific(pthread_key_t key, const void *value) { + if (sh_safe_api_level >= __ANDROID_API_M__) { + uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_PTHREAD_SETSPECIFIC); + return ((int (*)(pthread_key_t, const void *))addr)(key, value); + } else { + // Before Android M, pthread_setspecific() will call pthread_mutex_lock() and + // pthread_mutex_unlock(). So if we use pthread_setspecific() in hub's trampo, + // we will NOT be able to hook pthread_mutex_lock() and pthread_mutex_unlock(). + void **tls; +#if defined(__aarch64__) + __asm__("mrs %0, tpidr_el0" : "=r"(tls)); +#elif defined(__arm__) + __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(tls)); +#endif +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-qual" + tls[key] = (void *)value; +#pragma clang diagnostic pop + return 0; + } +} + +void sh_safe_abort(void) { + uintptr_t addr = sh_safe_get_orig_addr(SH_SAFE_IDX_ABORT); + ((void (*)(void))addr)(); +} + +void *sh_safe_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { + return sys_mmap(addr, length, prot, flags, fd, offset); +} + +int sh_safe_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, + unsigned long arg5) { + return sys_prctl(option, arg2, arg3, arg4, arg5); +} diff --git a/app/src/main/cpp/shadowhook/sh_safe.h b/app/src/main/cpp/shadowhook/sh_safe.h new file mode 100644 index 0000000000000000000000000000000000000000..5f353bd8687dd0e199c371230d35859f02e78e34 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_safe.h @@ -0,0 +1,37 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> + +int sh_safe_init(void); +uintptr_t *sh_safe_get_orig_addr_addr(uintptr_t target_addr); + +void *sh_safe_pthread_getspecific(pthread_key_t key); +int sh_safe_pthread_setspecific(pthread_key_t key, const void *value); +void sh_safe_abort(void); + +void *sh_safe_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); +int sh_safe_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); diff --git a/app/src/main/cpp/shadowhook/sh_switch.c b/app/src/main/cpp/shadowhook/sh_switch.c new file mode 100644 index 0000000000000000000000000000000000000000..0eb7ba9eee355ab31fbae321f8c0fcf3deab5ac2 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_switch.c @@ -0,0 +1,343 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_switch.h" + +#include <inttypes.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "sh_config.h" +#include "sh_errno.h" +#include "sh_hub.h" +#include "sh_inst.h" +#include "sh_linker.h" +#include "sh_log.h" +#include "sh_safe.h" +#include "sh_sig.h" +#include "sh_util.h" +#include "shadowhook.h" +#include "tree.h" +#include "xdl.h" + +// switch +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct sh_switch { + sh_inst_t inst; // align 16 + uintptr_t target_addr; + sh_hub_t *hub; + RB_ENTRY(sh_switch) link; +} sh_switch_t; +#pragma clang diagnostic pop + +// switch tree +static __inline__ int sh_switch_cmp(sh_switch_t *a, sh_switch_t *b) { + if (a->target_addr == b->target_addr) + return 0; + else + return a->target_addr > b->target_addr ? 1 : -1; +} +typedef RB_HEAD(sh_switch_tree, sh_switch) sh_switch_tree_t; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +RB_GENERATE_STATIC(sh_switch_tree, sh_switch, link, sh_switch_cmp) +#pragma clang diagnostic pop + +// switch tree object +static sh_switch_tree_t sh_switches = RB_INITIALIZER(&sh_switches); +static pthread_rwlock_t sh_switches_lock = PTHREAD_RWLOCK_INITIALIZER; + +static sh_switch_t *sh_switch_find(uintptr_t target_addr) { + sh_switch_t key = {.target_addr = target_addr}; + + pthread_rwlock_rdlock(&sh_switches_lock); + sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key); + pthread_rwlock_unlock(&sh_switches_lock); + + return self; +} + +static int sh_switch_create(sh_switch_t **self, uintptr_t target_addr, uintptr_t *hub_trampo) { + *self = memalign(16, sizeof(sh_switch_t)); + if (NULL == *self) return SHADOWHOOK_ERRNO_OOM; + + memset(&(*self)->inst, 0, sizeof((*self)->inst)); + (*self)->target_addr = target_addr; + (*self)->hub = NULL; + + if (NULL != hub_trampo) { + if (NULL == ((*self)->hub = sh_hub_create(target_addr, hub_trampo))) { + free(self); + return SHADOWHOOK_ERRNO_HUB_CREAT; + } + } + + return 0; +} + +static void sh_switch_destroy(sh_switch_t *self, bool hub_with_delay) { + if (NULL != self->hub) sh_hub_destroy(self->hub, hub_with_delay); + free(self); +} + +static void sh_switch_dump_enter(sh_switch_t *self) { +#ifdef SH_CONFIG_DEBUG + char buf[1024]; + size_t len = 0; + size_t zero = 0; + for (size_t i = 0; i < 128; i++) { + uint16_t inst = ((uint16_t *)(self->inst.enter_addr))[i]; + zero = (0 == inst ? zero + 1 : 0); + if (zero > 4) break; + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "%04" PRIx16 " ", inst); + } + SH_LOG_DEBUG("switch: enter < %s>", buf); +#else + (void)self; +#endif +} + +static int sh_switch_hook_unique(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, + size_t *backup_len, xdl_info_t *dlinfo) { + sh_switch_t *self = sh_switch_find(target_addr); + if (NULL != self) return SHADOWHOOK_ERRNO_HOOK_DUP; + + // alloc new switch + int r; + if (0 != (r = sh_switch_create(&self, target_addr, NULL))) return r; + + sh_switch_t *useless = NULL; + pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start + + // insert new switch to switch-tree + if (NULL != RB_INSERT(sh_switch_tree, &sh_switches, self)) { + useless = self; + r = SHADOWHOOK_ERRNO_HOOK_DUP; + goto end; + } + + // do hook + if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, new_addr, orig_addr, NULL))) { + RB_REMOVE(sh_switch_tree, &sh_switches, self); + useless = self; + goto end; + } + *backup_len = self->inst.backup_len; + sh_switch_dump_enter(self); + +end: + pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end + if (NULL != useless) sh_switch_destroy(useless, false); + return r; +} + +static int sh_switch_hook_shared(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, + size_t *backup_len, xdl_info_t *dlinfo) { + int r; + + pthread_rwlock_rdlock(&sh_switches_lock); // SYNC(read) - start + sh_switch_t key = {.target_addr = target_addr}; + sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key); + if (NULL != self) // already exists + { + // add an new proxy to hub + if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(self->hub); + r = sh_hub_add_proxy(self->hub, new_addr); + pthread_rwlock_unlock(&sh_switches_lock); // SYNC(read) - end + + *backup_len = self->inst.backup_len; + return r; + } + pthread_rwlock_unlock(&sh_switches_lock); // SYNC(read) - end + + // first hook for this target_addr + + // alloc new switch + uintptr_t hub_trampo; + if (0 != (r = sh_switch_create(&self, target_addr, &hub_trampo))) return r; + + sh_switch_t *useless = NULL; + pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start + + // insert new switch to switch-tree + sh_switch_t *exists; + if (NULL != (exists = RB_INSERT(sh_switch_tree, &sh_switches, self))) { + // already exists + useless = self; + if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(exists->hub); + r = sh_hub_add_proxy(exists->hub, new_addr); + *backup_len = exists->inst.backup_len; + } else { + // do hook + uintptr_t *safe_orig_addr_addr = sh_safe_get_orig_addr_addr(target_addr); + if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, hub_trampo, + sh_hub_get_orig_addr_addr(self->hub), safe_orig_addr_addr))) { + RB_REMOVE(sh_switch_tree, &sh_switches, self); + useless = self; + goto end; + } + *backup_len = self->inst.backup_len; + sh_switch_dump_enter(self); + + // return original-address + if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(self->hub); + + // add proxy to hub + if (0 != (r = sh_hub_add_proxy(self->hub, new_addr))) { + sh_inst_unhook(&self->inst, target_addr); + *backup_len = 0; + RB_REMOVE(sh_switch_tree, &sh_switches, self); + useless = self; + goto end; + } + } + +end: + pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end + if (NULL != useless) sh_switch_destroy(useless, false); + + return r; +} + +int sh_switch_hook(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len, + xdl_info_t *dlinfo) { + int r; + if (SHADOWHOOK_IS_UNIQUE_MODE) + r = sh_switch_hook_unique(target_addr, new_addr, orig_addr, backup_len, dlinfo); + else + r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo); + + if (0 == r) + SH_LOG_INFO("switch: hook in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR, + SHADOWHOOK_IS_UNIQUE_MODE ? "UNIQUE" : "SHARED", target_addr, new_addr); + + return r; +} + +static int sh_switch_hook_unique_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, + size_t *backup_len, xdl_info_t *dlinfo) { + pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start + + // do hook + sh_inst_t inst; + int r = sh_inst_hook(&inst, target_addr, dlinfo, new_addr, orig_addr, NULL); + + pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end + + *backup_len = inst.backup_len; + return r; +} + +int sh_switch_hook_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, + size_t *backup_len, xdl_info_t *dlinfo) { + int r; + if (SHADOWHOOK_IS_UNIQUE_MODE) + r = sh_switch_hook_unique_invisible(target_addr, new_addr, orig_addr, backup_len, dlinfo); + else + r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo); + + if (0 == r) + SH_LOG_INFO("switch: hook(invisible) in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR, + SHADOWHOOK_IS_UNIQUE_MODE ? "UNIQUE" : "SHARED", target_addr, new_addr); + return r; +} + +static int sh_switch_unhook_unique(uintptr_t target_addr) { + int r; + sh_switch_t *useless = NULL; + + pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start + + sh_switch_t key = {.target_addr = target_addr}; + sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key); + if (NULL == self) { + r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND; + goto end; + } + r = sh_inst_unhook(&self->inst, target_addr); + RB_REMOVE(sh_switch_tree, &sh_switches, self); + useless = self; + +end: + pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end + if (NULL != useless) sh_switch_destroy(useless, false); + return r; +} + +static int sh_switch_unhook_shared(uintptr_t target_addr, uintptr_t new_addr) { + int r; + sh_switch_t *useless = NULL; + + pthread_rwlock_wrlock(&sh_switches_lock); // SYNC - start + + sh_switch_t key = {.target_addr = target_addr}; + sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key); + if (NULL == self) { + r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND; + goto end; + } + + // delete proxy in hub + bool have_enabled_proxy; + if (0 != sh_hub_del_proxy(self->hub, new_addr, &have_enabled_proxy)) { + r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND; + goto end; + } + r = 0; + + // unhook inst, remove current switch from switch-tree + if (!have_enabled_proxy) { + r = sh_inst_unhook(&self->inst, target_addr); + + uintptr_t *safe_orig_addr_addr = sh_safe_get_orig_addr_addr(target_addr); + if (NULL != safe_orig_addr_addr) __atomic_store_n(safe_orig_addr_addr, 0, __ATOMIC_SEQ_CST); + + RB_REMOVE(sh_switch_tree, &sh_switches, self); + useless = self; + } + +end: + pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end + if (NULL != useless) sh_switch_destroy(useless, true); + return r; +} + +int sh_switch_unhook(uintptr_t target_addr, uintptr_t new_addr) { + int r; + if (SHADOWHOOK_IS_UNIQUE_MODE) { + r = sh_switch_unhook_unique(target_addr); + if (0 == r) SH_LOG_INFO("switch: unhook in UNIQUE mode OK: target_addr %" PRIxPTR, target_addr); + } else { + r = sh_switch_unhook_shared(target_addr, new_addr); + if (0 == r) + SH_LOG_INFO("switch: unhook in SHARED mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR, + target_addr, new_addr); + } + + return r; +} diff --git a/app/src/main/cpp/shadowhook/sh_switch.h b/app/src/main/cpp/shadowhook/sh_switch.h new file mode 100644 index 0000000000000000000000000000000000000000..3899a9344686b25eb4035410e59b54ec93ab66aa --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_switch.h @@ -0,0 +1,34 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdint.h> + +#include "xdl.h" + +int sh_switch_hook(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len, + xdl_info_t *dlinfo); +int sh_switch_unhook(uintptr_t target_addr, uintptr_t new_addr); + +int sh_switch_hook_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, + size_t *backup_len, xdl_info_t *dlinfo); diff --git a/app/src/main/cpp/shadowhook/sh_task.c b/app/src/main/cpp/shadowhook/sh_task.c new file mode 100644 index 0000000000000000000000000000000000000000..49ad751d96eb40c6228b10f0c14a26a8b4eedd87 --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_task.c @@ -0,0 +1,333 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_task.h" + +#include <inttypes.h> +#include <malloc.h> +#include <poll.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/eventfd.h> +#include <unistd.h> + +#include "queue.h" +#include "sh_config.h" +#include "sh_errno.h" +#include "sh_linker.h" +#include "sh_log.h" +#include "sh_recorder.h" +#include "sh_sig.h" +#include "sh_switch.h" +#include "sh_util.h" +#include "shadowhook.h" +#include "xdl.h" + +#define SH_TASK_THREAD_NAME "shadowhook-task" + +// task +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +struct sh_task { + char *lib_name; + char *sym_name; + uintptr_t target_addr; + uintptr_t new_addr; + uintptr_t *orig_addr; + shadowhook_hooked_t hooked; + void *hooked_arg; + uintptr_t caller_addr; + bool finished; + bool error; + bool ignore_symbol_check; + TAILQ_ENTRY(sh_task, ) link; +}; +#pragma clang diagnostic pop + +// task queue +typedef TAILQ_HEAD(sh_task_queue, sh_task, ) sh_task_queue_t; + +// task queue object +static sh_task_queue_t sh_tasks = TAILQ_HEAD_INITIALIZER(sh_tasks); +static pthread_rwlock_t sh_tasks_lock = PTHREAD_RWLOCK_INITIALIZER; +static int sh_tasks_unfinished_cnt = 0; + +static int sh_task_eventfd; +static int thread_started_result = SHADOWHOOK_ERRNO_MONITOR_THREAD; + +sh_task_t *sh_task_create_by_target_addr(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, + bool ignore_symbol_check, uintptr_t caller_addr) { + sh_task_t *self = malloc(sizeof(sh_task_t)); + if (NULL == self) return NULL; + self->lib_name = NULL; + self->sym_name = NULL; + self->target_addr = target_addr; + self->new_addr = new_addr; + self->orig_addr = orig_addr; + self->hooked = NULL; + self->hooked_arg = NULL; + self->caller_addr = caller_addr; + self->finished = false; + self->error = false; + self->ignore_symbol_check = ignore_symbol_check; + + return self; +} + +sh_task_t *sh_task_create_by_sym_name(const char *lib_name, const char *sym_name, uintptr_t new_addr, + uintptr_t *orig_addr, shadowhook_hooked_t hooked, void *hooked_arg, + uintptr_t caller_addr) { + sh_task_t *self = malloc(sizeof(sh_task_t)); + if (NULL == self) return NULL; + + if (NULL == (self->lib_name = strdup(lib_name))) goto err; + if (NULL == (self->sym_name = strdup(sym_name))) goto err; + self->target_addr = 0; + self->new_addr = new_addr; + self->orig_addr = orig_addr; + self->hooked = hooked; + self->hooked_arg = hooked_arg; + self->caller_addr = caller_addr; + self->finished = false; + self->error = false; + self->ignore_symbol_check = false; + + return self; + +err: + if (NULL != self->lib_name) free(self->lib_name); + if (NULL != self->sym_name) free(self->sym_name); + free(self); + return NULL; +} + +void sh_task_destroy(sh_task_t *self) { + if (NULL != self->lib_name) free(self->lib_name); + if (NULL != self->sym_name) free(self->sym_name); + free(self); +} + +static void sh_task_do_callback(sh_task_t *self, int error_number) { + if (NULL != self->hooked) + self->hooked(error_number, self->lib_name, self->sym_name, (void *)self->target_addr, + (void *)self->new_addr, self->orig_addr, self->hooked_arg); +} + +static int sh_task_hook_pending(struct dl_phdr_info *info, size_t size, void *arg) { + (void)size, (void)arg; + + pthread_rwlock_rdlock(&sh_tasks_lock); + + sh_task_t *task; + TAILQ_FOREACH(task, &sh_tasks, link) { + if (task->finished) continue; + if ('/' == info->dlpi_name[0] && NULL == strstr(info->dlpi_name, task->lib_name)) continue; + if ('/' != info->dlpi_name[0] && NULL == strstr(task->lib_name, info->dlpi_name)) continue; + + xdl_info_t dlinfo; + char real_lib_name[512]; + int r = sh_linker_get_dlinfo_by_sym_name(task->lib_name, task->sym_name, &dlinfo, real_lib_name, + sizeof(real_lib_name)); + task->target_addr = (uintptr_t)dlinfo.dli_saddr; + if (SHADOWHOOK_ERRNO_PENDING != r) { + size_t backup_len = 0; + if (0 == r) { + r = sh_switch_hook(task->target_addr, task->new_addr, task->orig_addr, &backup_len, &dlinfo); + if (0 != r) task->error = true; + } else { + strlcpy(real_lib_name, task->lib_name, sizeof(real_lib_name)); + task->error = true; + } + sh_recorder_add_hook(r, false, task->target_addr, real_lib_name, task->sym_name, task->new_addr, + backup_len, (uintptr_t)task, task->caller_addr); + task->finished = true; + sh_task_do_callback(task, r); + if (0 == __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST)) break; + } + } + + pthread_rwlock_unlock(&sh_tasks_lock); + + return __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0 ? 0 : 1; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-statement-expression" + +static void sh_task_post_dlopen_callback(void *arg) { + (void)arg; + + if (0 == thread_started_result && __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0) { + uint64_t ev_val = 1; + SH_UTIL_TEMP_FAILURE_RETRY(write(sh_task_eventfd, &ev_val, sizeof(ev_val))); + } +} + +__noreturn static void *sh_task_thread_func(void *arg) { + (void)arg; + pthread_t thread = pthread_self(); + pthread_setname_np(thread, SH_TASK_THREAD_NAME); + pthread_detach(thread); + + struct pollfd ev = {.fd = sh_task_eventfd, .events = POLLIN, .revents = 0}; + while (1) { + int n = SH_UTIL_TEMP_FAILURE_RETRY(poll(&ev, 1, -1)); + if (n < 0) { + sleep(1); + continue; + } else if (n > 0) { + uint64_t ev_val; + SH_UTIL_TEMP_FAILURE_RETRY(read(sh_task_eventfd, &ev_val, sizeof(ev_val))); + + if (sh_util_get_api_level() >= __ANDROID_API_L__) { + xdl_iterate_phdr(sh_task_hook_pending, NULL, XDL_DEFAULT); + } else { + SH_SIG_TRY(SIGSEGV, SIGBUS) { + xdl_iterate_phdr(sh_task_hook_pending, NULL, XDL_DEFAULT); + } + SH_SIG_CATCH() { + SH_LOG_WARN("task: dliterate crashed"); + } + SH_SIG_EXIT + } + } + } +} + +#pragma clang diagnostic pop + +static int sh_task_start_monitor(bool start_thread) { + static bool thread_started = false; + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + pthread_t thread; + int r; + + // hook linker dlopen() + if (0 != (r = sh_linker_hook_dlopen(sh_task_post_dlopen_callback, NULL))) return r; + + if (!start_thread) return 0; + + // start thread + if (thread_started) return thread_started_result; + pthread_mutex_lock(&lock); + if (thread_started) goto end; + + if (0 > (sh_task_eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC))) goto end; + if (0 != pthread_create(&thread, NULL, &sh_task_thread_func, NULL)) goto end; + + // OK + thread_started_result = 0; + +end: + thread_started = true; + pthread_mutex_unlock(&lock); + SH_LOG_INFO("task: start monitor %s, return: %d", 0 == thread_started_result ? "OK" : "FAILED", + thread_started_result); + return thread_started_result; +} + +int sh_task_hook(sh_task_t *self) { + int r; + bool is_hook_sym_addr = true; + char real_lib_name[512] = "unknown"; + char real_sym_name[1024] = "unknown"; + size_t backup_len = 0; + + // find target-address by library-name and symbol-name + xdl_info_t dlinfo; + memset(&dlinfo, 0, sizeof(xdl_info_t)); + if (0 == self->target_addr) { + is_hook_sym_addr = false; + strlcpy(real_lib_name, self->lib_name, sizeof(real_lib_name)); + strlcpy(real_sym_name, self->sym_name, sizeof(real_sym_name)); + r = sh_linker_get_dlinfo_by_sym_name(self->lib_name, self->sym_name, &dlinfo, real_lib_name, + sizeof(real_lib_name)); + if (SHADOWHOOK_ERRNO_PENDING == r) { + // we need to start monitor linker dlopen for handle the pending task + if (0 != (r = sh_task_start_monitor(true))) goto end; + r = SHADOWHOOK_ERRNO_PENDING; + goto end; + } + if (0 != r) goto end; // error + self->target_addr = (uintptr_t)dlinfo.dli_saddr; // OK + } else { + r = sh_linker_get_dlinfo_by_addr((void *)self->target_addr, &dlinfo, real_lib_name, sizeof(real_lib_name), + real_sym_name, sizeof(real_sym_name), self->ignore_symbol_check); + if (0 != r) goto end; // error + } + + // In UNIQUE mode, if external users are hooking the linker dlopen() or do_dlopen(), + // we MUST hook this method with invisible for ourself first. + if (sh_linker_need_to_hook_dlopen(self->target_addr)) { + SH_LOG_INFO("task: hook dlopen/do_dlopen internal. target-address %" PRIxPTR, self->target_addr); + if (0 != (r = sh_task_start_monitor(false))) goto end; + } + + // hook by target-address + r = sh_switch_hook(self->target_addr, self->new_addr, self->orig_addr, &backup_len, &dlinfo); + self->finished = true; + +end: + if (0 == r || SHADOWHOOK_ERRNO_PENDING == r) // "PENDING" is NOT an error + { + pthread_rwlock_wrlock(&sh_tasks_lock); + TAILQ_INSERT_TAIL(&sh_tasks, self, link); + if (!self->finished) __atomic_add_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST); + pthread_rwlock_unlock(&sh_tasks_lock); + } + + // record + sh_recorder_add_hook(r, is_hook_sym_addr, self->target_addr, real_lib_name, real_sym_name, self->new_addr, + backup_len, (uintptr_t)self, self->caller_addr); + + return r; +} + +int sh_task_unhook(sh_task_t *self, uintptr_t caller_addr) { + pthread_rwlock_wrlock(&sh_tasks_lock); + TAILQ_REMOVE(&sh_tasks, self, link); + if (!self->finished) __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1, __ATOMIC_SEQ_CST); + pthread_rwlock_unlock(&sh_tasks_lock); + + // check task status + int r; + if (self->error) { + r = SHADOWHOOK_ERRNO_UNHOOK_ON_ERROR; + goto end; + } + if (!self->finished) { + r = SHADOWHOOK_ERRNO_UNHOOK_ON_UNFINISHED; + goto end; + } + + // do unhook + r = sh_switch_unhook(self->target_addr, self->new_addr); + +end: + // record + sh_recorder_add_unhook(r, (uintptr_t)self, caller_addr); + return r; +} diff --git a/app/src/main/cpp/shadowhook/sh_task.h b/app/src/main/cpp/shadowhook/sh_task.h new file mode 100644 index 0000000000000000000000000000000000000000..e26aef9241f4df35314b2ae166f5da501fcac7cd --- /dev/null +++ b/app/src/main/cpp/shadowhook/sh_task.h @@ -0,0 +1,40 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#pragma once +#include <stdbool.h> +#include <stdint.h> + +#include "shadowhook.h" + +typedef struct sh_task sh_task_t; + +sh_task_t *sh_task_create_by_target_addr(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, + bool ignore_symbol_check, uintptr_t caller_addr); +sh_task_t *sh_task_create_by_sym_name(const char *lib_name, const char *sym_name, uintptr_t new_addr, + uintptr_t *orig_addr, shadowhook_hooked_t hooked, void *hooked_arg, + uintptr_t caller_addr); +void sh_task_destroy(sh_task_t *self); + +int sh_task_hook(sh_task_t *self); +int sh_task_unhook(sh_task_t *self, uintptr_t caller_addr); diff --git a/app/src/main/cpp/shadowhook/shadowhook.c b/app/src/main/cpp/shadowhook/shadowhook.c new file mode 100644 index 0000000000000000000000000000000000000000..254cda1b3c08d6dd80bb73664f26159435ed4fb0 --- /dev/null +++ b/app/src/main/cpp/shadowhook/shadowhook.c @@ -0,0 +1,328 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "shadowhook.h" + +#include <pthread.h> +#include <stdbool.h> +#include <stdlib.h> + +#include "bytesig.h" +#include "sh_enter.h" +#include "sh_errno.h" +#include "sh_exit.h" +#include "sh_hub.h" +#include "sh_linker.h" +#include "sh_log.h" +#include "sh_recorder.h" +#include "sh_safe.h" +#include "sh_sig.h" +#include "sh_switch.h" +#include "sh_task.h" +#include "sh_util.h" +#include "xdl.h" + +#define GOTO_ERR(errnum) \ + do { \ + r = errnum; \ + goto err; \ + } while (0) + +static int shadowhook_init_errno = SHADOWHOOK_ERRNO_UNINIT; +static shadowhook_mode_t shadowhook_mode = SHADOWHOOK_MODE_SHARED; + +const char *shadowhook_get_version(void) { + return "shadowhook version " SHADOWHOOK_VERSION; +} + +int shadowhook_init(shadowhook_mode_t mode, bool debuggable) { + bool do_init = false; + + if (__predict_true(SHADOWHOOK_ERRNO_UNINIT == shadowhook_init_errno)) { + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&lock); + if (__predict_true(SHADOWHOOK_ERRNO_UNINIT == shadowhook_init_errno)) { + do_init = true; + shadowhook_mode = mode; + sh_log_set_debuggable(debuggable); + +#define GOTO_END(errnum) \ + do { \ + shadowhook_init_errno = errnum; \ + goto end; \ + } while (0) + + if (__predict_false(0 != sh_errno_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_ERRNO); + if (__predict_false(0 != bytesig_init(SIGSEGV))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGSEGV); + if (__predict_false(0 != bytesig_init(SIGBUS))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGBUS); + if (__predict_false(0 != sh_enter_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_ENTER); + sh_exit_init(); + if (SHADOWHOOK_MODE_SHARED == shadowhook_mode) { + if (__predict_false(0 != sh_safe_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_SAFE); + if (__predict_false(0 != sh_hub_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_HUB); + } else { + if (__predict_false(0 != sh_linker_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_LINKER); + } + +#undef GOTO_END + + shadowhook_init_errno = SHADOWHOOK_ERRNO_OK; + } + end: + pthread_mutex_unlock(&lock); + } + + SH_LOG_ALWAYS_SHOW("%s: shadowhook init(mode: %s, debuggable: %s), return: %d, real-init: %s", + shadowhook_get_version(), SHADOWHOOK_MODE_SHARED == mode ? "SHARED" : "UNIQUE", + debuggable ? "true" : "false", shadowhook_init_errno, do_init ? "yes" : "no"); + SH_ERRNO_SET_RET_ERRNUM(shadowhook_init_errno); +} + +int shadowhook_get_init_errno(void) { + return shadowhook_init_errno; +} + +shadowhook_mode_t shadowhook_get_mode(void) { + return shadowhook_mode; +} + +bool shadowhook_get_debuggable(void) { + return sh_log_get_debuggable(); +} + +void shadowhook_set_debuggable(bool debuggable) { + sh_log_set_debuggable(debuggable); +} + +bool shadowhook_get_recordable(void) { + return sh_recorder_get_recordable(); +} + +void shadowhook_set_recordable(bool recordable) { + sh_recorder_set_recordable(recordable); +} + +int shadowhook_get_errno(void) { + return sh_errno_get(); +} + +const char *shadowhook_to_errmsg(int error_number) { + return sh_errno_to_errmsg(error_number); +} + +static void *shadowhook_hook_addr_impl(void *sym_addr, void *new_addr, void **orig_addr, + bool ignore_symbol_check, uintptr_t caller_addr) { + SH_LOG_INFO("shadowhook: hook_%s_addr(%p, %p) ...", ignore_symbol_check ? "func" : "sym", sym_addr, + new_addr); + sh_errno_reset(); + + int r; + if (NULL == sym_addr || NULL == new_addr) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG); + if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno); + + // create task + sh_task_t *task = + sh_task_create_by_target_addr((uintptr_t)sym_addr, (uintptr_t)new_addr, (uintptr_t *)orig_addr, + ignore_symbol_check, (uintptr_t)caller_addr); + if (NULL == task) GOTO_ERR(SHADOWHOOK_ERRNO_OOM); + + // do hook + r = sh_task_hook(task); + if (0 != r) { + sh_task_destroy(task); + GOTO_ERR(r); + } + + // OK + SH_LOG_INFO("shadowhook: hook_%s_addr(%p, %p) OK. return: %p", ignore_symbol_check ? "func" : "sym", + sym_addr, new_addr, (void *)task); + SH_ERRNO_SET_RET(SHADOWHOOK_ERRNO_OK, (void *)task); + +err: + SH_LOG_ERROR("shadowhook: hook_%s_addr(%p, %p) FAILED. %d - %s", ignore_symbol_check ? "func" : "sym", + sym_addr, new_addr, r, sh_errno_to_errmsg(r)); + SH_ERRNO_SET_RET_NULL(r); +} + +void *shadowhook_hook_func_addr(void *func_addr, void *new_addr, void **orig_addr) { + const void *caller_addr = __builtin_return_address(0); + return shadowhook_hook_addr_impl(func_addr, new_addr, orig_addr, true, (uintptr_t)caller_addr); +} + +void *shadowhook_hook_sym_addr(void *sym_addr, void *new_addr, void **orig_addr) { + const void *caller_addr = __builtin_return_address(0); + return shadowhook_hook_addr_impl(sym_addr, new_addr, orig_addr, false, (uintptr_t)caller_addr); +} + +static void *shadowhook_hook_sym_name_impl(const char *lib_name, const char *sym_name, void *new_addr, + void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg, + uintptr_t caller_addr) { + SH_LOG_INFO("shadowhook: hook_sym_name(%s, %s, %p) ...", lib_name, sym_name, new_addr); + sh_errno_reset(); + + int r; + if (NULL == lib_name || NULL == sym_name || NULL == new_addr) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG); + if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno); + + // create task + sh_task_t *task = + sh_task_create_by_sym_name(lib_name, sym_name, (uintptr_t)new_addr, (uintptr_t *)orig_addr, hooked, + hooked_arg, (uintptr_t)caller_addr); + if (NULL == task) GOTO_ERR(SHADOWHOOK_ERRNO_OOM); + + // do hook + r = sh_task_hook(task); + if (0 != r && SHADOWHOOK_ERRNO_PENDING != r) { + sh_task_destroy(task); + GOTO_ERR(r); + } + + // OK + SH_LOG_INFO("shadowhook: hook_sym_name(%s, %s, %p) OK. return: %p. %d - %s", lib_name, sym_name, new_addr, + (void *)task, r, sh_errno_to_errmsg(r)); + SH_ERRNO_SET_RET(r, (void *)task); + +err: + SH_LOG_ERROR("shadowhook: hook_sym_name(%s, %s, %p) FAILED. %d - %s", lib_name, sym_name, new_addr, r, + sh_errno_to_errmsg(r)); + SH_ERRNO_SET_RET_NULL(r); +} + +void *shadowhook_hook_sym_name(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr) { + const void *caller_addr = __builtin_return_address(0); + return shadowhook_hook_sym_name_impl(lib_name, sym_name, new_addr, orig_addr, NULL, NULL, + (uintptr_t)caller_addr); +} + +void *shadowhook_hook_sym_name_callback(const char *lib_name, const char *sym_name, void *new_addr, + void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg) { + const void *caller_addr = __builtin_return_address(0); + return shadowhook_hook_sym_name_impl(lib_name, sym_name, new_addr, orig_addr, hooked, hooked_arg, + (uintptr_t)caller_addr); +} + +int shadowhook_unhook(void *stub) { + const void *caller_addr = __builtin_return_address(0); + SH_LOG_INFO("shadowhook: unhook(%p) ...", stub); + sh_errno_reset(); + + int r; + if (NULL == stub) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG); + if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno); + + sh_task_t *task = (sh_task_t *)stub; + r = sh_task_unhook(task, (uintptr_t)caller_addr); + sh_task_destroy(task); + if (0 != r) GOTO_ERR(r); + + // OK + SH_LOG_INFO("shadowhook: unhook(%p) OK", stub); + SH_ERRNO_SET_RET_ERRNUM(SHADOWHOOK_ERRNO_OK); + +err: + SH_LOG_ERROR("shadowhook: unhook(%p) FAILED. %d - %s", stub, r, sh_errno_to_errmsg(r)); + SH_ERRNO_SET_RET_FAIL(r); +} + +char *shadowhook_get_records(uint32_t item_flags) { + return sh_recorder_get(item_flags); +} + +void shadowhook_dump_records(int fd, uint32_t item_flags) { + sh_recorder_dump(fd, item_flags); +} + +void *shadowhook_dlopen(const char *lib_name) { + void *handle = NULL; + if (sh_util_get_api_level() >= __ANDROID_API_L__) { + handle = xdl_open(lib_name, XDL_DEFAULT); + } else { + SH_SIG_TRY(SIGSEGV, SIGBUS) { + handle = xdl_open(lib_name, XDL_DEFAULT); + } + SH_SIG_CATCH() { + SH_LOG_WARN("shadowhook: dlopen crashed - %s", lib_name); + } + SH_SIG_EXIT + } + return handle; +} + +void shadowhook_dlclose(void *handle) { + xdl_close(handle); +} + +void *shadowhook_dlsym(void *handle, const char *sym_name) { + void *addr = shadowhook_dlsym_dynsym(handle, sym_name); + if (NULL == addr) addr = shadowhook_dlsym_symtab(handle, sym_name); + return addr; +} + +void *shadowhook_dlsym_dynsym(void *handle, const char *sym_name) { + void *addr = NULL; + SH_SIG_TRY(SIGSEGV, SIGBUS) { + addr = xdl_sym(handle, sym_name, NULL); + } + SH_SIG_CATCH() { + SH_LOG_WARN("shadowhook: dlsym_dynsym crashed - %p, %s", handle, sym_name); + } + SH_SIG_EXIT + return addr; +} + +void *shadowhook_dlsym_symtab(void *handle, const char *sym_name) { + void *addr = NULL; + SH_SIG_TRY(SIGSEGV, SIGBUS) { + addr = xdl_dsym(handle, sym_name, NULL); + } + SH_SIG_CATCH() { + SH_LOG_WARN("shadowhook: dlsym_symtab crashed - %p, %s", handle, sym_name); + } + SH_SIG_EXIT + return addr; +} + +void *shadowhook_get_prev_func(void *func) { + if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort(); + return sh_hub_get_prev_func(func); +} + +void shadowhook_pop_stack(void *return_address) { + if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort(); + sh_hub_pop_stack(return_address); +} + +void shadowhook_allow_reentrant(void *return_address) { + if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort(); + sh_hub_allow_reentrant(return_address); +} + +void shadowhook_disallow_reentrant(void *return_address) { + if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort(); + sh_hub_disallow_reentrant(return_address); +} + +void *shadowhook_get_return_address(void) { + if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort(); + return sh_hub_get_return_address(); +} diff --git a/app/src/main/cpp/shadowhook/third_party/bsd/queue.h b/app/src/main/cpp/shadowhook/third_party/bsd/queue.h new file mode 100644 index 0000000000000000000000000000000000000000..f550f6c26561849097ef3597e6c0171ad7b6c365 --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/bsd/queue.h @@ -0,0 +1,551 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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 QUEUE_H +#define QUEUE_H + +/* #include <sys/cdefs.h> */ +#define QUEUE_CONTAINER_OF(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field))) + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type, qual) \ + struct name { \ + struct type *qual slh_first; /* first element */ \ + } + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type, qual) \ + struct { \ + struct type *qual sle_next; /* next element */ \ + } + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ + } while (0) + +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ + } while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ + } while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ + } while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ + } while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + } while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ + } while (0) + +/* + * List declarations. + */ +#define LIST_HEAD(name, type, qual) \ + struct name { \ + struct type *qual lh_first; /* first element */ \ + } + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type, qual) \ + struct { \ + struct type *qual le_next; /* next element */ \ + struct type *qual *le_prev; /* address of previous next element */ \ + } + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ + } while (0) + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + QUEUE_CONTAINER_OF((elm)->field.le_prev, struct type, field.le_next)) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ + } while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ + } while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ + } while (0) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + } while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ + } while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type, qual) \ + struct name { \ + struct type *qual stqh_first;/* first element */ \ + struct type *qual *stqh_last;/* addr of last next element */ \ + } + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type, qual) \ + struct { \ + struct type *qual stqe_next; /* next element */ \ + } + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + QUEUE_CONTAINER_OF((head)->stqh_last, struct type, field.stqe_next)) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ + } while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ + } while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ + } while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + } while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ + } while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type, qual) \ + struct name { \ + struct type *qual tqh_first; /* first element */ \ + struct type *qual *tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type, qual) \ + struct { \ + struct type *qual tqe_next; /* next element */ \ + struct type *qual *tqe_prev; /* address of previous next element */ \ + } + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + } while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + } while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + } while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ + } while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ + } while (0) + +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/bsd/tree.h b/app/src/main/cpp/shadowhook/third_party/bsd/tree.h new file mode 100644 index 0000000000000000000000000000000000000000..e6db6a908df40a9e8874e99342b87e75108a8cad --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/bsd/tree.h @@ -0,0 +1,759 @@ +/*- + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided 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 TREE_H +#define TREE_H + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/lss/LICENSE b/app/src/main/cpp/shadowhook/third_party/lss/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..58ab3fba9d4c3318ea545f451bb3ecbfdd5d8f6f --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/lss/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2005-2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + 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. + Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 COPYRIGHT +OWNER OR CONTRIBUTORS 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. diff --git a/app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h b/app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h new file mode 100644 index 0000000000000000000000000000000000000000..d3791cd4e9c802c157dd8ddfb6d0d9803a9b3d22 --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h @@ -0,0 +1,4867 @@ +/* Copyright (c) 2005-2011, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + * + * --- + * Author: Markus Gutschke + */ + +/* This file includes Linux-specific support functions common to the + * coredumper and the thread lister; primarily, this is a collection + * of direct system calls, and a couple of symbols missing from + * standard header files. + * There are a few options that the including file can set to control + * the behavior of this file: + * + * SYS_CPLUSPLUS: + * The entire header file will normally be wrapped in 'extern "C" { }", + * making it suitable for compilation as both C and C++ source. If you + * do not want to do this, you can set the SYS_CPLUSPLUS macro to inhibit + * the wrapping. N.B. doing so will suppress inclusion of all prerequisite + * system header files, too. It is the caller's responsibility to provide + * the necessary definitions. + * + * SYS_ERRNO: + * All system calls will update "errno" unless overriden by setting the + * SYS_ERRNO macro prior to including this file. SYS_ERRNO should be + * an l-value. + * + * SYS_INLINE: + * New symbols will be defined "static inline", unless overridden by + * the SYS_INLINE macro. + * + * SYS_LINUX_SYSCALL_SUPPORT_H + * This macro is used to avoid multiple inclusions of this header file. + * If you need to include this file more than once, make sure to + * unset SYS_LINUX_SYSCALL_SUPPORT_H before each inclusion. + * + * SYS_PREFIX: + * New system calls will have a prefix of "sys_" unless overridden by + * the SYS_PREFIX macro. Valid values for this macro are [0..9] which + * results in prefixes "sys[0..9]_". It is also possible to set this + * macro to -1, which avoids all prefixes. + * + * SYS_SYSCALL_ENTRYPOINT: + * Some applications (such as sandboxes that filter system calls), need + * to be able to run custom-code each time a system call is made. If this + * macro is defined, it expands to the name of a "common" symbol. If + * this symbol is assigned a non-NULL pointer value, it is used as the + * address of the system call entrypoint. + * A pointer to this symbol can be obtained by calling + * get_syscall_entrypoint() + * + * This file defines a few internal symbols that all start with "LSS_". + * Do not access these symbols from outside this file. They are not part + * of the supported API. + */ +#ifndef SYS_LINUX_SYSCALL_SUPPORT_H +#define SYS_LINUX_SYSCALL_SUPPORT_H + +/* We currently only support x86-32, x86-64, ARM, MIPS, PPC, s390 and s390x + * on Linux. + * Porting to other related platforms should not be difficult. + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__) || \ + defined(__aarch64__) || defined(__s390__) || defined(__e2k__)) \ + && (defined(__linux) || defined(__ANDROID__)) + +#ifndef SYS_CPLUSPLUS +#ifdef __cplusplus +/* Some system header files in older versions of gcc neglect to properly + * handle being included from C++. As it appears to be harmless to have + * multiple nested 'extern "C"' blocks, just add another one here. + */ +extern "C" { +#endif + +#include <errno.h> +#include <fcntl.h> +#include <sched.h> +#include <signal.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <sys/ptrace.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <unistd.h> +#include <linux/unistd.h> +#include <endian.h> + +#ifdef __mips__ +/* Include definitions of the ABI currently in use. */ +#ifdef __ANDROID__ +/* Android doesn't have sgidefs.h, but does have asm/sgidefs.h, + * which has the definitions we need. + */ +#include <asm/sgidefs.h> +#else +#include <sgidefs.h> +#endif +#endif +#endif + +/* Some libcs, for example Android NDK and musl, #define these + * macros as aliases to their non-64 counterparts. To avoid naming + * conflict, remove them. + * + * These are restored by the corresponding #pragma pop_macro near + * the end of this file. + */ +#pragma push_macro("stat64") +#pragma push_macro("fstat64") +#pragma push_macro("lstat64") +#pragma push_macro("pread64") +#pragma push_macro("pwrite64") +#pragma push_macro("getdents64") +#undef stat64 +#undef fstat64 +#undef lstat64 +#undef pread64 +#undef pwrite64 +#undef getdents64 + +#if defined(__ANDROID__) && defined(__x86_64__) +// A number of x86_64 syscalls are blocked by seccomp on recent Android; +// undefine them so that modern alternatives will be used instead where +// possible. +// The alternative syscalls have been sanity checked against linux-3.4+; +// older versions might not work. +# undef __NR_getdents +# undef __NR_dup2 +# undef __NR_fork +# undef __NR_getpgrp +# undef __NR_open +# undef __NR_poll +# undef __NR_readlink +# undef __NR_stat +# undef __NR_unlink +# undef __NR_pipe +#endif + +#if defined(__ANDROID__) +// waitpid is blocked by seccomp on all architectures on recent Android. +# undef __NR_waitpid +#endif + +/* As glibc often provides subtly incompatible data structures (and implicit + * wrapper functions that convert them), we provide our own kernel data + * structures for use by the system calls. + * These structures have been developed by using Linux 2.6.23 headers for + * reference. Note though, we do not care about exact API compatibility + * with the kernel, and in fact the kernel often does not have a single + * API that works across architectures. Instead, we try to mimic the glibc + * API where reasonable, and only guarantee ABI compatibility with the + * kernel headers. + * Most notably, here are a few changes that were made to the structures + * defined by kernel headers: + * + * - we only define structures, but not symbolic names for kernel data + * types. For the latter, we directly use the native C datatype + * (i.e. "unsigned" instead of "mode_t"). + * - in a few cases, it is possible to define identical structures for + * both 32bit (e.g. i386) and 64bit (e.g. x86-64) platforms by + * standardizing on the 64bit version of the data types. In particular, + * this means that we use "unsigned" where the 32bit headers say + * "unsigned long". + * - overall, we try to minimize the number of cases where we need to + * conditionally define different structures. + * - the "struct kernel_sigaction" class of structures have been + * modified to more closely mimic glibc's API by introducing an + * anonymous union for the function pointer. + * - a small number of field names had to have an underscore appended to + * them, because glibc defines a global macro by the same name. + */ + +/* include/linux/dirent.h */ +struct kernel_dirent64 { + unsigned long long d_ino; + long long d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +/* include/linux/dirent.h */ +#if !defined(__NR_getdents) +// when getdents is not available, getdents64 is used for both. +#define kernel_dirent kernel_dirent64 +#else +struct kernel_dirent { + long d_ino; + long d_off; + unsigned short d_reclen; + char d_name[256]; +}; +#endif + +/* include/linux/uio.h */ +struct kernel_iovec { + void *iov_base; + unsigned long iov_len; +}; + +/* include/linux/socket.h */ +struct kernel_msghdr { + void *msg_name; + int msg_namelen; + struct kernel_iovec*msg_iov; + unsigned long msg_iovlen; + void *msg_control; + unsigned long msg_controllen; + unsigned msg_flags; +}; + +/* include/asm-generic/poll.h */ +struct kernel_pollfd { + int fd; + short events; + short revents; +}; + +/* include/linux/resource.h */ +struct kernel_rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; + +/* include/linux/time.h */ +struct kernel_timespec { + long tv_sec; + long tv_nsec; +}; + +/* include/linux/time.h */ +struct kernel_timeval { + long tv_sec; + long tv_usec; +}; + +/* include/linux/resource.h */ +struct kernel_rusage { + struct kernel_timeval ru_utime; + struct kernel_timeval ru_stime; + long ru_maxrss; + long ru_ixrss; + long ru_idrss; + long ru_isrss; + long ru_minflt; + long ru_majflt; + long ru_nswap; + long ru_inblock; + long ru_oublock; + long ru_msgsnd; + long ru_msgrcv; + long ru_nsignals; + long ru_nvcsw; + long ru_nivcsw; +}; + +#if defined(__i386__) || defined(__ARM_EABI__) || defined(__ARM_ARCH_3__) \ + || defined(__PPC__) || (defined(__s390__) && !defined(__s390x__)) \ + || defined(__e2k__) + +/* include/asm-{arm,i386,mips,ppc}/signal.h */ +struct kernel_old_sigaction { + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, siginfo_t *, void *); + }; + unsigned long sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +} __attribute__((packed,aligned(4))); +#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define kernel_old_sigaction kernel_sigaction +#elif defined(__aarch64__) + // No kernel_old_sigaction defined for arm64. +#endif + +/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the + * exactly match the size of the signal set, even though the API was + * intended to be extensible. We define our own KERNEL_NSIG to deal with + * this. + * Please note that glibc provides signals [1.._NSIG-1], whereas the + * kernel (and this header) provides the range [1..KERNEL_NSIG]. The + * actual number of signals is obviously the same, but the constants + * differ by one. + */ +#ifdef __mips__ +#define KERNEL_NSIG 128 +#else +#define KERNEL_NSIG 64 +#endif + +/* include/asm-{arm,aarch64,i386,mips,x86_64}/signal.h */ +struct kernel_sigset_t { + unsigned long sig[(KERNEL_NSIG + 8*sizeof(unsigned long) - 1)/ + (8*sizeof(unsigned long))]; +}; + +/* include/asm-{arm,i386,mips,x86_64,ppc}/signal.h */ +struct kernel_sigaction { +#ifdef __mips__ + unsigned long sa_flags; + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, siginfo_t *, void *); + }; + struct kernel_sigset_t sa_mask; +#else + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, siginfo_t *, void *); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + struct kernel_sigset_t sa_mask; +#endif +}; + +/* include/linux/socket.h */ +struct kernel_sockaddr { + unsigned short sa_family; + char sa_data[14]; +}; + +/* include/asm-{arm,aarch64,i386,mips,ppc,s390}/stat.h */ +#ifdef __mips__ +#if _MIPS_SIM == _MIPS_SIM_ABI64 +struct kernel_stat { +#else +struct kernel_stat64 { +#endif + unsigned st_dev; + unsigned __pad0[3]; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + unsigned __pad1[3]; + long long st_size; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned st_blksize; + unsigned __pad2; + unsigned long long st_blocks; +}; +#elif defined __PPC__ +struct kernel_stat64 { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned short int __pad2; + long long st_size; + long st_blksize; + long long st_blocks; + long st_atime_; + unsigned long st_atime_nsec_; + long st_mtime_; + unsigned long st_mtime_nsec_; + long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif defined(__e2k__) +struct kernel_stat64 { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + unsigned int st_uid; + unsigned int st_gid; + unsigned long long st_rdev; + long long st_size; + int st_blksize; + int __pad2; + unsigned long long st_blocks; + int st_atime_; + unsigned int st_atime_nsec_; + int st_mtime_; + unsigned int st_mtime_nsec_; + int st_ctime_; + unsigned int st_ctime_nsec_; + unsigned int __unused4; + unsigned int __unused5; +}; +#else +struct kernel_stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; + unsigned __st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned char __pad3[4]; + long long st_size; + unsigned st_blksize; + unsigned long long st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned long long st_ino; +}; +#endif + +/* include/asm-{arm,aarch64,i386,mips,x86_64,ppc,s390}/stat.h */ +#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +struct kernel_stat { + /* The kernel headers suggest that st_dev and st_rdev should be 32bit + * quantities encoding 12bit major and 20bit minor numbers in an interleaved + * format. In reality, we do not see useful data in the top bits. So, + * we'll leave the padding in here, until we find a better solution. + */ + unsigned short st_dev; + short pad1; + unsigned st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + short pad2; + unsigned st_size; + unsigned st_blksize; + unsigned st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned __unused4; + unsigned __unused5; +}; +#elif defined(__x86_64__) +struct kernel_stat { + uint64_t st_dev; + uint64_t st_ino; + uint64_t st_nlink; + unsigned st_mode; + unsigned st_uid; + unsigned st_gid; + unsigned __pad0; + uint64_t st_rdev; + int64_t st_size; + int64_t st_blksize; + int64_t st_blocks; + uint64_t st_atime_; + uint64_t st_atime_nsec_; + uint64_t st_mtime_; + uint64_t st_mtime_nsec_; + uint64_t st_ctime_; + uint64_t st_ctime_nsec_; + int64_t __unused4[3]; +}; +#elif defined(__PPC__) +struct kernel_stat { + unsigned st_dev; + unsigned long st_ino; // ino_t + unsigned long st_mode; // mode_t + unsigned short st_nlink; // nlink_t + unsigned st_uid; // uid_t + unsigned st_gid; // gid_t + unsigned st_rdev; + long st_size; // off_t + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) +struct kernel_stat { + unsigned st_dev; + int st_pad1[3]; + unsigned st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + int st_pad2[2]; + long st_size; + int st_pad3; + long st_atime_; + long st_atime_nsec_; + long st_mtime_; + long st_mtime_nsec_; + long st_ctime_; + long st_ctime_nsec_; + int st_blksize; + int st_blocks; + int st_pad4[14]; +}; +#elif defined(__aarch64__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + unsigned int st_uid; + unsigned int st_gid; + unsigned long st_rdev; + unsigned long __pad1; + long st_size; + int st_blksize; + int __pad2; + long st_blocks; + long st_atime_; + unsigned long st_atime_nsec_; + long st_mtime_; + unsigned long st_mtime_nsec_; + long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned int __unused4; + unsigned int __unused5; +}; +#elif defined(__s390x__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad1; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long st_blksize; + long st_blocks; + unsigned long __unused[3]; +}; +#elif defined(__s390__) +struct kernel_stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif defined(__e2k__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned int st_mode; + unsigned long st_nlink; + unsigned int st_uid; + unsigned int st_gid; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; +}; +#endif + +/* include/asm-{arm,aarch64,i386,mips,x86_64,ppc,s390}/statfs.h */ +#ifdef __mips__ +#if _MIPS_SIM != _MIPS_SIM_ABI64 +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_frsize; + unsigned long __pad; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_files; + unsigned long long f_ffree; + unsigned long long f_bavail; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_spare[6]; +}; +#endif +#elif defined(__s390__) +/* See also arch/s390/include/asm/compat.h */ +struct kernel_statfs64 { + unsigned int f_type; + unsigned int f_bsize; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_bavail; + unsigned long long f_files; + unsigned long long f_ffree; + struct { int val[2]; } f_fsid; + unsigned int f_namelen; + unsigned int f_frsize; + unsigned int f_flags; + unsigned int f_spare[4]; +}; +#elif !defined(__x86_64__) +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_bavail; + unsigned long long f_files; + unsigned long long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc,generic,s390}/statfs.h */ +#ifdef __mips__ +struct kernel_statfs { + long f_type; + long f_bsize; + long f_frsize; + long f_blocks; + long f_bfree; + long f_files; + long f_ffree; + long f_bavail; + struct { int val[2]; } f_fsid; + long f_namelen; + long f_spare[6]; +}; +#elif defined(__x86_64__) +struct kernel_statfs { + /* x86_64 actually defines all these fields as signed, whereas all other */ + /* platforms define them as unsigned. Leaving them at unsigned should not */ + /* cause any problems. Make sure these are 64-bit even on x32. */ + uint64_t f_type; + uint64_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + struct { int val[2]; } f_fsid; + uint64_t f_namelen; + uint64_t f_frsize; + uint64_t f_spare[5]; +}; +#elif defined(__s390__) +struct kernel_statfs { + unsigned int f_type; + unsigned int f_bsize; + unsigned long f_blocks; + unsigned long f_bfree; + unsigned long f_bavail; + unsigned long f_files; + unsigned long f_ffree; + struct { int val[2]; } f_fsid; + unsigned int f_namelen; + unsigned int f_frsize; + unsigned int f_flags; + unsigned int f_spare[4]; +}; +#else +struct kernel_statfs { + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_blocks; + unsigned long f_bfree; + unsigned long f_bavail; + unsigned long f_files; + unsigned long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + + +/* Definitions missing from the standard header files */ +#ifndef O_DIRECTORY +#if defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || defined(__aarch64__) +#define O_DIRECTORY 0040000 +#else +#define O_DIRECTORY 0200000 +#endif +#endif +#ifndef NT_PRXFPREG +#define NT_PRXFPREG 0x46e62b7f +#endif +#ifndef PTRACE_GETFPXREGS +#define PTRACE_GETFPXREGS ((enum __ptrace_request)18) +#endif +#ifndef PR_GET_DUMPABLE +#define PR_GET_DUMPABLE 3 +#endif +#ifndef PR_SET_DUMPABLE +#define PR_SET_DUMPABLE 4 +#endif +#ifndef PR_GET_SECCOMP +#define PR_GET_SECCOMP 21 +#endif +#ifndef PR_SET_SECCOMP +#define PR_SET_SECCOMP 22 +#endif +#ifndef AT_FDCWD +#define AT_FDCWD (-100) +#endif +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif +#ifndef AT_REMOVEDIR +#define AT_REMOVEDIR 0x200 +#endif +#ifndef MREMAP_FIXED +#define MREMAP_FIXED 2 +#endif +#ifndef SA_RESTORER +#define SA_RESTORER 0x04000000 +#endif +#ifndef CPUCLOCK_PROF +#define CPUCLOCK_PROF 0 +#endif +#ifndef CPUCLOCK_VIRT +#define CPUCLOCK_VIRT 1 +#endif +#ifndef CPUCLOCK_SCHED +#define CPUCLOCK_SCHED 2 +#endif +#ifndef CPUCLOCK_PERTHREAD_MASK +#define CPUCLOCK_PERTHREAD_MASK 4 +#endif +#ifndef MAKE_PROCESS_CPUCLOCK +#define MAKE_PROCESS_CPUCLOCK(pid, clock) \ + ((int)(~(unsigned)(pid) << 3) | (int)(clock)) +#endif +#ifndef MAKE_THREAD_CPUCLOCK +#define MAKE_THREAD_CPUCLOCK(tid, clock) \ + ((int)(~(unsigned)(tid) << 3) | \ + (int)((clock) | CPUCLOCK_PERTHREAD_MASK)) +#endif + +#ifndef FUTEX_WAIT +#define FUTEX_WAIT 0 +#endif +#ifndef FUTEX_WAKE +#define FUTEX_WAKE 1 +#endif +#ifndef FUTEX_FD +#define FUTEX_FD 2 +#endif +#ifndef FUTEX_REQUEUE +#define FUTEX_REQUEUE 3 +#endif +#ifndef FUTEX_CMP_REQUEUE +#define FUTEX_CMP_REQUEUE 4 +#endif +#ifndef FUTEX_WAKE_OP +#define FUTEX_WAKE_OP 5 +#endif +#ifndef FUTEX_LOCK_PI +#define FUTEX_LOCK_PI 6 +#endif +#ifndef FUTEX_UNLOCK_PI +#define FUTEX_UNLOCK_PI 7 +#endif +#ifndef FUTEX_TRYLOCK_PI +#define FUTEX_TRYLOCK_PI 8 +#endif +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif +#ifndef FUTEX_CMD_MASK +#define FUTEX_CMD_MASK ~FUTEX_PRIVATE_FLAG +#endif +#ifndef FUTEX_WAIT_PRIVATE +#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_WAKE_PRIVATE +#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_REQUEUE_PRIVATE +#define FUTEX_REQUEUE_PRIVATE (FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_CMP_REQUEUE_PRIVATE +#define FUTEX_CMP_REQUEUE_PRIVATE (FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_WAKE_OP_PRIVATE +#define FUTEX_WAKE_OP_PRIVATE (FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_LOCK_PI_PRIVATE +#define FUTEX_LOCK_PI_PRIVATE (FUTEX_LOCK_PI | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_UNLOCK_PI_PRIVATE +#define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_TRYLOCK_PI_PRIVATE +#define FUTEX_TRYLOCK_PI_PRIVATE (FUTEX_TRYLOCK_PI | FUTEX_PRIVATE_FLAG) +#endif + + +#if defined(__x86_64__) +#ifndef ARCH_SET_GS +#define ARCH_SET_GS 0x1001 +#endif +#ifndef ARCH_GET_GS +#define ARCH_GET_GS 0x1004 +#endif +#endif + +#if defined(__i386__) +#ifndef __NR_quotactl +#define __NR_quotactl 131 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigsuspend 179 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 180 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 181 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 220 +#endif +#ifndef __NR_gettid +#define __NR_gettid 224 +#endif +#ifndef __NR_readahead +#define __NR_readahead 225 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 226 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 227 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 229 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 230 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 232 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 233 +#endif +#ifndef __NR_tkill +#define __NR_tkill 238 +#endif +#ifndef __NR_futex +#define __NR_futex 240 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 258 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 265 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 266 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 268 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 269 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 272 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 289 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 290 +#endif +#ifndef __NR_openat +#define __NR_openat 295 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 300 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 301 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 317 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 318 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 324 +#endif +#ifndef __NR_getrandom +#define __NR_getrandom 355 +#endif +/* End of i386 definitions */ +#elif defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_SYSCALL_BASE + 164) +#define __NR_getresuid (__NR_SYSCALL_BASE + 165) +#define __NR_setresgid (__NR_SYSCALL_BASE + 170) +#define __NR_getresgid (__NR_SYSCALL_BASE + 171) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn (__NR_SYSCALL_BASE + 173) +#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174) +#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175) +#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176) +#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_SYSCALL_BASE + 180) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181) +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_SYSCALL_BASE + 195) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_SYSCALL_BASE + 197) +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208) +#define __NR_getresuid32 (__NR_SYSCALL_BASE + 209) +#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210) +#define __NR_getresgid32 (__NR_SYSCALL_BASE + 211) +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215) +#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_SYSCALL_BASE + 217) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_SYSCALL_BASE + 224) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_SYSCALL_BASE + 225) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_SYSCALL_BASE + 226) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_SYSCALL_BASE + 229) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_SYSCALL_BASE + 232) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_SYSCALL_BASE + 233) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_SYSCALL_BASE + 238) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_SYSCALL_BASE + 240) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241) +#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_SYSCALL_BASE + 263) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_SYSCALL_BASE + 264) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_SYSCALL_BASE + 266) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_SYSCALL_BASE + 314) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_SYSCALL_BASE + 315) +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 (__NR_SYSCALL_BASE + 327) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_SYSCALL_BASE + 344) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_SYSCALL_BASE + 345) +#endif +#ifndef __NR_getrandom +#define __NR_getrandom (__NR_SYSCALL_BASE + 384) +#endif +/* End of ARM 3/EABI definitions */ +#elif defined(__aarch64__) +#ifndef __NR_setxattr +#define __NR_setxattr 5 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 6 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 8 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 9 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 11 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 12 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 30 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 31 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 35 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 47 +#endif +#ifndef __NR_openat +#define __NR_openat 56 +#endif +#ifndef __NR_quotactl +#define __NR_quotactl 60 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 61 +#endif +#ifndef __NR_getdents +// when getdents is not available, getdents64 is used for both. +#define __NR_getdents __NR_getdents64 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 67 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 68 +#endif +#ifndef __NR_ppoll +#define __NR_ppoll 73 +#endif +#ifndef __NR_readlinkat +#define __NR_readlinkat 78 +#endif +#ifndef __NR_newfstatat +#define __NR_newfstatat 79 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 96 +#endif +#ifndef __NR_futex +#define __NR_futex 98 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 113 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 114 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 122 +#define __NR_sched_getaffinity 123 +#endif +#ifndef __NR_tkill +#define __NR_tkill 130 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 147 +#define __NR_getresuid 148 +#define __NR_setresgid 149 +#define __NR_getresgid 150 +#endif +#ifndef __NR_gettid +#define __NR_gettid 178 +#endif +#ifndef __NR_readahead +#define __NR_readahead 213 +#endif +#ifndef __NR_fadvise64 +#define __NR_fadvise64 223 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 239 +#endif +#ifndef __NR_getrandom +#define __NR_getrandom 278 +#endif +/* End of aarch64 definitions */ +#elif defined(__x86_64__) +#ifndef __NR_pread64 +#define __NR_pread64 17 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 18 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 117 +#define __NR_getresuid 118 +#define __NR_setresgid 119 +#define __NR_getresgid 120 +#endif +#ifndef __NR_quotactl +#define __NR_quotactl 179 +#endif +#ifndef __NR_gettid +#define __NR_gettid 186 +#endif +#ifndef __NR_readahead +#define __NR_readahead 187 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 188 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 189 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 191 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 192 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 194 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 195 +#endif +#ifndef __NR_tkill +#define __NR_tkill 200 +#endif +#ifndef __NR_futex +#define __NR_futex 202 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 203 +#define __NR_sched_getaffinity 204 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 217 +#endif +#ifndef __NR_getdents +// when getdents is not available, getdents64 is used for both. +#define __NR_getdents __NR_getdents64 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 218 +#endif +#ifndef __NR_fadvise64 +#define __NR_fadvise64 221 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 228 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 229 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 251 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 252 +#endif +#ifndef __NR_openat +#define __NR_openat 257 +#endif +#ifndef __NR_newfstatat +#define __NR_newfstatat 262 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 263 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 279 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 285 +#endif +#ifndef __NR_getrandom +#define __NR_getrandom 318 +#endif +/* End of x86-64 definitions */ +#elif defined(__mips__) +#if _MIPS_SIM == _MIPS_SIM_ABI32 +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 185) +#define __NR_getresuid (__NR_Linux + 186) +#define __NR_setresgid (__NR_Linux + 190) +#define __NR_getresgid (__NR_Linux + 191) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn (__NR_Linux + 193) +#define __NR_rt_sigaction (__NR_Linux + 194) +#define __NR_rt_sigprocmask (__NR_Linux + 195) +#define __NR_rt_sigpending (__NR_Linux + 196) +#define __NR_rt_sigsuspend (__NR_Linux + 199) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_Linux + 200) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_Linux + 201) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_Linux + 213) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_Linux + 215) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_Linux + 219) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 222) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 223) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 224) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 225) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 227) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 228) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 230) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 231) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 236) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 238) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 239) +#define __NR_sched_getaffinity (__NR_Linux + 240) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 252) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 255) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 256) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 263) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 264) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 288) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 293) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 294) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 308) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 312) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 314) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 315) +#endif +#ifndef __NR_getrandom +#define __NR_getrandom (__NR_Linux + 353) +#endif +/* End of MIPS (old 32bit API) definitions */ +#elif _MIPS_SIM == _MIPS_SIM_ABI64 +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_Linux + 16) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_Linux + 17) +#endif +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_getresuid (__NR_Linux + 116) +#define __NR_setresgid (__NR_Linux + 117) +#define __NR_getresgid (__NR_Linux + 118) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 186) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 187) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 192) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 212) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 222) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 223) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 247) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 252) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 253) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 267) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 271) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 273) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 274) +#endif +#ifndef __NR_getrandom +#define __NR_getrandom (__NR_Linux + 313) +#endif +/* End of MIPS (64bit API) definitions */ +#else +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_getresuid (__NR_Linux + 116) +#define __NR_setresgid (__NR_Linux + 117) +#define __NR_getresgid (__NR_Linux + 118) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 186) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 187) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 192) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 213) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 217) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 218) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 226) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 227) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 251) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 256) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 257) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 271) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 275) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 277) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 278) +#endif +/* End of MIPS (new 32bit API) definitions */ +#endif +/* End of MIPS definitions */ +#elif defined(__PPC__) +#ifndef __NR_setfsuid +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_setresgid 169 +#define __NR_getresgid 170 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn 172 +#define __NR_rt_sigaction 173 +#define __NR_rt_sigprocmask 174 +#define __NR_rt_sigpending 175 +#define __NR_rt_sigsuspend 178 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 179 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 180 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 190 +#endif +#ifndef __NR_readahead +#define __NR_readahead 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 202 +#endif +#ifndef __NR_gettid +#define __NR_gettid 207 +#endif +#ifndef __NR_tkill +#define __NR_tkill 208 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 209 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 210 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 212 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 213 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 215 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 216 +#endif +#ifndef __NR_futex +#define __NR_futex 221 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 222 +#define __NR_sched_getaffinity 223 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 232 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 246 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 247 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 252 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 253 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 254 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 273 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 274 +#endif +#ifndef __NR_openat +#define __NR_openat 286 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 291 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 292 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 301 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 302 +#endif +/* End of powerpc defininitions */ +#elif defined(__s390__) +#ifndef __NR_quotactl +#define __NR_quotactl 131 +#endif +#ifndef __NR_rt_sigreturn +#define __NR_rt_sigreturn 173 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction 174 +#endif +#ifndef __NR_rt_sigprocmask +#define __NR_rt_sigprocmask 175 +#endif +#ifndef __NR_rt_sigpending +#define __NR_rt_sigpending 176 +#endif +#ifndef __NR_rt_sigsuspend +#define __NR_rt_sigsuspend 179 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 180 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 181 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 220 +#endif +#ifndef __NR_readahead +#define __NR_readahead 222 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 224 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 225 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 227 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 228 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 230 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 231 +#endif +#ifndef __NR_gettid +#define __NR_gettid 236 +#endif +#ifndef __NR_tkill +#define __NR_tkill 237 +#endif +#ifndef __NR_futex +#define __NR_futex 238 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 239 +#endif +#ifndef __NR_sched_getaffinity +#define __NR_sched_getaffinity 240 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 252 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 260 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 261 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 265 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 266 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 282 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 283 +#endif +#ifndef __NR_openat +#define __NR_openat 288 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 294 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 310 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 311 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 314 +#endif +/* Some syscalls are named/numbered differently between s390 and s390x. */ +#ifdef __s390x__ +# ifndef __NR_getrlimit +# define __NR_getrlimit 191 +# endif +# ifndef __NR_setresuid +# define __NR_setresuid 208 +# endif +# ifndef __NR_getresuid +# define __NR_getresuid 209 +# endif +# ifndef __NR_setresgid +# define __NR_setresgid 210 +# endif +# ifndef __NR_getresgid +# define __NR_getresgid 211 +# endif +# ifndef __NR_setfsuid +# define __NR_setfsuid 215 +# endif +# ifndef __NR_setfsgid +# define __NR_setfsgid 216 +# endif +# ifndef __NR_fadvise64 +# define __NR_fadvise64 253 +# endif +# ifndef __NR_newfstatat +# define __NR_newfstatat 293 +# endif +#else /* __s390x__ */ +# ifndef __NR_getrlimit +# define __NR_getrlimit 76 +# endif +# ifndef __NR_setfsuid +# define __NR_setfsuid 138 +# endif +# ifndef __NR_setfsgid +# define __NR_setfsgid 139 +# endif +# ifndef __NR_setresuid +# define __NR_setresuid 164 +# endif +# ifndef __NR_getresuid +# define __NR_getresuid 165 +# endif +# ifndef __NR_setresgid +# define __NR_setresgid 170 +# endif +# ifndef __NR_getresgid +# define __NR_getresgid 171 +# endif +# ifndef __NR_ugetrlimit +# define __NR_ugetrlimit 191 +# endif +# ifndef __NR_mmap2 +# define __NR_mmap2 192 +# endif +# ifndef __NR_setresuid32 +# define __NR_setresuid32 208 +# endif +# ifndef __NR_getresuid32 +# define __NR_getresuid32 209 +# endif +# ifndef __NR_setresgid32 +# define __NR_setresgid32 210 +# endif +# ifndef __NR_getresgid32 +# define __NR_getresgid32 211 +# endif +# ifndef __NR_setfsuid32 +# define __NR_setfsuid32 215 +# endif +# ifndef __NR_setfsgid32 +# define __NR_setfsgid32 216 +# endif +# ifndef __NR_fadvise64_64 +# define __NR_fadvise64_64 264 +# endif +# ifndef __NR_fstatat64 +# define __NR_fstatat64 293 +# endif +#endif /* __s390__ */ +/* End of s390/s390x definitions */ +#endif + + +/* After forking, we must make sure to only call system calls. */ +#if defined(__BOUNDED_POINTERS__) + #error "Need to port invocations of syscalls for bounded ptrs" +#else + /* The core dumper and the thread lister get executed after threads + * have been suspended. As a consequence, we cannot call any functions + * that acquire locks. Unfortunately, libc wraps most system calls + * (e.g. in order to implement pthread_atfork, and to make calls + * cancellable), which means we cannot call these functions. Instead, + * we have to call syscall() directly. + */ + #undef LSS_ERRNO + #ifdef SYS_ERRNO + /* Allow the including file to override the location of errno. This can + * be useful when using clone() with the CLONE_VM option. + */ + #define LSS_ERRNO SYS_ERRNO + #else + #define LSS_ERRNO errno + #endif + + #undef LSS_INLINE + #ifdef SYS_INLINE + #define LSS_INLINE SYS_INLINE + #else + #define LSS_INLINE static inline + #endif + + /* Allow the including file to override the prefix used for all new + * system calls. By default, it will be set to "sys_". + */ + #undef LSS_NAME + #ifndef SYS_PREFIX + #define LSS_NAME(name) sys_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX < 0 + #define LSS_NAME(name) name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 0 + #define LSS_NAME(name) sys0_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 1 + #define LSS_NAME(name) sys1_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 2 + #define LSS_NAME(name) sys2_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 3 + #define LSS_NAME(name) sys3_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 4 + #define LSS_NAME(name) sys4_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 5 + #define LSS_NAME(name) sys5_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 6 + #define LSS_NAME(name) sys6_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 7 + #define LSS_NAME(name) sys7_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 8 + #define LSS_NAME(name) sys8_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 9 + #define LSS_NAME(name) sys9_##name + #endif + + #undef LSS_RETURN + #if defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \ + || defined(__ARM_EABI__) || defined(__aarch64__) || defined(__s390__) \ + || defined(__e2k__) + /* Failing system calls return a negative result in the range of + * -1..-4095. These are "errno" values with the sign inverted. + */ + #define LSS_RETURN(type, res) \ + do { \ + if ((unsigned long)(res) >= (unsigned long)(-4095)) { \ + LSS_ERRNO = -(res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__mips__) + /* On MIPS, failing system calls return -1, and set errno in a + * separate CPU register. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err) { \ + unsigned long __errnovalue = (res); \ + LSS_ERRNO = __errnovalue; \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__PPC__) + /* On PPC, failing system calls return -1, and set errno in a + * separate CPU register. See linux/unistd.h. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err & 0x10000000 ) { \ + LSS_ERRNO = (res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #endif + #if defined(__i386__) + /* In PIC mode (e.g. when building shared libraries), gcc for i386 + * reserves ebx. Unfortunately, most distribution ship with implementations + * of _syscallX() which clobber ebx. + * Also, most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_ENTRYPOINT + #ifdef SYS_SYSCALL_ENTRYPOINT + static inline void (**LSS_NAME(get_syscall_entrypoint)(void))(void) { + void (**entrypoint)(void); + asm volatile(".bss\n" + ".align 8\n" + ".globl " SYS_SYSCALL_ENTRYPOINT "\n" + ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n" + ".previous\n" + /* This logically does 'lea "SYS_SYSCALL_ENTRYPOINT", %0' */ + "call 0f\n" + "0:pop %0\n" + "add $_GLOBAL_OFFSET_TABLE_+[.-0b], %0\n" + "mov " SYS_SYSCALL_ENTRYPOINT "@GOT(%0), %0\n" + : "=r"(entrypoint)); + return entrypoint; + } + + #define LSS_ENTRYPOINT ".bss\n" \ + ".align 8\n" \ + ".globl " SYS_SYSCALL_ENTRYPOINT "\n" \ + ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n" \ + ".previous\n" \ + /* Check the SYS_SYSCALL_ENTRYPOINT vector */ \ + "push %%eax\n" \ + "call 10000f\n" \ + "10000:pop %%eax\n" \ + "add $_GLOBAL_OFFSET_TABLE_+[.-10000b], %%eax\n" \ + "mov " SYS_SYSCALL_ENTRYPOINT \ + "@GOT(%%eax), %%eax\n" \ + "mov 0(%%eax), %%eax\n" \ + "test %%eax, %%eax\n" \ + "jz 10002f\n" \ + "push %%eax\n" \ + "call 10001f\n" \ + "10001:pop %%eax\n" \ + "add $(10003f-10001b), %%eax\n" \ + "xchg 4(%%esp), %%eax\n" \ + "ret\n" \ + "10002:pop %%eax\n" \ + "int $0x80\n" \ + "10003:\n" + #else + #define LSS_ENTRYPOINT "int $0x80\n" + #endif + #undef LSS_BODY + #define LSS_BODY(type,args...) \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx" \ + args \ + : "memory"); \ + LSS_RETURN(type,__res) + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)(void) { \ + long __res; \ + __asm__ volatile(LSS_ENTRYPOINT \ + : "=a" (__res) \ + : "0" (__NR_##name) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1))); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1,type2 arg2) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name),"ri" ((long)(arg1)), "c" ((long)(arg2))); \ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1,type2 arg2,type3 arg3) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4))); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + "movl %1,%%eax\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx" \ + : "=a" (__res) \ + : "i" (__NR_##name), "ri" ((long)(arg1)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + long __res; \ + struct { long __a1; long __a6; } __s = { (long)arg1, (long) arg6 }; \ + __asm__ __volatile__("push %%ebp\n" \ + "push %%ebx\n" \ + "movl 4(%2),%%ebp\n" \ + "movl 0(%2), %%ebx\n" \ + "movl %1,%%eax\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx\n" \ + "pop %%ebp" \ + : "=a" (__res) \ + : "i" (__NR_##name), "0" ((long)(&__s)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "movl %3,%%ecx\n" + "jecxz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "movl %4,%%ecx\n" + "jecxz 1f\n" + + /* Set up alignment of the child stack: + * child_stack = (child_stack & ~0xF) - 20; + */ + "andl $-16,%%ecx\n" + "subl $20,%%ecx\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movl %6,%%eax\n" + "movl %%eax,4(%%ecx)\n" + "movl %3,%%eax\n" + "movl %%eax,(%%ecx)\n" + + /* %eax = syscall(%eax = __NR_clone, + * %ebx = flags, + * %ecx = child_stack, + * %edx = parent_tidptr, + * %esi = newtls, + * %edi = child_tidptr) + * Also, make sure that %ebx gets preserved as it is + * used in PIC mode. + */ + "movl %8,%%esi\n" + "movl %7,%%edx\n" + "movl %5,%%eax\n" + "movl %9,%%edi\n" + "pushl %%ebx\n" + "movl %%eax,%%ebx\n" + "movl %2,%%eax\n" + LSS_ENTRYPOINT + + /* In the parent: restore %ebx + * In the child: move "fn" into %ebx + */ + "popl %%ebx\n" + + /* if (%eax != 0) + * return %eax; + */ + "test %%eax,%%eax\n" + "jnz 1f\n" + + /* In the child, now. Terminate frame pointer chain. + */ + "movl $0,%%ebp\n" + + /* Call "fn". "arg" is already on the stack. + */ + "call *%%ebx\n" + + /* Call _exit(%ebx). Unfortunately older versions + * of gcc restrict the number of arguments that can + * be passed to asm(). So, we need to hard-code the + * system call number. + */ + "movl %%eax,%%ebx\n" + "movl $1,%%eax\n" + LSS_ENTRYPOINT + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), + "m"(fn), "m"(child_stack), "m"(flags), "m"(arg), + "m"(parent_tidptr), "m"(newtls), "m"(child_tidptr) + : "memory", "ecx", "edx", "esi", "edi"); + LSS_RETURN(int, __res); + } + + LSS_INLINE _syscall1(int, set_thread_area, void *, u) + LSS_INLINE _syscall1(int, get_thread_area, void *, u) + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:movl %1,%%eax\n" + LSS_ENTRYPOINT + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_rt_sigreturn)); + return res; + } + LSS_INLINE void (*LSS_NAME(restore)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:pop %%eax\n" + "movl %1,%%eax\n" + LSS_ENTRYPOINT + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_sigreturn)); + return res; + } + #elif defined(__x86_64__) + /* There are no known problems with any of the _syscallX() macros + * currently shipping for x86_64, but we still need to be able to define + * our own version so that we can override the location of the errno + * location (e.g. when using the clone() system call with the CLONE_VM + * option). + */ + #undef LSS_ENTRYPOINT + #ifdef SYS_SYSCALL_ENTRYPOINT + static inline void (**LSS_NAME(get_syscall_entrypoint)(void))(void) { + void (**entrypoint)(void); + asm volatile(".bss\n" + ".align 8\n" + ".globl " SYS_SYSCALL_ENTRYPOINT "\n" + ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n" + ".previous\n" + "mov " SYS_SYSCALL_ENTRYPOINT "@GOTPCREL(%%rip), %0\n" + : "=r"(entrypoint)); + return entrypoint; + } + + #define LSS_ENTRYPOINT \ + ".bss\n" \ + ".align 8\n" \ + ".globl " SYS_SYSCALL_ENTRYPOINT "\n" \ + ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n" \ + ".previous\n" \ + "mov " SYS_SYSCALL_ENTRYPOINT "@GOTPCREL(%%rip), %%rcx\n" \ + "mov 0(%%rcx), %%rcx\n" \ + "test %%rcx, %%rcx\n" \ + "jz 10001f\n" \ + "call *%%rcx\n" \ + "jmp 10002f\n" \ + "10001:syscall\n" \ + "10002:\n" + + #else + #define LSS_ENTRYPOINT "syscall\n" + #endif + + /* The x32 ABI has 32 bit longs, but the syscall interface is 64 bit. + * We need to explicitly cast to an unsigned 64 bit type to avoid implicit + * sign extension. We can't cast pointers directly because those are + * 32 bits, and gcc will dump ugly warnings about casting from a pointer + * to an integer of a different size. + */ + #undef LSS_SYSCALL_ARG + #define LSS_SYSCALL_ARG(a) ((uint64_t)(uintptr_t)(a)) + #undef _LSS_RETURN + #define _LSS_RETURN(type, res, cast) \ + do { \ + if ((uint64_t)(res) >= (uint64_t)(-4095)) { \ + LSS_ERRNO = -(res); \ + res = -1; \ + } \ + return (type)(cast)(res); \ + } while (0) + #undef LSS_RETURN + #define LSS_RETURN(type, res) _LSS_RETURN(type, res, uintptr_t) + + #undef _LSS_BODY + #define _LSS_BODY(nr, type, name, cast, ...) \ + long long __res; \ + __asm__ __volatile__(LSS_BODY_ASM##nr LSS_ENTRYPOINT \ + : "=a" (__res) \ + : "0" (__NR_##name) LSS_BODY_ARG##nr(__VA_ARGS__) \ + : LSS_BODY_CLOBBER##nr "r11", "rcx", "memory"); \ + _LSS_RETURN(type, __res, cast) + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + _LSS_BODY(nr, type, name, uintptr_t, ## args) + + #undef LSS_BODY_ASM0 + #undef LSS_BODY_ASM1 + #undef LSS_BODY_ASM2 + #undef LSS_BODY_ASM3 + #undef LSS_BODY_ASM4 + #undef LSS_BODY_ASM5 + #undef LSS_BODY_ASM6 + #define LSS_BODY_ASM0 + #define LSS_BODY_ASM1 LSS_BODY_ASM0 + #define LSS_BODY_ASM2 LSS_BODY_ASM1 + #define LSS_BODY_ASM3 LSS_BODY_ASM2 + #define LSS_BODY_ASM4 LSS_BODY_ASM3 "movq %5,%%r10;" + #define LSS_BODY_ASM5 LSS_BODY_ASM4 "movq %6,%%r8;" + #define LSS_BODY_ASM6 LSS_BODY_ASM5 "movq %7,%%r9;" + + #undef LSS_BODY_CLOBBER0 + #undef LSS_BODY_CLOBBER1 + #undef LSS_BODY_CLOBBER2 + #undef LSS_BODY_CLOBBER3 + #undef LSS_BODY_CLOBBER4 + #undef LSS_BODY_CLOBBER5 + #undef LSS_BODY_CLOBBER6 + #define LSS_BODY_CLOBBER0 + #define LSS_BODY_CLOBBER1 LSS_BODY_CLOBBER0 + #define LSS_BODY_CLOBBER2 LSS_BODY_CLOBBER1 + #define LSS_BODY_CLOBBER3 LSS_BODY_CLOBBER2 + #define LSS_BODY_CLOBBER4 LSS_BODY_CLOBBER3 "r10", + #define LSS_BODY_CLOBBER5 LSS_BODY_CLOBBER4 "r8", + #define LSS_BODY_CLOBBER6 LSS_BODY_CLOBBER5 "r9", + + #undef LSS_BODY_ARG0 + #undef LSS_BODY_ARG1 + #undef LSS_BODY_ARG2 + #undef LSS_BODY_ARG3 + #undef LSS_BODY_ARG4 + #undef LSS_BODY_ARG5 + #undef LSS_BODY_ARG6 + #define LSS_BODY_ARG0() + #define LSS_BODY_ARG1(arg1) \ + LSS_BODY_ARG0(), "D" (arg1) + #define LSS_BODY_ARG2(arg1, arg2) \ + LSS_BODY_ARG1(arg1), "S" (arg2) + #define LSS_BODY_ARG3(arg1, arg2, arg3) \ + LSS_BODY_ARG2(arg1, arg2), "d" (arg3) + #define LSS_BODY_ARG4(arg1, arg2, arg3, arg4) \ + LSS_BODY_ARG3(arg1, arg2, arg3), "r" (arg4) + #define LSS_BODY_ARG5(arg1, arg2, arg3, arg4, arg5) \ + LSS_BODY_ARG4(arg1, arg2, arg3, arg4), "r" (arg5) + #define LSS_BODY_ARG6(arg1, arg2, arg3, arg4, arg5, arg6) \ + LSS_BODY_ARG5(arg1, arg2, arg3, arg4, arg5), "r" (arg6) + + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, LSS_SYSCALL_ARG(arg1)); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2));\ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \ + LSS_SYSCALL_ARG(arg3)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \ + LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4));\ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \ + LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4), \ + LSS_SYSCALL_ARG(arg5)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \ + LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4), \ + LSS_SYSCALL_ARG(arg5), LSS_SYSCALL_ARG(arg6));\ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long long __res; + { + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "testq %4,%4\n" + "jz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "testq %5,%5\n" + "jz 1f\n" + + /* childstack -= 2*sizeof(void *); + */ + "subq $16,%5\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movq %7,8(%5)\n" + "movq %4,0(%5)\n" + + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "movq %2,%%rax\n" + "movq %9,%%r8\n" + "movq %10,%%r10\n" + LSS_ENTRYPOINT + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate frame pointer chain. + */ + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". + */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%ebx). + */ + "movq %%rax,%%rdi\n" + "movq %3,%%rax\n" + LSS_ENTRYPOINT + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), + "r"(LSS_SYSCALL_ARG(fn)), + "S"(LSS_SYSCALL_ARG(child_stack)), + "D"(LSS_SYSCALL_ARG(flags)), + "r"(LSS_SYSCALL_ARG(arg)), + "d"(LSS_SYSCALL_ARG(parent_tidptr)), + "r"(LSS_SYSCALL_ARG(newtls)), + "r"(LSS_SYSCALL_ARG(child_tidptr)) + : "memory", "r8", "r10", "r11", "rcx"); + } + LSS_RETURN(int, __res); + } + LSS_INLINE _syscall2(int, arch_prctl, int, c, void *, a) + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On x86-64, the kernel does not know how to return from + * a signal handler. Instead, it relies on user space to provide a + * restorer function that calls the rt_sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + long long res; + __asm__ __volatile__("jmp 2f\n" + ".align 16\n" + "1:movq %1,%%rax\n" + LSS_ENTRYPOINT + "2:leaq 1b(%%rip),%0\n" + : "=r" (res) + : "i" (__NR_rt_sigreturn)); + return (void (*)(void))(uintptr_t)res; + } + #elif defined(__ARM_ARCH_3__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ (__syscall(name) \ + : "=r"(__res_r0) : args : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "cmp %2,#0\n" + "cmpne %3,#0\n" + "moveq %0,%1\n" + "beq 1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "str %5,[%3,#-4]!\n" + "str %2,[%3,#-4]!\n" + + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + __syscall(clone)"\n" + + /* if (%r0 != 0) + * return %r0; + */ + "movs %0,r0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + "mov lr,pc\n" + "ldr pc,[sp]\n" + + /* Call _exit(%r0). + */ + __syscall(exit)"\n" + "1:\n" + : "=r" (__res) + : "i"(-EINVAL), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid) + : "cc", "lr", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__ARM_EABI__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all fo the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ ("push {r7}\n" \ + "mov r7, %1\n" \ + "swi 0x0\n" \ + "pop {r7}\n" \ + : "=r"(__res_r0) \ + : "i"(__NR_##name) , ## args \ + : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + if (fn == NULL || child_stack == NULL) { + __res = -EINVAL; + LSS_RETURN(int, __res); + } + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + { + uintptr_t* cstack = (uintptr_t*)child_stack - 2; + cstack[0] = (uintptr_t)fn; + cstack[1] = (uintptr_t)arg; + child_stack = cstack; + } + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__( +#ifdef __thumb2__ + "push {r7}\n" +#endif + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + "mov r7, %6\n" + "swi 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "cmp r0, #0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + + "ldr lr,[sp]\n" + "blx lr\n" + + /* Call _exit(%r0). + */ + "mov r7, %7\n" + "swi 0x0\n" + /* Unreachable */ + "bkpt #0\n" + "1:\n" +#ifdef __thumb2__ + "pop {r7}\n" +#endif + "movs %0,r0\n" + : "=r"(__res) + : "r"(__stack), "r"(__flags), "r"(__ptid), "r"(__tls), "r"(__ctid), + "i"(__NR_clone), "i"(__NR_exit) + : "cc", "lr", "memory" +#ifndef __thumb2__ + , "r7" +#endif + ); + } + LSS_RETURN(int, __res); + } + #elif defined(__aarch64__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register int64_t __r##r __asm__("x"#r) = (int64_t)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register int64_t __res_x0 __asm__("x0"); \ + int64_t __res; \ + __asm__ __volatile__ ("mov x8, %1\n" \ + "svc 0x0\n" \ + : "=r"(__res_x0) \ + : "i"(__NR_##name) , ## args \ + : "x8", "memory"); \ + __res = __res_x0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + int64_t __res; + { + register uint64_t __flags __asm__("x0") = flags; + register void *__stack __asm__("x1") = child_stack; + register void *__ptid __asm__("x2") = parent_tidptr; + register void *__tls __asm__("x3") = newtls; + register int *__ctid __asm__("x4") = child_tidptr; + __asm__ __volatile__(/* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "stp %1, %4, [%2, #-16]!\n" + + /* %x0 = syscall(%x0 = flags, + * %x1 = child_stack, + * %x2 = parent_tidptr, + * %x3 = newtls, + * %x4 = child_tidptr) + */ + "mov x8, %8\n" + "svc 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "mov %0, x0\n" + "cbnz x0, 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldp x1, x0, [sp], #16\n" + "blr x1\n" + + /* Call _exit(%r0). + */ + "mov x8, %9\n" + "svc 0x0\n" + "1:\n" + : "=r" (__res) + : "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid), + "i"(__NR_clone), "i"(__NR_exit) + : "cc", "x8", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__mips__) + #undef LSS_REG + #define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \ + (unsigned long)(a) + #undef LSS_BODY + #undef LSS_SYSCALL_CLOBBERS + #if _MIPS_SIM == _MIPS_SIM_ABI32 + #define LSS_SYSCALL_CLOBBERS "$1", "$3", "$8", "$9", "$10", \ + "$11", "$12", "$13", "$14", "$15", \ + "$24", "$25", "hi", "lo", "memory" + #else + #define LSS_SYSCALL_CLOBBERS "$1", "$3", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "$25", \ + "hi", "lo", "memory" + #endif + #define LSS_BODY(type,name,r7,...) \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ ("syscall\n" \ + : "=r"(__v0), r7 (__r7) \ + : "0"(__v0), ##__VA_ARGS__ \ + : LSS_SYSCALL_CLOBBERS); \ + LSS_RETURN(type, __v0, __r7) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_BODY(type, name, "=r"); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_BODY(type, name, "=r", "r"(__r4)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall5 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ (".set noreorder\n" \ + "subu $29, 32\n" \ + "sw %5, 16($29)\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "+r"(__v0), "+r" (__r7) \ + : "r"(__r4), "r"(__r5), \ + "r"(__r6), "r" ((unsigned long)arg5) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "$25", \ + "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8)); \ + } + #endif + #undef _syscall6 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ (".set noreorder\n" \ + "subu $29, 32\n" \ + "sw %5, 16($29)\n" \ + "sw %6, 20($29)\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "+r"(__v0), "+r" (__r7) \ + : "r"(__r4), "r"(__r5), \ + "r"(__r6), "r" ((unsigned long)arg5), \ + "r" ((unsigned long)arg6) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "$25", \ + "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5,type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); LSS_REG(9, arg6); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8), "r"(__r9)); \ + } + #endif + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + register unsigned long __v0 __asm__("$2") = -EINVAL; + register unsigned long __r7 __asm__("$7") = (unsigned long)newtls; + { + register int __flags __asm__("$4") = flags; + register void *__stack __asm__("$5") = child_stack; + register void *__ptid __asm__("$6") = parent_tidptr; + register int *__ctid __asm__("$8") = child_tidptr; + __asm__ __volatile__( + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu $29,24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub $29,16\n" + #else + "dsubu $29,16\n" + #endif + + /* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "beqz %4,1f\n" + "beqz %5,1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu %5,32\n" + "sw %4,0(%5)\n" + "sw %7,4(%5)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub %5,32\n" + "sw %4,0(%5)\n" + "sw %7,8(%5)\n" + #else + "dsubu %5,32\n" + "sd %4,0(%5)\n" + "sd %7,8(%5)\n" + #endif + + /* $7 = syscall($4 = flags, + * $5 = child_stack, + * $6 = parent_tidptr, + * $7 = newtls, + * $8 = child_tidptr) + */ + "li $2,%2\n" + "syscall\n" + + /* if ($7 != 0) + * return $2; + */ + "bnez $7,1f\n" + "bnez $2,1f\n" + + /* In the child, now. Call "fn(arg)". + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "lw $25,0($29)\n" + "lw $4,4($29)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "lw $25,0($29)\n" + "lw $4,8($29)\n" + #else + "ld $25,0($29)\n" + "ld $4,8($29)\n" + #endif + "jalr $25\n" + + /* Call _exit($2) + */ + "move $4,$2\n" + "li $2,%3\n" + "syscall\n" + + "1:\n" + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "addu $29, 24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "add $29, 16\n" + #else + "daddu $29,16\n" + #endif + : "+r" (__v0), "+r" (__r7) + : "i"(__NR_clone), "i"(__NR_exit), "r"(fn), + "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__ctid) + : "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$24", "$25", "memory"); + } + LSS_RETURN(int, __v0, __r7); + } + #elif defined (__PPC__) + #undef LSS_LOADARGS_0 + #define LSS_LOADARGS_0(name, dummy...) \ + __sc_0 = __NR_##name + #undef LSS_LOADARGS_1 + #define LSS_LOADARGS_1(name, arg1) \ + LSS_LOADARGS_0(name); \ + __sc_3 = (unsigned long) (arg1) + #undef LSS_LOADARGS_2 + #define LSS_LOADARGS_2(name, arg1, arg2) \ + LSS_LOADARGS_1(name, arg1); \ + __sc_4 = (unsigned long) (arg2) + #undef LSS_LOADARGS_3 + #define LSS_LOADARGS_3(name, arg1, arg2, arg3) \ + LSS_LOADARGS_2(name, arg1, arg2); \ + __sc_5 = (unsigned long) (arg3) + #undef LSS_LOADARGS_4 + #define LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4) \ + LSS_LOADARGS_3(name, arg1, arg2, arg3); \ + __sc_6 = (unsigned long) (arg4) + #undef LSS_LOADARGS_5 + #define LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5) \ + LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4); \ + __sc_7 = (unsigned long) (arg5) + #undef LSS_LOADARGS_6 + #define LSS_LOADARGS_6(name, arg1, arg2, arg3, arg4, arg5, arg6) \ + LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5); \ + __sc_8 = (unsigned long) (arg6) + #undef LSS_ASMINPUT_0 + #define LSS_ASMINPUT_0 "0" (__sc_0) + #undef LSS_ASMINPUT_1 + #define LSS_ASMINPUT_1 LSS_ASMINPUT_0, "1" (__sc_3) + #undef LSS_ASMINPUT_2 + #define LSS_ASMINPUT_2 LSS_ASMINPUT_1, "2" (__sc_4) + #undef LSS_ASMINPUT_3 + #define LSS_ASMINPUT_3 LSS_ASMINPUT_2, "3" (__sc_5) + #undef LSS_ASMINPUT_4 + #define LSS_ASMINPUT_4 LSS_ASMINPUT_3, "4" (__sc_6) + #undef LSS_ASMINPUT_5 + #define LSS_ASMINPUT_5 LSS_ASMINPUT_4, "5" (__sc_7) + #undef LSS_ASMINPUT_6 + #define LSS_ASMINPUT_6 LSS_ASMINPUT_5, "6" (__sc_8) + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + \ + LSS_LOADARGS_##nr(name, args); \ + __asm__ __volatile__ \ + ("sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory", \ + "r9", "r10", "r11", "r12"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, arg1); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, arg1, arg2); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, arg1, arg2, arg3); \ + } + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, arg1, arg2, arg3, arg4); \ + } + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5); \ + } + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \ + } + /* clone function adapted from glibc 2.3.6 clone.S */ + /* TODO(csilvers): consider wrapping some args up in a struct, like we + * do for i386's _syscall6, so we can compile successfully on gcc 2.95 + */ + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __ret, __err; + { + register int (*__fn)(void *) __asm__ ("r8") = fn; + register void *__cstack __asm__ ("r4") = child_stack; + register int __flags __asm__ ("r3") = flags; + register void * __arg __asm__ ("r9") = arg; + register int * __ptidptr __asm__ ("r5") = parent_tidptr; + register void * __newtls __asm__ ("r6") = newtls; + register int * __ctidptr __asm__ ("r7") = child_tidptr; + __asm__ __volatile__( + /* check for fn == NULL + * and child_stack == NULL + */ + "cmpwi cr0, %6, 0\n\t" + "cmpwi cr1, %7, 0\n\t" + "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t" + "beq- cr0, 1f\n\t" + + /* set up stack frame for child */ + "clrrwi %7, %7, 4\n\t" + "li 0, 0\n\t" + "stwu 0, -16(%7)\n\t" + + /* fn, arg, child_stack are saved across the syscall: r28-30 */ + "mr 28, %6\n\t" + "mr 29, %7\n\t" + "mr 27, %9\n\t" + + /* syscall */ + "li 0, %4\n\t" + /* flags already in r3 + * child_stack already in r4 + * ptidptr already in r5 + * newtls already in r6 + * ctidptr already in r7 + */ + "sc\n\t" + + /* Test if syscall was successful */ + "cmpwi cr1, 3, 0\n\t" + "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" + "bne- cr1, 1f\n\t" + + /* Do the function call */ + "mtctr 28\n\t" + "mr 3, 27\n\t" + "bctrl\n\t" + + /* Call _exit(r3) */ + "li 0, %5\n\t" + "sc\n\t" + + /* Return to parent */ + "1:\n" + "mfcr %1\n\t" + "mr %0, 3\n\t" + : "=r" (__ret), "=r" (__err) + : "0" (-1), "1" (EINVAL), + "i" (__NR_clone), "i" (__NR_exit), + "r" (__fn), "r" (__cstack), "r" (__flags), + "r" (__arg), "r" (__ptidptr), "r" (__newtls), + "r" (__ctidptr) + : "cr0", "cr1", "memory", "ctr", + "r0", "r29", "r27", "r28"); + } + LSS_RETURN(int, __ret, __err); + } + #elif defined(__s390__) + #undef LSS_REG + #define LSS_REG(r, a) register unsigned long __r##r __asm__("r"#r) = (unsigned long) a + #undef LSS_BODY + #define LSS_BODY(type, name, args...) \ + register unsigned long __nr __asm__("r1") \ + = (unsigned long)(__NR_##name); \ + register long __res_r2 __asm__("r2"); \ + long __res; \ + __asm__ __volatile__ \ + ("svc 0\n\t" \ + : "=d"(__res_r2) \ + : "d"(__nr), ## args \ + : "memory"); \ + __res = __res_r2; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(2, arg1); \ + LSS_BODY(type, name, "0"(__r2)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4)); \ + } + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5)); \ + } + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4, type5 arg5) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); LSS_REG(6, arg5); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5), "d"(__r6)); \ + } + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4, type5 arg5, type6 arg6) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); LSS_REG(6, arg5); LSS_REG(7, arg6); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5), "d"(__r6), "d"(__r7)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __ret; + { + register int (*__fn)(void *) __asm__ ("r1") = fn; + register void *__cstack __asm__ ("r2") = child_stack; + register int __flags __asm__ ("r3") = flags; + register void *__arg __asm__ ("r0") = arg; + register int *__ptidptr __asm__ ("r4") = parent_tidptr; + register void *__newtls __asm__ ("r6") = newtls; + register int *__ctidptr __asm__ ("r5") = child_tidptr; + __asm__ __volatile__ ( + #ifndef __s390x__ + /* arg already in r0 */ + "ltr %4, %4\n\t" /* check fn, which is already in r1 */ + "jz 1f\n\t" /* NULL function pointer, return -EINVAL */ + "ltr %5, %5\n\t" /* check child_stack, which is already in r2 */ + "jz 1f\n\t" /* NULL stack pointer, return -EINVAL */ + /* flags already in r3 */ + /* parent_tidptr already in r4 */ + /* child_tidptr already in r5 */ + /* newtls already in r6 */ + "svc %2\n\t" /* invoke clone syscall */ + "ltr %0,%%r2\n\t" /* load return code into __ret and test */ + "jnz 1f\n\t" /* return to parent if non-zero */ + /* start child thread */ + "lr %%r2, %7\n\t" /* set first parameter to void *arg */ + "ahi %%r15, -96\n\t" /* make room on the stack for the save area */ + "xc 0(4,%%r15), 0(%%r15)\n\t" + "basr %%r14, %4\n\t" /* jump to fn */ + "svc %3\n" /* invoke exit syscall */ + "1:\n" + #else + /* arg already in r0 */ + "ltgr %4, %4\n\t" /* check fn, which is already in r1 */ + "jz 1f\n\t" /* NULL function pointer, return -EINVAL */ + "ltgr %5, %5\n\t" /* check child_stack, which is already in r2 */ + "jz 1f\n\t" /* NULL stack pointer, return -EINVAL */ + /* flags already in r3 */ + /* parent_tidptr already in r4 */ + /* child_tidptr already in r5 */ + /* newtls already in r6 */ + "svc %2\n\t" /* invoke clone syscall */ + "ltgr %0, %%r2\n\t" /* load return code into __ret and test */ + "jnz 1f\n\t" /* return to parent if non-zero */ + /* start child thread */ + "lgr %%r2, %7\n\t" /* set first parameter to void *arg */ + "aghi %%r15, -160\n\t" /* make room on the stack for the save area */ + "xc 0(8,%%r15), 0(%%r15)\n\t" + "basr %%r14, %4\n\t" /* jump to fn */ + "svc %3\n" /* invoke exit syscall */ + "1:\n" + #endif + : "=r" (__ret) + : "0" (-EINVAL), "i" (__NR_clone), "i" (__NR_exit), + "d" (__fn), "d" (__cstack), "d" (__flags), "d" (__arg), + "d" (__ptidptr), "d" (__newtls), "d" (__ctidptr) + : "cc", "r14", "memory" + ); + } + LSS_RETURN(int, __ret); + } + #elif defined(__e2k__) + + #undef _LSS_BODY + #define _LSS_BODY(nr, type, name, ...) \ + register unsigned long long __res; \ + __asm__ __volatile__ \ + ( \ + "{\n\t" \ + " sdisp %%ctpr1, 0x3\n\t" \ + " addd, s 0x0, %[sys_num], %%b[0]\n\t" \ + LSS_BODY_ASM##nr \ + "}\n\t" \ + "{\n\t" \ + " call %%ctpr1, wbs = %#\n\t" \ + "}\n\t" \ + "{\n\t" \ + " addd, s 0x0, %%b[0], %[res]\n\t" \ + "}\n\t" \ + : [res] "=r" (__res) \ + : \ + LSS_BODY_ARG##nr(__VA_ARGS__) \ + [sys_num] "ri" (__NR_##name) \ + : "ctpr1", "ctpr2", "ctpr3", \ + "b[0]", "b[1]", "b[2]", "b[3]", \ + "b[4]", "b[5]", "b[6]", "b[7]" \ + ); \ + LSS_RETURN(type, __res); + + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + _LSS_BODY(nr, type, name, ## args) + + #undef LSS_BODY_ASM0 + #undef LSS_BODY_ASM1 + #undef LSS_BODY_ASM2 + #undef LSS_BODY_ASM3 + #undef LSS_BODY_ASM4 + #undef LSS_BODY_ASM5 + #undef LSS_BODY_ASM6 + + #define LSS_BODY_ASM0 + #define LSS_BODY_ASM1 LSS_BODY_ASM0 \ + " addd, s 0x0, %[arg1], %%b[1]\n\t" + #define LSS_BODY_ASM2 LSS_BODY_ASM1 \ + " addd, s 0x0, %[arg2], %%b[2]\n\t" + #define LSS_BODY_ASM3 LSS_BODY_ASM2 \ + " addd, s 0x0, %[arg3], %%b[3]\n\t" + #define LSS_BODY_ASM4 LSS_BODY_ASM3 \ + " addd, s 0x0, %[arg4], %%b[4]\n\t" + #define LSS_BODY_ASM5 LSS_BODY_ASM4 \ + " addd, s 0x0, %[arg5], %%b[5]\n\t" + #define LSS_BODY_ASM6 LSS_BODY_ASM5 \ + "}\n\t" \ + "{\n\t" \ + " addd, s 0x0, %[arg6], %%b[6]\n\t" + + #undef LSS_SYSCALL_ARG + #define LSS_SYSCALL_ARG(a) ((unsigned long long)(uintptr_t)(a)) + + #undef LSS_BODY_ARG0 + #undef LSS_BODY_ARG1 + #undef LSS_BODY_ARG2 + #undef LSS_BODY_ARG3 + #undef LSS_BODY_ARG4 + #undef LSS_BODY_ARG5 + #undef LSS_BODY_ARG6 + + #define LSS_BODY_ARG0() + #define LSS_BODY_ARG1(_arg1) \ + [arg1] "ri" LSS_SYSCALL_ARG(_arg1), + #define LSS_BODY_ARG2(_arg1, _arg2) \ + LSS_BODY_ARG1(_arg1) \ + [arg2] "ri" LSS_SYSCALL_ARG(_arg2), + #define LSS_BODY_ARG3(_arg1, _arg2, _arg3) \ + LSS_BODY_ARG2(_arg1, _arg2) \ + [arg3] "ri" LSS_SYSCALL_ARG(_arg3), + #define LSS_BODY_ARG4(_arg1, _arg2, _arg3, _arg4) \ + LSS_BODY_ARG3(_arg1, _arg2, _arg3) \ + [arg4] "ri" LSS_SYSCALL_ARG(_arg4), + #define LSS_BODY_ARG5(_arg1, _arg2, _arg3, _arg4, _arg5) \ + LSS_BODY_ARG4(_arg1, _arg2, _arg3, _arg4) \ + [arg5] "ri" LSS_SYSCALL_ARG(_arg5), + #define LSS_BODY_ARG6(_arg1, _arg2, _arg3, _arg4, _arg5, _arg6) \ + LSS_BODY_ARG5(_arg1, _arg2, _arg3, _arg4, _arg5) \ + [arg6] "ri" LSS_SYSCALL_ARG(_arg6), + + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, arg1) \ + } + + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, arg1, arg2) \ + } + + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, arg1, arg2, arg3) \ + } + + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, arg1, arg2, arg3, arg4) \ + } + + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5) \ + } + + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6) \ + } + + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + unsigned long long __res; + + __asm__ __volatile__ ( + "{\n\t" + " addd,s 0x0, %[nr_clone], %%b[0]\n\t" + " addd,s 0x0, %[flags], %%db[1]\n\t" + " addd,s 0x0, %[child_stack], %%db[2]\n\t" + " addd,s 0x0, %[parent_tidptr], %%db[3]\n\t" + " addd,s 0x0, %[child_tidptr], %%db[4]\n\t" + " addd,s 0x0, %[newtls], %%db[5]\n\t" + "}\n\t" + /* if (fn == NULL) + * return -EINVAL; + */ + + "{\n\t" + " disp %%ctpr1, .L1\n\t" + "}\n\t" + "{\n\t" + " cmpesb,s 0x0, %[fn], %%pred0\n\t" + "}\n\t" + "{\n\t" + " ct %%ctpr1 ? %%pred0\n\t" + "}\n\t" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "{\n\t" + " cmpesb,s 0x0, %%db[2], %%pred0\n\t" + "}\n\t" + "{\n\t" + " ct %%ctpr1 ? %%pred0\n\t" + "}\n\t" + + /* b[0] = syscall(%b[0] = __NR_clone, + * %db[1] = flags, + * %db[2] = child_stack, + * %db[3] = parent_tidptr, + * %db[4] = child_tidptr, + * %db[5] = newtls) + */ + "{\n\t" + " sdisp %%ctpr1, 0x3\n\t" + "}\n\t" + "{\n\t" + " call %%ctpr1, wbs = %#\n\t" + "}\n\t" + + /* if (%[b0] != 0) + * return %b[0]; + */ + "{\n\t" + " disp %%ctpr1, .L2\n\t" + " cmpesb,s 0x0, %%b[0], %%pred0\n\t" + "}\n\t" + "{\n\t" + " ct %%ctpr1 ? ~%%pred0\n\t" + "}\n\t" + /* In the child, now. Call "fn(arg)". + */ + + "{\n\t" + " movtd,s %[fn], %%ctpr1\n\t" + "}\n\t" + "{\n\t" + " addd,s 0x0, %[arg], %%db[0]\n\t" + "}\n\t" + "{\n\t" + " call %%ctpr1, wbs = %#\n\t" + "}\n\t" + /* Call _exit(%b[0]). + */ + + "{\n\t" + " sdisp %%ctpr1, 0x3\n\t" + " addd,s 0x0, %%b[0], %%b[1]\n\t" + "}\n\t" + "{\n\t" + " addd,s 0x0, %[nr_exit], %%b[0]\n\t" + "}\n\t" + "{\n\t" + " call %%ctpr1, wbs = %#\n\t" + "}\n\t" + "{\n\t" + " disp %%ctpr1, .L2\n\t" + " adds,s 0x0, 0x0, %%b[0]\n\t" + "}\n\t" + "{\n\t" + " ct %%ctpr1\n\t" + "}\n\t" + ".L1:\n\t" + "{\n\t" + " addd,s 0x0, %[einval], %%b[0]\n\t" + "}\n\t" + ".L2:\n\t" + "{\n\t" + " addd,s 0x0, %%b[0], %[res]\n\t" + "}\n\t" + : [res] "=r" LSS_SYSCALL_ARG(__res) + : [nr_clone] "ri" LSS_SYSCALL_ARG(__NR_clone) + [arg] "ri" LSS_SYSCALL_ARG(arg) + [nr_exit] "ri" LSS_SYSCALL_ARG(__NR_exit) + [flags] "ri" LSS_SYSCALL_ARG(flags) + [child_stack] "ri" LSS_SYSCALL_ARG(child_stack) + [parent_tidptr] "ri" + LSS_SYSCALL_ARG(parent_tidptr) + [newtls] "ri" LSS_SYSCALL_ARG(newtls) + [child_tidptr] "ri" + LSS_SYSCALL_ARG(child_tidptr) + [fn] "ri" LSS_SYSCALL_ARG(fn) + [einval] "ri" LSS_SYSCALL_ARG(-EINVAL) + : "ctpr1", "b[0]", "b[1]", "b[2]", "b[3]", + "b[4]", "b[5]", "pred0"); + LSS_RETURN(int, __res); + } + + #endif + #define __NR__exit __NR_exit + #define __NR__gettid __NR_gettid + #define __NR__mremap __NR_mremap + LSS_INLINE _syscall1(void *, brk, void *, e) + LSS_INLINE _syscall1(int, chdir, const char *,p) + LSS_INLINE _syscall1(int, close, int, f) + LSS_INLINE _syscall2(int, clock_getres, int, c, + struct kernel_timespec*, t) + LSS_INLINE _syscall2(int, clock_gettime, int, c, + struct kernel_timespec*, t) + LSS_INLINE _syscall1(int, dup, int, f) + #if defined(__NR_dup2) + // dup2 is polyfilled below when not available. + LSS_INLINE _syscall2(int, dup2, int, s, + int, d) + #endif + #if defined(__NR_dup3) + LSS_INLINE _syscall3(int, dup3, int, s, int, d, int, f) + #endif + LSS_INLINE _syscall3(int, execve, const char*, f, + const char*const*,a,const char*const*, e) + LSS_INLINE _syscall1(int, _exit, int, e) + LSS_INLINE _syscall1(int, exit_group, int, e) + LSS_INLINE _syscall3(int, fcntl, int, f, + int, c, long, a) + #if defined(__NR_fork) + // fork is polyfilled below when not available. + LSS_INLINE _syscall0(pid_t, fork) + #endif + LSS_INLINE _syscall2(int, fstat, int, f, + struct kernel_stat*, b) + LSS_INLINE _syscall2(int, fstatfs, int, f, + struct kernel_statfs*, b) + #if defined(__x86_64__) + /* Need to make sure off_t isn't truncated to 32-bits under x32. */ + LSS_INLINE int LSS_NAME(ftruncate)(int f, off_t l) { + LSS_BODY(2, int, ftruncate, LSS_SYSCALL_ARG(f), (uint64_t)(l)); + } + #else + LSS_INLINE _syscall2(int, ftruncate, int, f, + off_t, l) + #endif + LSS_INLINE _syscall6(int, futex, int*, u, + int, o, int, v, + struct kernel_timespec*, t, + int*, u2, int, v2) + LSS_INLINE _syscall3(int, getdents, int, f, + struct kernel_dirent*, d, int, c) + LSS_INLINE _syscall3(int, getdents64, int, f, + struct kernel_dirent64*, d, int, c) + LSS_INLINE _syscall0(gid_t, getegid) + LSS_INLINE _syscall0(uid_t, geteuid) + #if defined(__NR_getpgrp) + LSS_INLINE _syscall0(pid_t, getpgrp) + #endif + LSS_INLINE _syscall0(pid_t, getpid) + LSS_INLINE _syscall0(pid_t, getppid) + LSS_INLINE _syscall2(int, getpriority, int, a, + int, b) + LSS_INLINE _syscall3(int, getresgid, gid_t *, r, + gid_t *, e, gid_t *, s) + LSS_INLINE _syscall3(int, getresuid, uid_t *, r, + uid_t *, e, uid_t *, s) +#if !defined(__ARM_EABI__) + LSS_INLINE _syscall2(int, getrlimit, int, r, + struct kernel_rlimit*, l) +#endif + LSS_INLINE _syscall1(pid_t, getsid, pid_t, p) + LSS_INLINE _syscall0(pid_t, _gettid) + LSS_INLINE _syscall2(pid_t, gettimeofday, struct kernel_timeval*, t, + void*, tz) + LSS_INLINE _syscall5(int, setxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall5(int, lsetxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall4(ssize_t, getxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall4(ssize_t, lgetxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall3(ssize_t, listxattr, const char *,p, + char *, l, size_t, s) + LSS_INLINE _syscall3(ssize_t, llistxattr, const char *,p, + char *, l, size_t, s) + LSS_INLINE _syscall3(int, ioctl, int, d, + int, r, void *, a) + LSS_INLINE _syscall2(int, ioprio_get, int, which, + int, who) + LSS_INLINE _syscall3(int, ioprio_set, int, which, + int, who, int, ioprio) + LSS_INLINE _syscall2(int, kill, pid_t, p, + int, s) + #if defined(__x86_64__) + /* Need to make sure off_t isn't truncated to 32-bits under x32. */ + LSS_INLINE off_t LSS_NAME(lseek)(int f, off_t o, int w) { + _LSS_BODY(3, off_t, lseek, off_t, LSS_SYSCALL_ARG(f), (uint64_t)(o), + LSS_SYSCALL_ARG(w)); + } + #else + LSS_INLINE _syscall3(off_t, lseek, int, f, + off_t, o, int, w) + #endif + LSS_INLINE _syscall2(int, munmap, void*, s, + size_t, l) + LSS_INLINE _syscall6(long, move_pages, pid_t, p, + unsigned long, n, void **,g, int *, d, + int *, s, int, f) + LSS_INLINE _syscall3(int, mprotect, const void *,a, + size_t, l, int, p) + LSS_INLINE _syscall5(void*, _mremap, void*, o, + size_t, os, size_t, ns, + unsigned long, f, void *, a) + #if defined(__NR_open) + // open is polyfilled below when not available. + LSS_INLINE _syscall3(int, open, const char*, p, + int, f, int, m) + #endif + #if defined(__NR_poll) + // poll is polyfilled below when not available. + LSS_INLINE _syscall3(int, poll, struct kernel_pollfd*, u, + unsigned int, n, int, t) + #endif + #if defined(__NR_ppoll) + LSS_INLINE _syscall5(int, ppoll, struct kernel_pollfd *, u, + unsigned int, n, const struct kernel_timespec *, t, + const struct kernel_sigset_t *, sigmask, size_t, s) + #endif + LSS_INLINE _syscall5(int, prctl, int, option, + unsigned long, arg2, + unsigned long, arg3, + unsigned long, arg4, + unsigned long, arg5) + LSS_INLINE _syscall4(long, ptrace, int, r, + pid_t, p, void *, a, void *, d) + #if defined(__NR_quotactl) + // Defined on x86_64 / i386 only + LSS_INLINE _syscall4(int, quotactl, int, cmd, const char *, special, + int, id, caddr_t, addr) + #endif + LSS_INLINE _syscall3(ssize_t, read, int, f, + void *, b, size_t, c) + #if defined(__NR_readlink) + // readlink is polyfilled below when not available. + LSS_INLINE _syscall3(int, readlink, const char*, p, + char*, b, size_t, s) + #endif + #if defined(__NR_readlinkat) + LSS_INLINE _syscall4(int, readlinkat, int, d, const char *, p, char *, b, + size_t, s) + #endif + LSS_INLINE _syscall4(int, rt_sigaction, int, s, + const struct kernel_sigaction*, a, + struct kernel_sigaction*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s, + size_t, c) + LSS_INLINE _syscall4(int, rt_sigprocmask, int, h, + const struct kernel_sigset_t*, s, + struct kernel_sigset_t*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigsuspend, + const struct kernel_sigset_t*, s, size_t, c) + LSS_INLINE _syscall4(int, rt_sigtimedwait, const struct kernel_sigset_t*, s, + siginfo_t*, i, const struct timespec*, t, size_t, c) + LSS_INLINE _syscall3(int, sched_getaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall3(int, sched_setaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall0(int, sched_yield) + LSS_INLINE _syscall1(long, set_tid_address, int *, t) + LSS_INLINE _syscall1(int, setfsgid, gid_t, g) + LSS_INLINE _syscall1(int, setfsuid, uid_t, u) + LSS_INLINE _syscall1(int, setuid, uid_t, u) + LSS_INLINE _syscall1(int, setgid, gid_t, g) + LSS_INLINE _syscall2(int, setpgid, pid_t, p, + pid_t, g) + LSS_INLINE _syscall3(int, setpriority, int, a, + int, b, int, p) + LSS_INLINE _syscall3(int, setresgid, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, setresuid, uid_t, r, + uid_t, e, uid_t, s) + LSS_INLINE _syscall2(int, setrlimit, int, r, + const struct kernel_rlimit*, l) + LSS_INLINE _syscall0(pid_t, setsid) + LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s, + const stack_t*, o) + #if defined(__NR_sigreturn) + LSS_INLINE _syscall1(int, sigreturn, unsigned long, u) + #endif + #if defined(__NR_stat) + // stat and lstat are polyfilled below when not available. + LSS_INLINE _syscall2(int, stat, const char*, f, + struct kernel_stat*, b) + #endif + #if defined(__NR_lstat) + LSS_INLINE _syscall2(int, lstat, const char*, f, + struct kernel_stat*, b) + #endif + LSS_INLINE _syscall2(int, statfs, const char*, f, + struct kernel_statfs*, b) + LSS_INLINE _syscall3(int, tgkill, pid_t, p, + pid_t, t, int, s) + LSS_INLINE _syscall2(int, tkill, pid_t, p, + int, s) + #if defined(__NR_unlink) + // unlink is polyfilled below when not available. + LSS_INLINE _syscall1(int, unlink, const char*, f) + #endif + LSS_INLINE _syscall3(ssize_t, write, int, f, + const void *, b, size_t, c) + LSS_INLINE _syscall3(ssize_t, writev, int, f, + const struct kernel_iovec*, v, size_t, c) + #if defined(__NR_getcpu) + LSS_INLINE _syscall3(long, getcpu, unsigned *, cpu, + unsigned *, node, void *, unused) + #endif + #if defined(__x86_64__) || defined(__e2k__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) + LSS_INLINE _syscall3(int, recvmsg, int, s, + struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall3(int, sendmsg, int, s, + const struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall6(int, sendto, int, s, + const void*, m, size_t, l, + int, f, + const struct kernel_sockaddr*, a, int, t) + LSS_INLINE _syscall2(int, shutdown, int, s, + int, h) + LSS_INLINE _syscall3(int, socket, int, d, + int, t, int, p) + LSS_INLINE _syscall4(int, socketpair, int, d, + int, t, int, p, int*, s) + #endif + #if defined(__NR_fadvise64) + #if defined(__x86_64__) + /* Need to make sure loff_t isn't truncated to 32-bits under x32. */ + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, loff_t len, + int advice) { + LSS_BODY(4, int, fadvise64, LSS_SYSCALL_ARG(fd), (uint64_t)(offset), + (uint64_t)(len), LSS_SYSCALL_ARG(advice)); + } + #else + LSS_INLINE _syscall4(int, fadvise64, + int, fd, loff_t, offset, loff_t, len, int, advice) + #endif + #elif defined(__i386__) + #define __NR__fadvise64_64 __NR_fadvise64_64 + LSS_INLINE _syscall6(int, _fadvise64_64, int, fd, + unsigned, offset_lo, unsigned, offset_hi, + unsigned, len_lo, unsigned, len_hi, + int, advice) + + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, + loff_t len, int advice) { + return LSS_NAME(_fadvise64_64)(fd, + (unsigned)offset, (unsigned)(offset >>32), + (unsigned)len, (unsigned)(len >> 32), + advice); + } + + #elif defined(__s390__) && !defined(__s390x__) + #define __NR__fadvise64_64 __NR_fadvise64_64 + struct kernel_fadvise64_64_args { + int fd; + long long offset; + long long len; + int advice; + }; + + LSS_INLINE _syscall1(int, _fadvise64_64, + struct kernel_fadvise64_64_args *args) + + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, + loff_t len, int advice) { + struct kernel_fadvise64_64_args args = { fd, offset, len, advice }; + return LSS_NAME(_fadvise64_64)(&args); + } + #endif + #if defined(__NR_fallocate) + #if defined(__x86_64__) + /* Need to make sure loff_t isn't truncated to 32-bits under x32. */ + LSS_INLINE int LSS_NAME(fallocate)(int f, int mode, loff_t offset, + loff_t len) { + LSS_BODY(4, int, fallocate, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(mode), + (uint64_t)(offset), (uint64_t)(len)); + } + #elif (defined(__i386__) || (defined(__s390__) && !defined(__s390x__)) \ + || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) \ + || (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) \ + || defined(__PPC__)) + #define __NR__fallocate __NR_fallocate + LSS_INLINE _syscall6(int, _fallocate, int, fd, + int, mode, + unsigned, offset_lo, unsigned, offset_hi, + unsigned, len_lo, unsigned, len_hi) + + LSS_INLINE int LSS_NAME(fallocate)(int fd, int mode, + loff_t offset, loff_t len) { + union { loff_t off; unsigned w[2]; } o = { offset }, l = { len }; + return LSS_NAME(_fallocate)(fd, mode, o.w[0], o.w[1], l.w[0], l.w[1]); + } + #else + LSS_INLINE _syscall4(int, fallocate, + int, f, int, mode, loff_t, offset, loff_t, len) + #endif + #endif + #if defined(__NR_getrandom) + LSS_INLINE _syscall3(ssize_t, getrandom, void*, buffer, size_t, length, + unsigned int, flags) + #endif + #if defined(__NR_newfstatat) + LSS_INLINE _syscall4(int, newfstatat, int, d, + const char *, p, + struct kernel_stat*, b, int, f) + #endif + #if defined(__x86_64__) || defined(__s390x__) + LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid, + gid_t *egid, + gid_t *sgid) { + return LSS_NAME(getresgid)(rgid, egid, sgid); + } + + LSS_INLINE int LSS_NAME(getresuid32)(uid_t *ruid, + uid_t *euid, + uid_t *suid) { + return LSS_NAME(getresuid)(ruid, euid, suid); + } + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + return LSS_NAME(setfsgid)(gid); + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + return LSS_NAME(setfsuid)(uid); + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + return LSS_NAME(setresgid)(rgid, egid, sgid); + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + return LSS_NAME(setresuid)(ruid, euid, suid); + } + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + #if defined(__x86_64__) + /* On x86_64, the kernel requires us to always set our own + * SA_RESTORER in order to be able to return from a signal handler. + * This function must have a "magic" signature that the "gdb" + * (and maybe the kernel?) can recognize. + */ + if (act != NULL && !(act->sa_flags & SA_RESTORER)) { + struct kernel_sigaction a = *act; + a.sa_flags |= SA_RESTORER; + a.sa_restorer = LSS_NAME(restore_rt)(); + return LSS_NAME(rt_sigaction)(signum, &a, oldact, + (KERNEL_NSIG+7)/8); + } else + #endif + return LSS_NAME(rt_sigaction)(signum, act, oldact, + (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__NR_rt_sigprocmask) + LSS_INLINE int LSS_NAME(sigprocmask)(int how, + const struct kernel_sigset_t *set, + struct kernel_sigset_t *oldset) { + return LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__NR_rt_sigtimedwait) + LSS_INLINE int LSS_NAME(sigtimedwait)(const struct kernel_sigset_t *set, + siginfo_t *info, + const struct timespec *timeout) { + return LSS_NAME(rt_sigtimedwait)(set, info, timeout, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__NR_wait4) + LSS_INLINE _syscall4(pid_t, wait4, pid_t, p, + int*, s, int, o, + struct kernel_rusage*, r) + #endif + #if defined(__NR_openat) + LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m) + #endif + #if defined(__NR_unlinkat) + LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f) + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + (defined(__s390__) && !defined(__s390x__)) + #define __NR__getresgid32 __NR_getresgid32 + #define __NR__getresuid32 __NR_getresuid32 + #define __NR__setfsgid32 __NR_setfsgid32 + #define __NR__setfsuid32 __NR_setfsuid32 + #define __NR__setresgid32 __NR_setresgid32 + #define __NR__setresuid32 __NR_setresuid32 +#if defined(__ARM_EABI__) + LSS_INLINE _syscall2(int, ugetrlimit, int, r, + struct kernel_rlimit*, l) +#endif + LSS_INLINE _syscall3(int, _getresgid32, gid_t *, r, + gid_t *, e, gid_t *, s) + LSS_INLINE _syscall3(int, _getresuid32, uid_t *, r, + uid_t *, e, uid_t *, s) + LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f) + LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f) + LSS_INLINE _syscall3(int, _setresgid32, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, _setresuid32, uid_t, r, + uid_t, e, uid_t, s) + + LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid, + gid_t *egid, + gid_t *sgid) { + int rc; + if ((rc = LSS_NAME(_getresgid32)(rgid, egid, sgid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((rgid == NULL) || (egid == NULL) || (sgid == NULL)) { + return EFAULT; + } + // Clear the high bits first, since getresgid only sets 16 bits + *rgid = *egid = *sgid = 0; + rc = LSS_NAME(getresgid)(rgid, egid, sgid); + } + return rc; + } + + LSS_INLINE int LSS_NAME(getresuid32)(uid_t *ruid, + uid_t *euid, + uid_t *suid) { + int rc; + if ((rc = LSS_NAME(_getresuid32)(ruid, euid, suid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((ruid == NULL) || (euid == NULL) || (suid == NULL)) { + return EFAULT; + } + // Clear the high bits first, since getresuid only sets 16 bits + *ruid = *euid = *suid = 0; + rc = LSS_NAME(getresuid)(ruid, euid, suid); + } + return rc; + } + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + int rc; + if ((rc = LSS_NAME(_setfsgid32)(gid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)gid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsgid)(gid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + int rc; + if ((rc = LSS_NAME(_setfsuid32)(uid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)uid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsuid)(uid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + int rc; + if ((rc = LSS_NAME(_setresgid32)(rgid, egid, sgid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)rgid & ~0xFFFFu || + (unsigned int)egid & ~0xFFFFu || + (unsigned int)sgid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresgid)(rgid, egid, sgid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + int rc; + if ((rc = LSS_NAME(_setresuid32)(ruid, euid, suid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)ruid & ~0xFFFFu || + (unsigned int)euid & ~0xFFFFu || + (unsigned int)suid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresuid)(ruid, euid, suid); + } + } + return rc; + } + #endif + LSS_INLINE int LSS_NAME(sigemptyset)(struct kernel_sigset_t *set) { + memset(&set->sig, 0, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigfillset)(struct kernel_sigset_t *set) { + memset(&set->sig, -1, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigaddset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + |= 1UL << ((signum - 1) % (8*sizeof(set->sig[0]))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigdelset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + &= ~(1UL << ((signum - 1) % (8*sizeof(set->sig[0])))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigismember)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + return !!(set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] & + (1UL << ((signum - 1) % (8*sizeof(set->sig[0]))))); + } + } + #if defined(__i386__) || \ + defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + defined(__PPC__) || \ + (defined(__s390__) && !defined(__s390x__)) || defined(__e2k__) + #define __NR__sigaction __NR_sigaction + #define __NR__sigpending __NR_sigpending + #define __NR__sigsuspend __NR_sigsuspend + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, fstat64, int, f, + struct kernel_stat64 *, b) + LSS_INLINE _syscall5(int, _llseek, uint, fd, + unsigned long, hi, unsigned long, lo, + loff_t *, res, uint, wh) +#if defined(__s390__) && !defined(__s390x__) + /* On s390, mmap2() arguments are passed in memory. */ + LSS_INLINE void* LSS_NAME(_mmap2)(void *s, size_t l, int p, int f, int d, + off_t o) { + unsigned long buf[6] = { (unsigned long) s, (unsigned long) l, + (unsigned long) p, (unsigned long) f, + (unsigned long) d, (unsigned long) o }; + LSS_REG(2, buf); + LSS_BODY(void*, mmap2, "0"(__r2)); + } +#else + #define __NR__mmap2 __NR_mmap2 + LSS_INLINE _syscall6(void*, _mmap2, void*, s, + size_t, l, int, p, + int, f, int, d, + off_t, o) +#endif + LSS_INLINE _syscall3(int, _sigaction, int, s, + const struct kernel_old_sigaction*, a, + struct kernel_old_sigaction*, o) + LSS_INLINE _syscall1(int, _sigpending, unsigned long*, s) + #ifdef __PPC__ + LSS_INLINE _syscall1(int, _sigsuspend, unsigned long, s) + #else + LSS_INLINE _syscall3(int, _sigsuspend, const void*, a, + int, b, + unsigned long, s) + #endif + LSS_INLINE _syscall2(int, stat64, const char *, p, + struct kernel_stat64 *, b) + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + int old_errno = LSS_ERRNO; + int rc; + struct kernel_sigaction a; + if (act != NULL) { + a = *act; + #ifdef __i386__ + /* On i386, the kernel requires us to always set our own + * SA_RESTORER when using realtime signals. Otherwise, it does not + * know how to return from a signal handler. This function must have + * a "magic" signature that the "gdb" (and maybe the kernel?) can + * recognize. + * Apparently, a SA_RESTORER is implicitly set by the kernel, when + * using non-realtime signals. + * + * TODO: Test whether ARM needs a restorer + */ + if (!(a.sa_flags & SA_RESTORER)) { + a.sa_flags |= SA_RESTORER; + a.sa_restorer = (a.sa_flags & SA_SIGINFO) + ? LSS_NAME(restore_rt)() : LSS_NAME(restore)(); + } + #endif + } + rc = LSS_NAME(rt_sigaction)(signum, act ? &a : act, oldact, + (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + struct kernel_old_sigaction oa, ooa, *ptr_a = &oa, *ptr_oa = &ooa; + if (!act) { + ptr_a = NULL; + } else { + oa.sa_handler_ = act->sa_handler_; + memcpy(&oa.sa_mask, &act->sa_mask, sizeof(oa.sa_mask)); + #ifndef __mips__ + oa.sa_restorer = act->sa_restorer; + #endif + oa.sa_flags = act->sa_flags; + } + if (!oldact) { + ptr_oa = NULL; + } + LSS_ERRNO = old_errno; + rc = LSS_NAME(_sigaction)(signum, ptr_a, ptr_oa); + if (rc == 0 && oldact) { + if (act) { + memcpy(oldact, act, sizeof(*act)); + } else { + memset(oldact, 0, sizeof(*oldact)); + } + oldact->sa_handler_ = ptr_oa->sa_handler_; + oldact->sa_flags = ptr_oa->sa_flags; + memcpy(&oldact->sa_mask, &ptr_oa->sa_mask, sizeof(ptr_oa->sa_mask)); + #ifndef __mips__ + oldact->sa_restorer = ptr_oa->sa_restorer; + #endif + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + int old_errno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = old_errno; + LSS_NAME(sigemptyset)(set); + rc = LSS_NAME(_sigpending)(&set->sig[0]); + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + int olderrno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = olderrno; + rc = LSS_NAME(_sigsuspend)( + #ifndef __PPC__ + set, 0, + #endif + set->sig[0]); + } + return rc; + } + #endif + #if defined(__i386__) || \ + defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + defined(__PPC__) || \ + (defined(__s390__) && !defined(__s390x__)) + /* On these architectures, implement mmap() with mmap2(). */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + if (o % 4096) { + LSS_ERRNO = EINVAL; + return (void *) -1; + } + return LSS_NAME(_mmap2)(s, l, p, f, d, (o / 4096)); + } + #elif defined(__s390x__) + /* On s390x, mmap() arguments are passed in memory. */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + unsigned long buf[6] = { (unsigned long) s, (unsigned long) l, + (unsigned long) p, (unsigned long) f, + (unsigned long) d, (unsigned long) o }; + LSS_REG(2, buf); + LSS_BODY(void*, mmap, "0"(__r2)); + } + #elif defined(__x86_64__) + /* Need to make sure __off64_t isn't truncated to 32-bits under x32. */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + LSS_BODY(6, void*, mmap, LSS_SYSCALL_ARG(s), LSS_SYSCALL_ARG(l), + LSS_SYSCALL_ARG(p), LSS_SYSCALL_ARG(f), + LSS_SYSCALL_ARG(d), (uint64_t)(o)); + } + #else + /* Remaining 64-bit architectures. */ + LSS_INLINE _syscall6(void*, mmap, void*, addr, size_t, length, int, prot, + int, flags, int, fd, int64_t, offset) + #endif + #if defined(__PPC__) + #undef LSS_SC_LOADARGS_0 + #define LSS_SC_LOADARGS_0(dummy...) + #undef LSS_SC_LOADARGS_1 + #define LSS_SC_LOADARGS_1(arg1) \ + __sc_4 = (unsigned long) (arg1) + #undef LSS_SC_LOADARGS_2 + #define LSS_SC_LOADARGS_2(arg1, arg2) \ + LSS_SC_LOADARGS_1(arg1); \ + __sc_5 = (unsigned long) (arg2) + #undef LSS_SC_LOADARGS_3 + #define LSS_SC_LOADARGS_3(arg1, arg2, arg3) \ + LSS_SC_LOADARGS_2(arg1, arg2); \ + __sc_6 = (unsigned long) (arg3) + #undef LSS_SC_LOADARGS_4 + #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4) \ + LSS_SC_LOADARGS_3(arg1, arg2, arg3); \ + __sc_7 = (unsigned long) (arg4) + #undef LSS_SC_LOADARGS_5 + #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5) \ + LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4); \ + __sc_8 = (unsigned long) (arg5) + #undef LSS_SC_BODY + #define LSS_SC_BODY(nr, type, opt, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall; \ + register unsigned long __sc_3 __asm__ ("r3") = opt; \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + LSS_SC_LOADARGS_##nr(args); \ + __asm__ __volatile__ \ + ("stwu 1, -48(1)\n\t" \ + "stw 4, 20(1)\n\t" \ + "stw 5, 24(1)\n\t" \ + "stw 6, 28(1)\n\t" \ + "stw 7, 32(1)\n\t" \ + "stw 8, 36(1)\n\t" \ + "addi 4, 1, 20\n\t" \ + "sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + LSS_SC_BODY(3, ssize_t, 17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + LSS_SC_BODY(3, ssize_t, 16, s, msg, flags); + } + + // TODO(csilvers): why is this ifdef'ed out? +#if 0 + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + LSS_BODY(6, ssize_t, 11, s, buf, len, flags, to, tolen); + } +#endif + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + LSS_SC_BODY(2, int, 13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + LSS_SC_BODY(3, int, 1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + LSS_SC_BODY(4, int, 8, d, type, protocol, sv); + } + #endif + #if defined(__ARM_EABI__) || defined (__aarch64__) + LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg, + int, flags) + LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*, + msg, int, flags) + LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t,len, + int, flags, const struct kernel_sockaddr*, to, + unsigned int, tolen) + LSS_INLINE _syscall2(int, shutdown, int, s, int, how) + LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol) + LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol, + int*, sv) + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + defined(__s390__) + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, _socketcall, int, c, + va_list, a) + LSS_INLINE int LSS_NAME(socketcall)(int op, ...) { + int rc; + va_list ap; + va_start(ap, op); + rc = LSS_NAME(_socketcall)(op, ap); + va_end(ap); + return rc; + } + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen); + } + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + return LSS_NAME(socketcall)(13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + return LSS_NAME(socketcall)(1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + return LSS_NAME(socketcall)(8, d, type, protocol, sv); + } + #endif + #if defined(__NR_fstatat64) + LSS_INLINE _syscall4(int, fstatat64, int, d, + const char *, p, + struct kernel_stat64 *, b, int, f) + #endif + #if defined(__NR_waitpid) + // waitpid is polyfilled below when not available. + LSS_INLINE _syscall3(pid_t, waitpid, pid_t, p, + int*, s, int, o) + #endif + #if defined(__mips__) + /* sys_pipe() on MIPS has non-standard calling conventions, as it returns + * both file handles through CPU registers. + */ + LSS_INLINE int LSS_NAME(pipe)(int *p) { + register unsigned long __v0 __asm__("$2") = __NR_pipe; + register unsigned long __v1 __asm__("$3"); + register unsigned long __r7 __asm__("$7"); + __asm__ __volatile__ ("syscall\n" + : "=r"(__v0), "=r"(__v1), "=r" (__r7) + : "0"(__v0) + : "$8", "$9", "$10", "$11", "$12", + "$13", "$14", "$15", "$24", "$25", "memory"); + if (__r7) { + unsigned long __errnovalue = __v0; + LSS_ERRNO = __errnovalue; + return -1; + } else { + p[0] = __v0; + p[1] = __v1; + return 0; + } + } + #elif defined(__NR_pipe) + // pipe is polyfilled below when not available. + LSS_INLINE _syscall1(int, pipe, int *, p) + #endif + #if defined(__NR_pipe2) + LSS_INLINE _syscall2(int, pipe2, int *, pipefd, int, flags) + #endif + /* TODO(csilvers): see if ppc can/should support this as well */ + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) || \ + (defined(__s390__) && !defined(__s390x__)) + #define __NR__statfs64 __NR_statfs64 + #define __NR__fstatfs64 __NR_fstatfs64 + LSS_INLINE _syscall3(int, _statfs64, const char*, p, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE _syscall3(int, _fstatfs64, int, f, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE int LSS_NAME(statfs64)(const char *p, + struct kernel_statfs64 *b) { + return LSS_NAME(_statfs64)(p, sizeof(*b), b); + } + LSS_INLINE int LSS_NAME(fstatfs64)(int f,struct kernel_statfs64 *b) { + return LSS_NAME(_fstatfs64)(f, sizeof(*b), b); + } + #endif + + LSS_INLINE int LSS_NAME(execv)(const char *path, const char *const argv[]) { + extern char **environ; + return LSS_NAME(execve)(path, argv, (const char *const *)environ); + } + + LSS_INLINE pid_t LSS_NAME(gettid)(void) { + pid_t tid = LSS_NAME(_gettid)(); + if (tid != -1) { + return tid; + } + return LSS_NAME(getpid)(); + } + + LSS_INLINE void *LSS_NAME(mremap)(void *old_address, size_t old_size, + size_t new_size, int flags, ...) { + va_list ap; + void *new_address, *rc; + va_start(ap, flags); + new_address = va_arg(ap, void *); + rc = LSS_NAME(_mremap)(old_address, old_size, new_size, + flags, new_address); + va_end(ap); + return rc; + } + + LSS_INLINE int LSS_NAME(ptrace_detach)(pid_t pid) { + /* PTRACE_DETACH can sometimes forget to wake up the tracee and it + * then sends job control signals to the real parent, rather than to + * the tracer. We reduce the risk of this happening by starting a + * whole new time slice, and then quickly sending a SIGCONT signal + * right after detaching from the tracee. + * + * We use tkill to ensure that we only issue a wakeup for the thread being + * detached. Large multi threaded apps can take a long time in the kernel + * processing SIGCONT. + */ + int rc, err; + LSS_NAME(sched_yield)(); + rc = LSS_NAME(ptrace)(PTRACE_DETACH, pid, (void *)0, (void *)0); + err = LSS_ERRNO; + LSS_NAME(tkill)(pid, SIGCONT); + /* Old systems don't have tkill */ + if (LSS_ERRNO == ENOSYS) + LSS_NAME(kill)(pid, SIGCONT); + LSS_ERRNO = err; + return rc; + } + + LSS_INLINE int LSS_NAME(raise)(int sig) { + return LSS_NAME(kill)(LSS_NAME(getpid)(), sig); + } + + LSS_INLINE int LSS_NAME(setpgrp)(void) { + return LSS_NAME(setpgid)(0, 0); + } + + #if defined(__x86_64__) + /* Need to make sure loff_t isn't truncated to 32-bits under x32. */ + LSS_INLINE ssize_t LSS_NAME(pread64)(int f, void *b, size_t c, loff_t o) { + LSS_BODY(4, ssize_t, pread64, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(b), + LSS_SYSCALL_ARG(c), (uint64_t)(o)); + } + + LSS_INLINE ssize_t LSS_NAME(pwrite64)(int f, const void *b, size_t c, + loff_t o) { + LSS_BODY(4, ssize_t, pwrite64, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(b), + LSS_SYSCALL_ARG(c), (uint64_t)(o)); + } + + LSS_INLINE int LSS_NAME(readahead)(int f, loff_t o, unsigned c) { + LSS_BODY(3, int, readahead, LSS_SYSCALL_ARG(f), (uint64_t)(o), + LSS_SYSCALL_ARG(c)); + } + #elif defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64 + LSS_INLINE _syscall4(ssize_t, pread64, int, f, + void *, b, size_t, c, + loff_t, o) + LSS_INLINE _syscall4(ssize_t, pwrite64, int, f, + const void *, b, size_t, c, + loff_t, o) + LSS_INLINE _syscall3(int, readahead, int, f, + loff_t, o, unsigned, c) + #else + #define __NR__pread64 __NR_pread64 + #define __NR__pwrite64 __NR_pwrite64 + #define __NR__readahead __NR_readahead + #if defined(__ARM_EABI__) || defined(__mips__) + /* On ARM and MIPS, a 64-bit parameter has to be in an even-odd register + * pair. Hence these calls ignore their fourth argument (r3) so that their + * fifth and sixth make such a pair (r4,r5). + */ + #define LSS_LLARG_PAD 0, + LSS_INLINE _syscall6(ssize_t, _pread64, int, f, + void *, b, size_t, c, + unsigned, skip, unsigned, o1, unsigned, o2) + LSS_INLINE _syscall6(ssize_t, _pwrite64, int, f, + const void *, b, size_t, c, + unsigned, skip, unsigned, o1, unsigned, o2) + LSS_INLINE _syscall5(int, _readahead, int, f, + unsigned, skip, + unsigned, o1, unsigned, o2, size_t, c) + #else + #define LSS_LLARG_PAD + LSS_INLINE _syscall5(ssize_t, _pread64, int, f, + void *, b, size_t, c, unsigned, o1, + unsigned, o2) + LSS_INLINE _syscall5(ssize_t, _pwrite64, int, f, + const void *, b, size_t, c, unsigned, o1, + long, o2) + LSS_INLINE _syscall4(int, _readahead, int, f, + unsigned, o1, unsigned, o2, size_t, c) + #endif + /* We force 64bit-wide parameters onto the stack, then access each + * 32-bit component individually. This guarantees that we build the + * correct parameters independent of the native byte-order of the + * underlying architecture. + */ + LSS_INLINE ssize_t LSS_NAME(pread64)(int fd, void *buf, size_t count, + loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pread64)(fd, buf, count, + LSS_LLARG_PAD o.arg[0], o.arg[1]); + } + LSS_INLINE ssize_t LSS_NAME(pwrite64)(int fd, const void *buf, + size_t count, loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pwrite64)(fd, buf, count, + LSS_LLARG_PAD o.arg[0], o.arg[1]); + } + LSS_INLINE int LSS_NAME(readahead)(int fd, loff_t off, int len) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_readahead)(fd, LSS_LLARG_PAD o.arg[0], o.arg[1], len); + } + #endif +#endif + +/* + * Polyfills for deprecated syscalls. + */ + +#if !defined(__NR_dup2) + LSS_INLINE int LSS_NAME(dup2)(int s, int d) { + return LSS_NAME(dup3)(s, d, 0); + } +#endif + +#if !defined(__NR_open) + LSS_INLINE int LSS_NAME(open)(const char *pathname, int flags, int mode) { + return LSS_NAME(openat)(AT_FDCWD, pathname, flags, mode); + } +#endif + +#if !defined(__NR_unlink) + LSS_INLINE int LSS_NAME(unlink)(const char *pathname) { + return LSS_NAME(unlinkat)(AT_FDCWD, pathname, 0); + } +#endif + +#if !defined(__NR_readlink) + LSS_INLINE int LSS_NAME(readlink)(const char *pathname, char *buffer, + size_t size) { + return LSS_NAME(readlinkat)(AT_FDCWD, pathname, buffer, size); + } +#endif + +#if !defined(__NR_pipe) + LSS_INLINE int LSS_NAME(pipe)(int *pipefd) { + return LSS_NAME(pipe2)(pipefd, 0); + } +#endif + +#if !defined(__NR_poll) + LSS_INLINE int LSS_NAME(poll)(struct kernel_pollfd *fds, unsigned int nfds, + int timeout) { + struct kernel_timespec timeout_ts; + struct kernel_timespec *timeout_ts_p = NULL; + + if (timeout >= 0) { + timeout_ts.tv_sec = timeout / 1000; + timeout_ts.tv_nsec = (timeout % 1000) * 1000000; + timeout_ts_p = &timeout_ts; + } + return LSS_NAME(ppoll)(fds, nfds, timeout_ts_p, NULL, 0); + } +#endif + +#if !defined(__NR_stat) + LSS_INLINE int LSS_NAME(stat)(const char *pathname, + struct kernel_stat *buf) { + return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, 0); + } +#endif +#if !defined(__NR_lstat) + LSS_INLINE int LSS_NAME(lstat)(const char *pathname, + struct kernel_stat *buf) { + return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, AT_SYMLINK_NOFOLLOW); + } +#endif + +#if !defined(__NR_waitpid) + LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options) { + return LSS_NAME(wait4)(pid, status, options, 0); + } +#endif + +#if !defined(__NR_fork) +// TODO: define this in an arch-independant way instead of inlining the clone +// syscall body. + +# if defined(__aarch64__) + LSS_INLINE pid_t LSS_NAME(fork)(void) { + // No fork syscall on aarch64 - implement by means of the clone syscall. + // Note that this does not reset glibc's cached view of the PID/TID, so + // some glibc interfaces might go wrong in the forked subprocess. + int flags = SIGCHLD; + void *child_stack = NULL; + void *parent_tidptr = NULL; + void *newtls = NULL; + void *child_tidptr = NULL; + + LSS_REG(0, flags); + LSS_REG(1, child_stack); + LSS_REG(2, parent_tidptr); + LSS_REG(3, newtls); + LSS_REG(4, child_tidptr); + LSS_BODY(pid_t, clone, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), + "r"(__r4)); + } +# elif defined(__x86_64__) + LSS_INLINE pid_t LSS_NAME(fork)(void) { + // Android disallows the fork syscall on x86_64 - implement by means of the + // clone syscall as above for aarch64. + int flags = SIGCHLD; + void *child_stack = NULL; + void *parent_tidptr = NULL; + void *newtls = NULL; + void *child_tidptr = NULL; + + LSS_BODY(5, pid_t, clone, LSS_SYSCALL_ARG(flags), + LSS_SYSCALL_ARG(child_stack), LSS_SYSCALL_ARG(parent_tidptr), + LSS_SYSCALL_ARG(newtls), LSS_SYSCALL_ARG(child_tidptr)); + } +# else +# error missing fork polyfill for this architecture +# endif +#endif + +/* These restore the original values of these macros saved by the + * corresponding #pragma push_macro near the top of this file. */ +#pragma pop_macro("stat64") +#pragma pop_macro("fstat64") +#pragma pop_macro("lstat64") +#pragma pop_macro("pread64") +#pragma pop_macro("pwrite64") +#pragma pop_macro("getdents64") + +#if defined(__cplusplus) && !defined(SYS_CPLUSPLUS) +} +#endif + +#endif +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/LICENSE b/app/src/main/cpp/shadowhook/third_party/xdl/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c20d2c800e36fe7fd741303ea5d394f14be2d3d1 --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2023 HexHacking Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl.c new file mode 100644 index 0000000000000000000000000000000000000000..a707961bd0a6de8927e91e699ba6fa245d6e813d --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl.c @@ -0,0 +1,910 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2020-10-04. + +#include "xdl.h" + +#include <android/api-level.h> +#include <elf.h> +#include <fcntl.h> +#include <inttypes.h> +#include <link.h> +#include <pthread.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/auxv.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "xdl_iterate.h" +#include "xdl_linker.h" +#include "xdl_lzma.h" +#include "xdl_util.h" + +#ifndef __LP64__ +#define XDL_LIB_PATH "/system/lib" +#else +#define XDL_LIB_PATH "/system/lib64" +#endif + +#define XDL_DYNSYM_IS_EXPORT_SYM(shndx) (SHN_UNDEF != (shndx)) +#define XDL_SYMTAB_IS_EXPORT_SYM(shndx) \ + (SHN_UNDEF != (shndx) && !((shndx) >= SHN_LORESERVE && (shndx) <= SHN_HIRESERVE)) + +extern __attribute((weak)) unsigned long int getauxval(unsigned long int); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" + +typedef struct xdl { + char *pathname; + uintptr_t load_bias; + const ElfW(Phdr) *dlpi_phdr; + ElfW(Half) dlpi_phnum; + + struct xdl *next; // to next xdl obj for cache in xdl_addr() + void *linker_handle; // hold handle returned by xdl_linker_force_dlopen() + + // + // (1) for searching symbols from .dynsym + // + + bool dynsym_try_load; + ElfW(Sym) *dynsym; // .dynsym + const char *dynstr; // .dynstr + + // .hash (SYSV hash for .dynstr) + struct { + const uint32_t *buckets; + uint32_t buckets_cnt; + const uint32_t *chains; + uint32_t chains_cnt; + } sysv_hash; + + // .gnu.hash (GNU hash for .dynstr) + struct { + const uint32_t *buckets; + uint32_t buckets_cnt; + const uint32_t *chains; + uint32_t symoffset; + const ElfW(Addr) *bloom; + uint32_t bloom_cnt; + uint32_t bloom_shift; + } gnu_hash; + + // + // (2) for searching symbols from .symtab + // + + bool symtab_try_load; + uintptr_t base; + + ElfW(Sym) *symtab; // .symtab + size_t symtab_cnt; + char *strtab; // .strtab + size_t strtab_sz; +} xdl_t; + +#pragma clang diagnostic pop + +// load from memory +static int xdl_dynsym_load(xdl_t *self) { + // find the dynamic segment + ElfW(Dyn) *dynamic = NULL; + for (size_t i = 0; i < self->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]); + if (PT_DYNAMIC == phdr->p_type) { + dynamic = (ElfW(Dyn) *)(self->load_bias + phdr->p_vaddr); + break; + } + } + if (NULL == dynamic) return -1; + + // iterate the dynamic segment + for (ElfW(Dyn) *entry = dynamic; entry && entry->d_tag != DT_NULL; entry++) { + switch (entry->d_tag) { + case DT_SYMTAB: //.dynsym + self->dynsym = (ElfW(Sym) *)(self->load_bias + entry->d_un.d_ptr); + break; + case DT_STRTAB: //.dynstr + self->dynstr = (const char *)(self->load_bias + entry->d_un.d_ptr); + break; + case DT_HASH: //.hash + self->sysv_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0]; + self->sysv_hash.chains_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1]; + self->sysv_hash.buckets = &(((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]); + self->sysv_hash.chains = &(self->sysv_hash.buckets[self->sysv_hash.buckets_cnt]); + break; + case DT_GNU_HASH: //.gnu.hash + self->gnu_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0]; + self->gnu_hash.symoffset = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1]; + self->gnu_hash.bloom_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]; + self->gnu_hash.bloom_shift = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[3]; + self->gnu_hash.bloom = (const ElfW(Addr) *)(self->load_bias + entry->d_un.d_ptr + 16); + self->gnu_hash.buckets = (const uint32_t *)(&(self->gnu_hash.bloom[self->gnu_hash.bloom_cnt])); + self->gnu_hash.chains = (const uint32_t *)(&(self->gnu_hash.buckets[self->gnu_hash.buckets_cnt])); + break; + default: + break; + } + } + + if (NULL == self->dynsym || NULL == self->dynstr || + (0 == self->sysv_hash.buckets_cnt && 0 == self->gnu_hash.buckets_cnt)) { + self->dynsym = NULL; + self->dynstr = NULL; + self->sysv_hash.buckets_cnt = 0; + self->gnu_hash.buckets_cnt = 0; + return -1; + } + + return 0; +} + +static void *xdl_read_file_to_heap(int file_fd, size_t file_sz, size_t data_offset, size_t data_len) { + if (0 == data_len) return NULL; + if (data_offset >= file_sz) return NULL; + if (data_offset + data_len > file_sz) return NULL; + + if (data_offset != (size_t)lseek(file_fd, (off_t)data_offset, SEEK_SET)) return NULL; + + void *data = malloc(data_len); + if (NULL == data) return NULL; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-statement-expression" + if ((ssize_t)data_len != XDL_UTIL_TEMP_FAILURE_RETRY(read(file_fd, data, data_len))) +#pragma clang diagnostic pop + { + free(data); + return NULL; + } + + return data; +} + +static void *xdl_read_file_to_heap_by_section(int file_fd, size_t file_sz, ElfW(Shdr) *shdr) { + return xdl_read_file_to_heap(file_fd, file_sz, (size_t)shdr->sh_offset, shdr->sh_size); +} + +static void *xdl_read_memory_to_heap(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) { + if (0 == data_len) return NULL; + if (data_offset >= mem_sz) return NULL; + if (data_offset + data_len > mem_sz) return NULL; + + void *data = malloc(data_len); + if (NULL == data) return NULL; + + memcpy(data, (void *)((uintptr_t)mem + data_offset), data_len); + return data; +} + +static void *xdl_read_memory_to_heap_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) { + return xdl_read_memory_to_heap(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size); +} + +static void *xdl_get_memory(void *mem, size_t mem_sz, size_t data_offset, size_t data_len) { + if (0 == data_len) return NULL; + if (data_offset >= mem_sz) return NULL; + if (data_offset + data_len > mem_sz) return NULL; + + return (void *)((uintptr_t)mem + data_offset); +} + +static void *xdl_get_memory_by_section(void *mem, size_t mem_sz, ElfW(Shdr) *shdr) { + return xdl_get_memory(mem, mem_sz, (size_t)shdr->sh_offset, shdr->sh_size); +} + +// load from disk and memory +static int xdl_symtab_load_from_debugdata(xdl_t *self, int file_fd, size_t file_sz, + ElfW(Shdr) *shdr_debugdata) { + void *debugdata = NULL; + ElfW(Shdr) *shdrs = NULL; + int r = -1; + + // get zipped .gnu_debugdata + uint8_t *debugdata_zip = (uint8_t *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_debugdata); + if (NULL == debugdata_zip) return -1; + + // get unzipped .gnu_debugdata + size_t debugdata_sz; + if (0 != xdl_lzma_decompress(debugdata_zip, shdr_debugdata->sh_size, (uint8_t **)&debugdata, &debugdata_sz)) + goto end; + + // get ELF header + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)debugdata; + if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end; + + // get section headers + shdrs = (ElfW(Shdr) *)xdl_read_memory_to_heap(debugdata, debugdata_sz, (size_t)ehdr->e_shoff, + ehdr->e_shentsize * ehdr->e_shnum); + if (NULL == shdrs) goto end; + + // get .shstrtab + if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end; + char *shstrtab = (char *)xdl_get_memory_by_section(debugdata, debugdata_sz, shdrs + ehdr->e_shstrndx); + if (NULL == shstrtab) goto end; + + // find .symtab & .strtab + for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) { + char *shdr_name = shstrtab + shdr->sh_name; + + if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) { + // get & check associated .strtab section + if (shdr->sh_link >= ehdr->e_shnum) continue; + ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link; + if (SHT_STRTAB != shdr_strtab->sh_type) continue; + + // get .symtab & .strtab + ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr); + if (NULL == symtab) continue; + char *strtab = (char *)xdl_read_memory_to_heap_by_section(debugdata, debugdata_sz, shdr_strtab); + if (NULL == strtab) { + free(symtab); + continue; + } + + // OK + self->symtab = symtab; + self->symtab_cnt = shdr->sh_size / shdr->sh_entsize; + self->strtab = strtab; + self->strtab_sz = shdr_strtab->sh_size; + r = 0; + break; + } + } + +end: + free(debugdata_zip); + if (NULL != debugdata) free(debugdata); + if (NULL != shdrs) free(shdrs); + return r; +} + +// load from disk and memory +static int xdl_symtab_load(xdl_t *self) { + if ('[' == self->pathname[0]) return -1; + + int r = -1; + ElfW(Shdr) *shdrs = NULL; + char *shstrtab = NULL; + + // get base address + uintptr_t vaddr_min = UINTPTR_MAX; + for (size_t i = 0; i < self->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]); + if (PT_LOAD == phdr->p_type) { + if (vaddr_min > phdr->p_vaddr) vaddr_min = phdr->p_vaddr; + } + } + if (UINTPTR_MAX == vaddr_min) return -1; + self->base = self->load_bias + vaddr_min; + + // open file + int flags = O_RDONLY | O_CLOEXEC; + int file_fd; + if ('/' == self->pathname[0]) { + file_fd = open(self->pathname, flags); + } else { + char full_pathname[1024]; + // try the fast method + snprintf(full_pathname, sizeof(full_pathname), "%s/%s", XDL_LIB_PATH, self->pathname); + file_fd = open(full_pathname, flags); + if (file_fd < 0) { + // try the slow method + if (0 != xdl_iterate_get_full_pathname(self->base, full_pathname, sizeof(full_pathname))) return -1; + file_fd = open(full_pathname, flags); + } + } + if (file_fd < 0) return -1; + struct stat st; + if (0 != fstat(file_fd, &st)) goto end; + size_t file_sz = (size_t)st.st_size; + + // get ELF header + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)self->base; + if (0 == ehdr->e_shnum || ehdr->e_shentsize != sizeof(ElfW(Shdr))) goto end; + + // get section headers + shdrs = (ElfW(Shdr) *)xdl_read_file_to_heap(file_fd, file_sz, (size_t)ehdr->e_shoff, + ehdr->e_shentsize * ehdr->e_shnum); + if (NULL == shdrs) goto end; + + // get .shstrtab + if (SHN_UNDEF == ehdr->e_shstrndx || ehdr->e_shstrndx >= ehdr->e_shnum) goto end; + shstrtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdrs + ehdr->e_shstrndx); + if (NULL == shstrtab) goto end; + + // find .symtab & .strtab + for (ElfW(Shdr) *shdr = shdrs; shdr < shdrs + ehdr->e_shnum; shdr++) { + char *shdr_name = shstrtab + shdr->sh_name; + + if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) { + // get & check associated .strtab section + if (shdr->sh_link >= ehdr->e_shnum) continue; + ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link; + if (SHT_STRTAB != shdr_strtab->sh_type) continue; + + // get .symtab & .strtab + ElfW(Sym) *symtab = (ElfW(Sym) *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr); + if (NULL == symtab) continue; + char *strtab = (char *)xdl_read_file_to_heap_by_section(file_fd, file_sz, shdr_strtab); + if (NULL == strtab) { + free(symtab); + continue; + } + + // OK + self->symtab = symtab; + self->symtab_cnt = shdr->sh_size / shdr->sh_entsize; + self->strtab = strtab; + self->strtab_sz = shdr_strtab->sh_size; + r = 0; + break; + } else if (SHT_PROGBITS == shdr->sh_type && 0 == strcmp(".gnu_debugdata", shdr_name)) { + if (0 == xdl_symtab_load_from_debugdata(self, file_fd, file_sz, shdr)) { + // OK + r = 0; + break; + } + } + } + +end: + close(file_fd); + if (NULL != shdrs) free(shdrs); + if (NULL != shstrtab) free(shstrtab); + return r; +} + +static xdl_t *xdl_find_from_auxv(unsigned long type, const char *pathname) { + if (NULL == getauxval) return NULL; // API level < 18 + + uintptr_t val = (uintptr_t)getauxval(type); + if (0 == val) return NULL; + + // get base + uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val); + if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return NULL; + + // ELF info + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; + const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); + ElfW(Half) dlpi_phnum = ehdr->e_phnum; + + // get bias + uintptr_t min_vaddr = UINTPTR_MAX; + for (size_t i = 0; i < dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(dlpi_phdr[i]); + if (PT_LOAD == phdr->p_type) { + if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; + } + } + if (UINTPTR_MAX == min_vaddr || base < min_vaddr) return NULL; + uintptr_t load_bias = base - min_vaddr; + + // create xDL object + xdl_t *self; + if (NULL == (self = calloc(1, sizeof(xdl_t)))) return NULL; + if (NULL == (self->pathname = strdup(pathname))) { + free(self); + return NULL; + } + self->load_bias = load_bias; + self->dlpi_phdr = dlpi_phdr; + self->dlpi_phnum = dlpi_phnum; + self->dynsym_try_load = false; + self->symtab_try_load = false; + return self; +} + +static int xdl_find_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) { + (void)size; + + uintptr_t *pkg = (uintptr_t *)arg; + xdl_t **self = (xdl_t **)*pkg++; + const char *filename = (const char *)*pkg; + + // check load_bias + if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; + + // check pathname + if ('[' == filename[0]) { + if (0 != strcmp(info->dlpi_name, filename)) return 0; + } else if ('/' == filename[0]) { + if ('/' == info->dlpi_name[0]) { + if (0 != strcmp(info->dlpi_name, filename)) return 0; + } else { + if (!xdl_util_ends_with(filename, info->dlpi_name)) return 0; + } + } else { + if ('/' == info->dlpi_name[0]) { + if (!xdl_util_ends_with(info->dlpi_name, filename)) return 0; + } else { + if (0 != strcmp(info->dlpi_name, filename)) return 0; + } + } + + // found the target ELF + if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // return failed + if (NULL == ((*self)->pathname = strdup(info->dlpi_name))) { + free(*self); + *self = NULL; + return 1; // return failed + } + (*self)->load_bias = info->dlpi_addr; + (*self)->dlpi_phdr = info->dlpi_phdr; + (*self)->dlpi_phnum = info->dlpi_phnum; + (*self)->dynsym_try_load = false; + (*self)->symtab_try_load = false; + return 1; // return OK +} + +static xdl_t *xdl_find(const char *filename) { + // from auxv (linker, vDSO) + xdl_t *self = NULL; + if (xdl_util_ends_with(filename, XDL_UTIL_LINKER_BASENAME)) + self = xdl_find_from_auxv(AT_BASE, XDL_UTIL_LINKER_PATHNAME); + else if (xdl_util_ends_with(filename, XDL_UTIL_VDSO_BASENAME)) + self = xdl_find_from_auxv(AT_SYSINFO_EHDR, XDL_UTIL_VDSO_BASENAME); + + // from auxv (app_process) + const char *basename, *pathname; +#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ + if (xdl_util_get_api_level() < __ANDROID_API_L__) { + basename = XDL_UTIL_APP_PROCESS_BASENAME_K; + pathname = XDL_UTIL_APP_PROCESS_PATHNAME_K; + } else +#endif + { + basename = XDL_UTIL_APP_PROCESS_BASENAME; + pathname = XDL_UTIL_APP_PROCESS_PATHNAME; + } + if (xdl_util_ends_with(filename, basename)) self = xdl_find_from_auxv(AT_PHDR, pathname); + + if (NULL != self) return self; + + // from dl_iterate_phdr + uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)filename}; + xdl_iterate_phdr(xdl_find_iterate_cb, pkg, XDL_DEFAULT); + return self; +} + +static void *xdl_open_always_force(const char *filename) { + // always force dlopen() + void *linker_handle = xdl_linker_force_dlopen(filename); + if (NULL == linker_handle) return NULL; + + // find + xdl_t *self = xdl_find(filename); + if (NULL == self) + dlclose(linker_handle); + else + self->linker_handle = linker_handle; + + return (void *)self; +} + +static void *xdl_open_try_force(const char *filename) { + // find + xdl_t *self = xdl_find(filename); + if (NULL != self) return (void *)self; + + // try force dlopen() + void *linker_handle = xdl_linker_force_dlopen(filename); + if (NULL == linker_handle) return NULL; + + // find again + self = xdl_find(filename); + if (NULL == self) + dlclose(linker_handle); + else + self->linker_handle = linker_handle; + + return (void *)self; +} + +void *xdl_open(const char *filename, int flags) { + if (NULL == filename) return NULL; + + if (flags & XDL_ALWAYS_FORCE_LOAD) + return xdl_open_always_force(filename); + else if (flags & XDL_TRY_FORCE_LOAD) + return xdl_open_try_force(filename); + else + return xdl_find(filename); +} + +void *xdl_close(void *handle) { + if (NULL == handle) return NULL; + + xdl_t *self = (xdl_t *)handle; + if (NULL != self->pathname) free(self->pathname); + if (NULL != self->symtab) free(self->symtab); + if (NULL != self->strtab) free(self->strtab); + + void *linker_handle = self->linker_handle; + free(self); + return linker_handle; +} + +static uint32_t xdl_sysv_hash(const uint8_t *name) { + uint32_t h = 0, g; + + while (*name) { + h = (h << 4) + *name++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + return h; +} + +static uint32_t xdl_gnu_hash(const uint8_t *name) { + uint32_t h = 5381; + + while (*name) { + h += (h << 5) + *name++; + } + return h; +} + +static ElfW(Sym) *xdl_dynsym_find_symbol_use_sysv_hash(xdl_t *self, const char *sym_name) { + uint32_t hash = xdl_sysv_hash((const uint8_t *)sym_name); + + for (uint32_t i = self->sysv_hash.buckets[hash % self->sysv_hash.buckets_cnt]; 0 != i; + i = self->sysv_hash.chains[i]) { + ElfW(Sym) *sym = self->dynsym + i; + if (0 != strcmp(self->dynstr + sym->st_name, sym_name)) continue; + return sym; + } + + return NULL; +} + +static ElfW(Sym) *xdl_dynsym_find_symbol_use_gnu_hash(xdl_t *self, const char *sym_name) { + uint32_t hash = xdl_gnu_hash((const uint8_t *)sym_name); + + static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8; + size_t word = self->gnu_hash.bloom[(hash / elfclass_bits) % self->gnu_hash.bloom_cnt]; + size_t mask = 0 | (size_t)1 << (hash % elfclass_bits) | + (size_t)1 << ((hash >> self->gnu_hash.bloom_shift) % elfclass_bits); + + // if at least one bit is not set, this symbol is surely missing + if ((word & mask) != mask) return NULL; + + // ignore STN_UNDEF + uint32_t i = self->gnu_hash.buckets[hash % self->gnu_hash.buckets_cnt]; + if (i < self->gnu_hash.symoffset) return NULL; + + // loop through the chain + while (1) { + ElfW(Sym) *sym = self->dynsym + i; + uint32_t sym_hash = self->gnu_hash.chains[i - self->gnu_hash.symoffset]; + + if ((hash | (uint32_t)1) == (sym_hash | (uint32_t)1)) { + if (0 == strcmp(self->dynstr + sym->st_name, sym_name)) { + return sym; + } + } + + // chain ends with an element with the lowest bit set to 1 + if (sym_hash & (uint32_t)1) break; + + i++; + } + + return NULL; +} + +void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size) { + if (NULL == handle || NULL == symbol) return NULL; + if (NULL != symbol_size) *symbol_size = 0; + + xdl_t *self = (xdl_t *)handle; + + // load .dynsym only once + if (!self->dynsym_try_load) { + self->dynsym_try_load = true; + if (0 != xdl_dynsym_load(self)) return NULL; + } + + // find symbol + if (NULL == self->dynsym) return NULL; + ElfW(Sym) *sym = NULL; + if (self->gnu_hash.buckets_cnt > 0) { + // use GNU hash (.gnu.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) + sym = xdl_dynsym_find_symbol_use_gnu_hash(self, symbol); + } + if (NULL == sym && self->sysv_hash.buckets_cnt > 0) { + // use SYSV hash (.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) + sym = xdl_dynsym_find_symbol_use_sysv_hash(self, symbol); + } + if (NULL == sym || !XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) return NULL; + + if (NULL != symbol_size) *symbol_size = sym->st_size; + return (void *)(self->load_bias + sym->st_value); +} + +// clang-format off +/* + * For internal symbols in .symtab, LLVM may add some suffixes (for example for thinLTO). + * The format of the suffix is: ".xxxx.[hash]". LLVM may add multiple suffixes at once. + * The symbol name after removing these all suffixes is called canonical name. + * + * Because the hash part in the suffix may change when recompiled, so here we only match + * the canonical name. + * + * IN ADDITION: According to C/C++ syntax, it is illegal for a function name to contain + * dot character('.'), either in the middle or at the end. + * + * samples: + * + * symbol name in .symtab lookup is match + * ---------------------- ---------------- -------- + * abcd abc N + * abcd abcd Y + * abcd.llvm.10190306339727611508 abc N + * abcd.llvm.10190306339727611508 abcd Y + * abcd.llvm.10190306339727611508 abcd. N + * abcd.llvm.10190306339727611508 abcd.llvm Y + * abcd.llvm.10190306339727611508 abcd.llvm. N + * abcd.__uniq.513291356003753 abcd.__uniq.51329 N + * abcd.__uniq.513291356003753 abcd.__uniq.513291356003753 Y + */ +// clang-format on +static inline bool xdl_dsym_is_match(const char *str, const char *sym, size_t str_len) { + if (__predict_false(0 == str_len)) return false; + + do { + if (*str != *sym) return __predict_false('.' == *str && '\0' == *sym); + str++; + sym++; + if ('\0' == *str) break; + } while (0 != --str_len); + + return true; +} + +void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size) { + if (NULL == handle || NULL == symbol) return NULL; + if (NULL != symbol_size) *symbol_size = 0; + + xdl_t *self = (xdl_t *)handle; + + // load .symtab only once + if (!self->symtab_try_load) { + self->symtab_try_load = true; + if (0 != xdl_symtab_load(self)) return NULL; + } + + // find symbol + if (NULL == self->symtab) return NULL; + for (size_t i = 0; i < self->symtab_cnt; i++) { + ElfW(Sym) *sym = self->symtab + i; + + if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) continue; + // if (0 != strncmp(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue; + if (!xdl_dsym_is_match(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue; + + if (NULL != symbol_size) *symbol_size = sym->st_size; + return (void *)(self->load_bias + sym->st_value); + } + + return NULL; +} + +static bool xdl_elf_is_match(uintptr_t load_bias, const ElfW(Phdr) *dlpi_phdr, ElfW(Half) dlpi_phnum, + uintptr_t addr) { + if (addr < load_bias) return false; + + uintptr_t vaddr = addr - load_bias; + for (size_t i = 0; i < dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(dlpi_phdr[i]); + if (PT_LOAD != phdr->p_type) continue; + + if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true; + } + + return false; +} + +static int xdl_open_by_addr_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) { + (void)size; + + uintptr_t *pkg = (uintptr_t *)arg; + xdl_t **self = (xdl_t **)*pkg++; + uintptr_t addr = *pkg; + + if (xdl_elf_is_match(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, addr)) { + // found the target ELF + if (NULL == ((*self) = calloc(1, sizeof(xdl_t)))) return 1; // failed + if (NULL == ((*self)->pathname = strdup(info->dlpi_name))) { + free(*self); + *self = NULL; + return 1; // failed + } + (*self)->load_bias = info->dlpi_addr; + (*self)->dlpi_phdr = info->dlpi_phdr; + (*self)->dlpi_phnum = info->dlpi_phnum; + (*self)->dynsym_try_load = false; + (*self)->symtab_try_load = false; + return 1; // OK + } + + return 0; // mismatch +} + +static void *xdl_open_by_addr(void *addr) { + if (NULL == addr) return NULL; + + xdl_t *self = NULL; + uintptr_t pkg[2] = {(uintptr_t)&self, (uintptr_t)addr}; + xdl_iterate_phdr(xdl_open_by_addr_iterate_cb, pkg, XDL_DEFAULT); + + return (void *)self; +} + +static bool xdl_sym_is_match(ElfW(Sym) *sym, uintptr_t offset, bool is_symtab) { + if (is_symtab) { + if (!XDL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) false; + } else { + if (!XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) false; + } + + return ELF_ST_TYPE(sym->st_info) != STT_TLS && offset >= sym->st_value && + offset < sym->st_value + sym->st_size; +} + +static ElfW(Sym) *xdl_sym_by_addr(void *handle, void *addr) { + xdl_t *self = (xdl_t *)handle; + + // load .dynsym only once + if (!self->dynsym_try_load) { + self->dynsym_try_load = true; + if (0 != xdl_dynsym_load(self)) return NULL; + } + + // find symbol + if (NULL == self->dynsym) return NULL; + uintptr_t offset = (uintptr_t)addr - self->load_bias; + if (self->gnu_hash.buckets_cnt > 0) { + const uint32_t *chains_all = self->gnu_hash.chains - self->gnu_hash.symoffset; + for (size_t i = 0; i < self->gnu_hash.buckets_cnt; i++) { + uint32_t n = self->gnu_hash.buckets[i]; + if (n < self->gnu_hash.symoffset) continue; + do { + ElfW(Sym) *sym = self->dynsym + n; + if (xdl_sym_is_match(sym, offset, false)) return sym; + } while ((chains_all[n++] & 1) == 0); + } + } else if (self->sysv_hash.chains_cnt > 0) { + for (size_t i = 0; i < self->sysv_hash.chains_cnt; i++) { + ElfW(Sym) *sym = self->dynsym + i; + if (xdl_sym_is_match(sym, offset, false)) return sym; + } + } + + return NULL; +} + +static ElfW(Sym) *xdl_dsym_by_addr(void *handle, void *addr) { + xdl_t *self = (xdl_t *)handle; + + // load .symtab only once + if (!self->symtab_try_load) { + self->symtab_try_load = true; + if (0 != xdl_symtab_load(self)) return NULL; + } + + // find symbol + if (NULL == self->symtab) return NULL; + uintptr_t offset = (uintptr_t)addr - self->load_bias; + for (size_t i = 0; i < self->symtab_cnt; i++) { + ElfW(Sym) *sym = self->symtab + i; + if (xdl_sym_is_match(sym, offset, true)) return sym; + } + + return NULL; +} + +int xdl_addr(void *addr, xdl_info_t *info, void **cache) { + if (NULL == addr || NULL == info || NULL == cache) return 0; + + memset(info, 0, sizeof(Dl_info)); + + // find handle from cache + xdl_t *handle = NULL; + for (handle = *((xdl_t **)cache); NULL != handle; handle = handle->next) + if (xdl_elf_is_match(handle->load_bias, handle->dlpi_phdr, handle->dlpi_phnum, (uintptr_t)addr)) break; + + // create new handle, save handle to cache + if (NULL == handle) { + handle = (xdl_t *)xdl_open_by_addr(addr); + if (NULL == handle) return 0; + handle->next = *(xdl_t **)cache; + *(xdl_t **)cache = handle; + } + + // we have at least: load_bias, pathname, dlpi_phdr, dlpi_phnum + info->dli_fbase = (void *)handle->load_bias; + info->dli_fname = handle->pathname; + info->dli_sname = NULL; + info->dli_saddr = 0; + info->dli_ssize = 0; + info->dlpi_phdr = handle->dlpi_phdr; + info->dlpi_phnum = (size_t)handle->dlpi_phnum; + + // keep looking for: symbol name, symbol offset, symbol size + ElfW(Sym) *sym; + if (NULL != (sym = xdl_sym_by_addr((void *)handle, addr))) { + info->dli_sname = handle->dynstr + sym->st_name; + info->dli_saddr = (void *)(handle->load_bias + sym->st_value); + info->dli_ssize = sym->st_size; + } else if (NULL != (sym = xdl_dsym_by_addr((void *)handle, addr))) { + info->dli_sname = handle->strtab + sym->st_name; + info->dli_saddr = (void *)(handle->load_bias + sym->st_value); + info->dli_ssize = sym->st_size; + } + + return 1; +} + +void xdl_addr_clean(void **cache) { + if (NULL == cache) return; + + xdl_t *handle = *((xdl_t **)cache); + while (NULL != handle) { + xdl_t *tmp = handle; + handle = handle->next; + xdl_close(tmp); + } + *cache = NULL; +} + +int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags) { + if (NULL == callback) return 0; + + return xdl_iterate_phdr_impl(callback, data, flags); +} + +int xdl_info(void *handle, int request, void *info) { + if (NULL == handle || XDL_DI_DLINFO != request || NULL == info) return -1; + + xdl_t *self = (xdl_t *)handle; + xdl_info_t *dlinfo = (xdl_info_t *)info; + + dlinfo->dli_fbase = (void *)self->load_bias; + dlinfo->dli_fname = self->pathname; + dlinfo->dli_sname = NULL; + dlinfo->dli_saddr = 0; + dlinfo->dli_ssize = 0; + dlinfo->dlpi_phdr = self->dlpi_phdr; + dlinfo->dlpi_phnum = (size_t)self->dlpi_phnum; + return 0; +} diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl.h new file mode 100644 index 0000000000000000000000000000000000000000..d927c43ac6bfb6ecfcbf8ad496f61596fc5083e9 --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl.h @@ -0,0 +1,92 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2020-10-04. + +// +// xDL version: 2.1.0 +// +// xDL is an enhanced implementation of the Android DL series functions. +// For more information, documentation, and the latest version please check: +// https://github.com/hexhacking/xDL +// + +#ifndef IO_GITHUB_HEXHACKING_XDL +#define IO_GITHUB_HEXHACKING_XDL + +#include <dlfcn.h> +#include <link.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + // same as Dl_info: + const char *dli_fname; // Pathname of shared object that contains address. + void *dli_fbase; // Address at which shared object is loaded. + const char *dli_sname; // Name of nearest symbol with address lower than addr. + void *dli_saddr; // Exact address of symbol named in dli_sname. + // added by xDL: + size_t dli_ssize; // Symbol size of nearest symbol with address lower than addr. + const ElfW(Phdr) *dlpi_phdr; // Pointer to array of ELF program headers for this object. + size_t dlpi_phnum; // Number of items in dlpi_phdr. +} xdl_info_t; + +// +// Default value for flags in both xdl_open() and xdl_iterate_phdr(). +// +#define XDL_DEFAULT 0x00 + +// +// Enhanced dlopen() / dlclose() / dlsym(). +// +#define XDL_TRY_FORCE_LOAD 0x01 +#define XDL_ALWAYS_FORCE_LOAD 0x02 +void *xdl_open(const char *filename, int flags); +void *xdl_close(void *handle); +void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size); +void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size); + +// +// Enhanced dladdr(). +// +int xdl_addr(void *addr, xdl_info_t *info, void **cache); +void xdl_addr_clean(void **cache); + +// +// Enhanced dl_iterate_phdr(). +// +#define XDL_FULL_PATHNAME 0x01 +int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags); + +// +// Custom dlinfo(). +// +#define XDL_DI_DLINFO 1 // type of info: xdl_info_t +int xdl_info(void *handle, int request, void *info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.c new file mode 100644 index 0000000000000000000000000000000000000000..b89b642f4978f94143c898cf46248678d8477bc8 --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.c @@ -0,0 +1,297 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2020-10-04. + +#include "xdl_iterate.h" + +#include <android/api-level.h> +#include <ctype.h> +#include <dlfcn.h> +#include <elf.h> +#include <inttypes.h> +#include <link.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/auxv.h> + +#include "xdl.h" +#include "xdl_linker.h" +#include "xdl_util.h" + +/* + * ========================================================================================================= + * API-LEVEL ANDROID-VERSION SOLUTION + * ========================================================================================================= + * 16 4.1 /proc/self/maps + * 17 4.2 /proc/self/maps + * 18 4.3 /proc/self/maps + * 19 4.4 /proc/self/maps + * 20 4.4W /proc/self/maps + * --------------------------------------------------------------------------------------------------------- + * 21 5.0 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3) + * 22 5.1 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3) + * --------------------------------------------------------------------------------------------------------- + * 23 >= 6.0 dl_iterate_phdr() + linker/linker64 from getauxval(3) + * ========================================================================================================= + */ + +extern __attribute((weak)) int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *); +extern __attribute((weak)) unsigned long int getauxval(unsigned long int); + +static uintptr_t xdl_iterate_get_min_vaddr(struct dl_phdr_info *info) { + uintptr_t min_vaddr = UINTPTR_MAX; + for (size_t i = 0; i < info->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); + if (PT_LOAD == phdr->p_type) { + if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; + } + } + return min_vaddr; +} + +static int xdl_iterate_open_or_rewind_maps(FILE **maps) { + if (NULL == *maps) { + *maps = fopen("/proc/self/maps", "r"); + if (NULL == *maps) return -1; + } else + rewind(*maps); + + return 0; +} + +static int xdl_iterate_get_pathname_from_maps(uintptr_t base, char *buf, size_t buf_len, FILE **maps) { + // open or rewind maps-file + if (0 != xdl_iterate_open_or_rewind_maps(maps)) return -1; // failed + + char line[1024]; + while (fgets(line, sizeof(line), *maps)) { + // check base address + uintptr_t start, end; + if (2 != sscanf(line, "%" SCNxPTR "-%" SCNxPTR " r", &start, &end)) continue; + if (base < start) break; // failed + if (base >= end) continue; + + // get pathname + char *pathname = strchr(line, '/'); + if (NULL == pathname) break; // failed + xdl_util_trim_ending(pathname); + + // found it + strlcpy(buf, pathname, buf_len); + return 0; // OK + } + + return -1; // failed +} + +static int xdl_iterate_by_linker_cb(struct dl_phdr_info *info, size_t size, void *arg) { + uintptr_t *pkg = (uintptr_t *)arg; + xdl_iterate_phdr_cb_t cb = (xdl_iterate_phdr_cb_t)*pkg++; + void *cb_arg = (void *)*pkg++; + FILE **maps = (FILE **)*pkg++; + uintptr_t linker_load_bias = *pkg++; + int flags = (int)*pkg; + + // ignore invalid ELF + if (0 == info->dlpi_addr || NULL == info->dlpi_name || '\0' == info->dlpi_name[0]) return 0; + + // ignore linker if we have returned it already + if (linker_load_bias == info->dlpi_addr) return 0; + + struct dl_phdr_info info_fixed; + info_fixed.dlpi_addr = info->dlpi_addr; + info_fixed.dlpi_name = info->dlpi_name; + info_fixed.dlpi_phdr = info->dlpi_phdr; + info_fixed.dlpi_phnum = info->dlpi_phnum; + info = &info_fixed; + + // fix dlpi_phdr & dlpi_phnum (from memory) + if (NULL == info->dlpi_phdr || 0 == info->dlpi_phnum) { + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)info->dlpi_addr; + info->dlpi_phdr = (ElfW(Phdr) *)(info->dlpi_addr + ehdr->e_phoff); + info->dlpi_phnum = ehdr->e_phnum; + } + + // fix dlpi_name (from /proc/self/maps) + if ('/' != info->dlpi_name[0] && '[' != info->dlpi_name[0] && (0 != (flags & XDL_FULL_PATHNAME))) { + // get base address + uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(info); + if (UINTPTR_MAX == min_vaddr) return 0; // ignore this ELF + uintptr_t base = (uintptr_t)(info->dlpi_addr + min_vaddr); + + char buf[1024]; + if (0 != xdl_iterate_get_pathname_from_maps(base, buf, sizeof(buf), maps)) return 0; // ignore this ELF + + info->dlpi_name = (const char *)buf; + } + + // callback + return cb(info, size, cb_arg); +} + +static uintptr_t xdl_iterate_get_linker_base(void) { + if (NULL == getauxval) return 0; + + uintptr_t base = (uintptr_t)getauxval(AT_BASE); + if (0 == base) return 0; + if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return 0; + + return base; +} + +static int xdl_iterate_do_callback(xdl_iterate_phdr_cb_t cb, void *cb_arg, uintptr_t base, + const char *pathname, uintptr_t *load_bias) { + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; + + struct dl_phdr_info info; + info.dlpi_name = pathname; + info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); + info.dlpi_phnum = ehdr->e_phnum; + + // get load bias + uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(&info); + if (UINTPTR_MAX == min_vaddr) return 0; // ignore invalid ELF + info.dlpi_addr = (ElfW(Addr))(base - min_vaddr); + if (NULL != load_bias) *load_bias = info.dlpi_addr; + + return cb(&info, sizeof(struct dl_phdr_info), cb_arg); +} + +static int xdl_iterate_by_linker(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) { + if (NULL == dl_iterate_phdr) return 0; + + int api_level = xdl_util_get_api_level(); + FILE *maps = NULL; + int r; + + // dl_iterate_phdr(3) does NOT contain linker/linker64 when Android version < 8.1 (API level 27). + // Here we always try to get linker base address from auxv. + uintptr_t linker_load_bias = 0; + uintptr_t linker_base = xdl_iterate_get_linker_base(); + if (0 != linker_base) { + if (0 != + (r = xdl_iterate_do_callback(cb, cb_arg, linker_base, XDL_UTIL_LINKER_PATHNAME, &linker_load_bias))) + return r; + } + + // for other ELF + uintptr_t pkg[5] = {(uintptr_t)cb, (uintptr_t)cb_arg, (uintptr_t)&maps, linker_load_bias, (uintptr_t)flags}; + if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_lock(); + r = dl_iterate_phdr(xdl_iterate_by_linker_cb, pkg); + if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_unlock(); + + if (NULL != maps) fclose(maps); + return r; +} + +#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ +static int xdl_iterate_by_maps(xdl_iterate_phdr_cb_t cb, void *cb_arg) { + FILE *maps = fopen("/proc/self/maps", "r"); + if (NULL == maps) return 0; + + int r = 0; + char buf1[1024], buf2[1024]; + char *line = buf1; + uintptr_t prev_base = 0; + bool try_next_line = false; + + while (fgets(line, sizeof(buf1), maps)) { + // Try to find an ELF which loaded by linker. + uintptr_t base, offset; + char exec; + if (3 != sscanf(line, "%" SCNxPTR "-%*" SCNxPTR " r%*c%cp %" SCNxPTR " ", &base, &exec, &offset)) + goto clean; + + if ('-' == exec && 0 == offset) { + // r--p + prev_base = base; + line = (line == buf1 ? buf2 : buf1); + try_next_line = true; + continue; + } else if (exec == 'x') { + // r-xp + char *pathname = NULL; + if (try_next_line && 0 != offset) { + char *prev = (line == buf1 ? buf2 : buf1); + char *prev_pathname = strchr(prev, '/'); + if (NULL == prev_pathname) goto clean; + + pathname = strchr(line, '/'); + if (NULL == pathname) goto clean; + + xdl_util_trim_ending(prev_pathname); + xdl_util_trim_ending(pathname); + if (0 != strcmp(prev_pathname, pathname)) goto clean; + + // we found the line with r-xp in the next line + base = prev_base; + offset = 0; + } + + if (0 != offset) goto clean; + + // get pathname + if (NULL == pathname) { + pathname = strchr(line, '/'); + if (NULL == pathname) goto clean; + xdl_util_trim_ending(pathname); + } + + if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) goto clean; + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; + struct dl_phdr_info info; + info.dlpi_name = pathname; + info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); + info.dlpi_phnum = ehdr->e_phnum; + + // callback + if (0 != (r = xdl_iterate_do_callback(cb, cb_arg, base, pathname, NULL))) break; + } + + clean: + try_next_line = false; + } + + fclose(maps); + return r; +} +#endif + +int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) { + // iterate by /proc/self/maps in Android 4.x (Android 4.x only supports arm32 and x86) +#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ + if (xdl_util_get_api_level() < __ANDROID_API_L__) return xdl_iterate_by_maps(cb, cb_arg); +#endif + + // iterate by dl_iterate_phdr() + return xdl_iterate_by_linker(cb, cb_arg, flags); +} + +int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len) { + FILE *maps = NULL; + int r = xdl_iterate_get_pathname_from_maps(base, buf, buf_len, &maps); + if (NULL != maps) fclose(maps); + return r; +} diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.h new file mode 100644 index 0000000000000000000000000000000000000000..ab228660b48d785a6613655dd23996eedf9f7c4e --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.h @@ -0,0 +1,43 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2020-10-04. + +#ifndef IO_GITHUB_HEXHACKING_XDL_ITERATE +#define IO_GITHUB_HEXHACKING_XDL_ITERATE + +#include <link.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*xdl_iterate_phdr_cb_t)(struct dl_phdr_info *info, size_t size, void *arg); +int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags); + +int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.c new file mode 100644 index 0000000000000000000000000000000000000000..9021867cdb9a184ebd9777f3c75c5e1f65217d2c --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.c @@ -0,0 +1,234 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2021-02-21. + +#include "xdl_linker.h" + +#include <dlfcn.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> + +#include "xdl.h" +#include "xdl_iterate.h" +#include "xdl_util.h" + +#define XDL_LINKER_SYM_MUTEX "__dl__ZL10g_dl_mutex" +#define XDL_LINKER_SYM_DLOPEN_EXT_N "__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv" +#define XDL_LINKER_SYM_DO_DLOPEN_N "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv" +#define XDL_LINKER_SYM_DLOPEN_O "__dl__Z8__dlopenPKciPKv" +#define XDL_LINKER_SYM_LOADER_DLOPEN_P "__loader_dlopen" + +#ifndef __LP64__ +#define LIB "lib" +#else +#define LIB "lib64" +#endif + +typedef void *(*xdl_linker_dlopen_n_t)(const char *, int, const void *, void *); +typedef void *(*xdl_linker_dlopen_o_t)(const char *, int, const void *); + +static pthread_mutex_t *xdl_linker_mutex = NULL; +static void *xdl_linker_dlopen = NULL; + +typedef enum { MATCH_PREFIX, MATCH_SUFFIX } xdl_linker_match_type_t; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct { + xdl_linker_match_type_t type; + const char *value; +} xdl_linker_match_t; +#pragma clang diagnostic pop + +typedef struct { + void *addr; + xdl_linker_match_t *matches; + size_t matches_cursor; +} xdl_linker_caller_t; + +// https://source.android.com/docs/core/architecture/vndk/linker-namespace +// The following rules are loose and incomplete, you can add more according to your needs. +static xdl_linker_match_t xdl_linker_match_default[] = {{MATCH_SUFFIX, "/libc.so"}}; +static xdl_linker_match_t xdl_linker_match_art[] = {{MATCH_SUFFIX, "/libart.so"}}; +static xdl_linker_match_t xdl_linker_match_sphal[] = {{MATCH_PREFIX, "/vendor/" LIB "/egl/"}, + {MATCH_PREFIX, "/vendor/" LIB "/hw/"}, + {MATCH_PREFIX, "/vendor/" LIB "/"}, + {MATCH_PREFIX, "/odm/" LIB "/"}}; +static xdl_linker_match_t xdl_linker_match_vndk[] = {{MATCH_PREFIX, "/apex/com.android.vndk.v"}, + {MATCH_PREFIX, "/vendor/" LIB "/vndk-sp/"}, + {MATCH_PREFIX, "/odm/" LIB "/vndk-sp/"}}; +static xdl_linker_caller_t xdl_linker_callers[] = { + {NULL, xdl_linker_match_default, sizeof(xdl_linker_match_default) / sizeof(xdl_linker_match_t)}, + {NULL, xdl_linker_match_art, sizeof(xdl_linker_match_art) / sizeof(xdl_linker_match_t)}, + {NULL, xdl_linker_match_sphal, sizeof(xdl_linker_match_sphal) / sizeof(xdl_linker_match_t)}, + {NULL, xdl_linker_match_vndk, sizeof(xdl_linker_match_vndk) / sizeof(xdl_linker_match_t)}}; + +static void xdl_linker_init_symbols_impl(void) { + // find linker from: /proc/self/maps (API level < 18) or getauxval (API level >= 18) + void *handle = xdl_open(XDL_UTIL_LINKER_BASENAME, XDL_DEFAULT); + if (NULL == handle) return; + + int api_level = xdl_util_get_api_level(); + if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) { + // == Android 5.x + xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL); + } else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { + // == Android 7.x + xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_EXT_N, NULL); + if (NULL == xdl_linker_dlopen) { + xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DO_DLOPEN_N, NULL); + xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL); + } + } else if (__ANDROID_API_O__ == api_level || __ANDROID_API_O_MR1__ == api_level) { + // == Android 8.x + xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_O, NULL); + } else if (api_level >= __ANDROID_API_P__) { + // >= Android 9.0 + xdl_linker_dlopen = xdl_sym(handle, XDL_LINKER_SYM_LOADER_DLOPEN_P, NULL); + } + + xdl_close(handle); +} + +static void xdl_linker_init_symbols(void) { + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static bool inited = false; + if (!inited) { + pthread_mutex_lock(&lock); + if (!inited) { + xdl_linker_init_symbols_impl(); + inited = true; + } + pthread_mutex_unlock(&lock); + } +} + +void xdl_linker_lock(void) { + xdl_linker_init_symbols(); + + if (NULL != xdl_linker_mutex) pthread_mutex_lock(xdl_linker_mutex); +} + +void xdl_linker_unlock(void) { + if (NULL != xdl_linker_mutex) pthread_mutex_unlock(xdl_linker_mutex); +} + +static void *xdl_linker_get_caller_addr(struct dl_phdr_info *info) { + for (size_t i = 0; i < info->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); + if (PT_LOAD == phdr->p_type) { + return (void *)(info->dlpi_addr + phdr->p_vaddr); + } + } + return NULL; +} + +static void xdl_linker_save_caller_addr(struct dl_phdr_info *info, xdl_linker_caller_t *caller, + size_t cursor) { + void *addr = xdl_linker_get_caller_addr(info); + if (NULL != addr) { + caller->addr = addr; + caller->matches_cursor = cursor; + } +} + +static int xdl_linker_get_caller_addr_cb(struct dl_phdr_info *info, size_t size, void *arg) { + (void)size, (void)arg; + if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; // continue + + int ret = 1; // OK + for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) { + xdl_linker_caller_t *caller = &xdl_linker_callers[i]; + for (size_t j = 0; j < caller->matches_cursor; j++) { + xdl_linker_match_t *match = &caller->matches[j]; + switch (match->type) { + case MATCH_PREFIX: + if (xdl_util_starts_with(info->dlpi_name, match->value)) { + xdl_linker_save_caller_addr(info, caller, j); + } + break; + case MATCH_SUFFIX: + if (xdl_util_ends_with(info->dlpi_name, match->value)) { + xdl_linker_save_caller_addr(info, caller, j); + } + break; + } + } + if (NULL == caller->addr || 0 != caller->matches_cursor) ret = 0; // continue + } + return ret; +} + +static void xdl_linker_init_caller_addr_impl(void) { + xdl_iterate_phdr_impl(xdl_linker_get_caller_addr_cb, NULL, XDL_DEFAULT); +} + +static void xdl_linker_init_caller_addr(void) { + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static bool inited = false; + if (!inited) { + pthread_mutex_lock(&lock); + if (!inited) { + xdl_linker_init_caller_addr_impl(); + inited = true; + } + pthread_mutex_unlock(&lock); + } +} + +void *xdl_linker_force_dlopen(const char *filename) { + int api_level = xdl_util_get_api_level(); + + if (api_level <= __ANDROID_API_M__) { + // <= Android 6.0 + return dlopen(filename, RTLD_NOW); + } else { + xdl_linker_init_symbols(); + if (NULL == xdl_linker_dlopen) return NULL; + xdl_linker_init_caller_addr(); + + void *handle = NULL; + if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { + // == Android 7.x + xdl_linker_lock(); + for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) { + xdl_linker_caller_t *caller = &xdl_linker_callers[i]; + if (NULL != caller->addr) { + handle = ((xdl_linker_dlopen_n_t)xdl_linker_dlopen)(filename, RTLD_NOW, NULL, caller->addr); + if (NULL != handle) break; + } + } + xdl_linker_unlock(); + } else { + // >= Android 8.0 + for (size_t i = 0; i < sizeof(xdl_linker_callers) / sizeof(xdl_linker_callers[0]); i++) { + xdl_linker_caller_t *caller = &xdl_linker_callers[i]; + if (NULL != caller->addr) { + handle = ((xdl_linker_dlopen_o_t)xdl_linker_dlopen)(filename, RTLD_NOW, caller->addr); + if (NULL != handle) break; + } + } + } + return handle; + } +} diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h new file mode 100644 index 0000000000000000000000000000000000000000..00cbf44b75db5ff897d32d7ad5f802afdd4ebaba --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2021-02-21. + +#ifndef IO_GITHUB_HEXHACKING_XDL_LINKER +#define IO_GITHUB_HEXHACKING_XDL_LINKER + +#ifdef __cplusplus +extern "C" { +#endif + +void xdl_linker_lock(void); +void xdl_linker_unlock(void); + +void *xdl_linker_force_dlopen(const char *filename); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.c new file mode 100644 index 0000000000000000000000000000000000000000..541300de88c00f6cd7232792c127a568da1eda3f --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.c @@ -0,0 +1,187 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2020-11-08. + +#include "xdl_lzma.h" + +#include <ctype.h> +#include <inttypes.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "xdl.h" +#include "xdl_util.h" + +// LZMA library pathname & symbol names +#ifndef __LP64__ +#define XDL_LZMA_PATHNAME "/system/lib/liblzma.so" +#else +#define XDL_LZMA_PATHNAME "/system/lib64/liblzma.so" +#endif +#define XDL_LZMA_SYM_CRCGEN "CrcGenerateTable" +#define XDL_LZMA_SYM_CRC64GEN "Crc64GenerateTable" +#define XDL_LZMA_SYM_CONSTRUCT "XzUnpacker_Construct" +#define XDL_LZMA_SYM_ISFINISHED "XzUnpacker_IsStreamWasFinished" +#define XDL_LZMA_SYM_FREE "XzUnpacker_Free" +#define XDL_LZMA_SYM_CODE "XzUnpacker_Code" + +// LZMA data type definition +#define SZ_OK 0 +typedef struct ISzAlloc ISzAlloc; +typedef const ISzAlloc *ISzAllocPtr; +struct ISzAlloc { + void *(*Alloc)(ISzAllocPtr p, size_t size); + void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */ +}; +typedef enum { + CODER_STATUS_NOT_SPECIFIED, /* use main error code instead */ + CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + CODER_STATUS_NOT_FINISHED, /* stream was not finished */ + CODER_STATUS_NEEDS_MORE_INPUT /* you must provide more input bytes */ +} ECoderStatus; +typedef enum { + CODER_FINISH_ANY, /* finish at any point */ + CODER_FINISH_END /* block must be finished at the end */ +} ECoderFinishMode; + +// LZMA function type definition +typedef void (*xdl_lzma_crcgen_t)(void); +typedef void (*xdl_lzma_crc64gen_t)(void); +typedef void (*xdl_lzma_construct_t)(void *, ISzAllocPtr); +typedef int (*xdl_lzma_isfinished_t)(const void *); +typedef void (*xdl_lzma_free_t)(void *); +typedef int (*xdl_lzma_code_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, ECoderFinishMode, + ECoderStatus *); +typedef int (*xdl_lzma_code_q_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, int, + ECoderFinishMode, ECoderStatus *); + +// LZMA function pointor +static xdl_lzma_construct_t xdl_lzma_construct = NULL; +static xdl_lzma_isfinished_t xdl_lzma_isfinished = NULL; +static xdl_lzma_free_t xdl_lzma_free = NULL; +static void *xdl_lzma_code = NULL; + +// LZMA init +static void xdl_lzma_init() { + void *lzma = xdl_open(XDL_LZMA_PATHNAME, XDL_TRY_FORCE_LOAD); + if (NULL == lzma) return; + + xdl_lzma_crcgen_t crcgen = NULL; + xdl_lzma_crc64gen_t crc64gen = NULL; + if (NULL == (crcgen = (xdl_lzma_crcgen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRCGEN, NULL))) goto end; + if (NULL == (crc64gen = (xdl_lzma_crc64gen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRC64GEN, NULL))) goto end; + if (NULL == (xdl_lzma_construct = (xdl_lzma_construct_t)xdl_sym(lzma, XDL_LZMA_SYM_CONSTRUCT, NULL))) + goto end; + if (NULL == (xdl_lzma_isfinished = (xdl_lzma_isfinished_t)xdl_sym(lzma, XDL_LZMA_SYM_ISFINISHED, NULL))) + goto end; + if (NULL == (xdl_lzma_free = (xdl_lzma_free_t)xdl_sym(lzma, XDL_LZMA_SYM_FREE, NULL))) goto end; + if (NULL == (xdl_lzma_code = xdl_sym(lzma, XDL_LZMA_SYM_CODE, NULL))) goto end; + crcgen(); + crc64gen(); + +end: + xdl_close(lzma); +} + +// LZMA internal alloc / free +static void *xdl_lzma_internal_alloc(ISzAllocPtr p, size_t size) { + (void)p; + return malloc(size); +} +static void xdl_lzma_internal_free(ISzAllocPtr p, void *address) { + (void)p; + free(address); +} + +int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size) { + size_t src_offset = 0; + size_t dst_offset = 0; + size_t src_remaining; + size_t dst_remaining; + ISzAlloc alloc = {.Alloc = xdl_lzma_internal_alloc, .Free = xdl_lzma_internal_free}; + long long state[4096 / sizeof(long long)]; // must be enough, 8-bit aligned + ECoderStatus status; + int api_level = xdl_util_get_api_level(); + + // init and check + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static bool inited = false; + if (!inited) { + pthread_mutex_lock(&lock); + if (!inited) { + xdl_lzma_init(); + inited = true; + } + pthread_mutex_unlock(&lock); + } + if (NULL == xdl_lzma_code) return -1; + + xdl_lzma_construct(&state, &alloc); + + *dst_size = 2 * src_size; + *dst = NULL; + do { + *dst_size *= 2; + if (NULL == (*dst = realloc(*dst, *dst_size))) { + xdl_lzma_free(&state); + return -1; + } + + src_remaining = src_size - src_offset; + dst_remaining = *dst_size - dst_offset; + + int result; + if (api_level >= __ANDROID_API_Q__) { + xdl_lzma_code_q_t lzma_code_q = (xdl_lzma_code_q_t)xdl_lzma_code; + result = lzma_code_q(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 1, + CODER_FINISH_ANY, &status); + } else { + xdl_lzma_code_t lzma_code = (xdl_lzma_code_t)xdl_lzma_code; + result = lzma_code(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, + CODER_FINISH_ANY, &status); + } + if (SZ_OK != result) { + free(*dst); + xdl_lzma_free(&state); + return -1; + } + + src_offset += src_remaining; + dst_offset += dst_remaining; + } while (status == CODER_STATUS_NOT_FINISHED); + + xdl_lzma_free(&state); + + if (!xdl_lzma_isfinished(&state)) { + free(*dst); + return -1; + } + + *dst_size = dst_offset; + *dst = realloc(*dst, *dst_size); + return 0; +} diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h new file mode 100644 index 0000000000000000000000000000000000000000..63c848a61e3e5bcd7a6d3510c645ba802f8f3b36 --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2020-11-08. + +#ifndef IO_GITHUB_HEXHACKING_XDL_LZMA +#define IO_GITHUB_HEXHACKING_XDL_LZMA + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.c b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.c new file mode 100644 index 0000000000000000000000000000000000000000..7d9e640db3a724a4fcebaa317457816dc7f56d15 --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.c @@ -0,0 +1,95 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2020-10-04. + +#include "xdl_util.h" + +#include <android/api-level.h> +#include <ctype.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +bool xdl_util_starts_with(const char *str, const char *start) { + while (*str && *str == *start) { + str++; + start++; + } + + return '\0' == *start; +} + +bool xdl_util_ends_with(const char *str, const char *ending) { + size_t str_len = strlen(str); + size_t ending_len = strlen(ending); + + if (ending_len > str_len) return false; + + return 0 == strcmp(str + (str_len - ending_len), ending); +} + +size_t xdl_util_trim_ending(char *start) { + char *end = start + strlen(start); + while (start < end && isspace((int)(*(end - 1)))) { + end--; + *end = '\0'; + } + return (size_t)(end - start); +} + +static int xdl_util_get_api_level_from_build_prop(void) { + char buf[128]; + int api_level = -1; + + FILE *fp = fopen("/system/build.prop", "r"); + if (NULL == fp) goto end; + + while (fgets(buf, sizeof(buf), fp)) { + if (xdl_util_starts_with(buf, "ro.build.version.sdk=")) { + api_level = atoi(buf + 21); + break; + } + } + fclose(fp); + +end: + return (api_level > 0) ? api_level : -1; +} + +int xdl_util_get_api_level(void) { + static int xdl_util_api_level = -1; + + if (xdl_util_api_level < 0) { + int api_level = android_get_device_api_level(); + if (api_level < 0) + api_level = xdl_util_get_api_level_from_build_prop(); // compatible with unusual models + if (api_level < __ANDROID_API_J__) api_level = __ANDROID_API_J__; + + __atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST); + } + + return xdl_util_api_level; +} diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h new file mode 100644 index 0000000000000000000000000000000000000000..582e99fdb574715521e1856416698f699ae667d8 --- /dev/null +++ b/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h @@ -0,0 +1,71 @@ +// Copyright (c) 2020-2023 HexHacking Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2020-10-04. + +#ifndef IO_GITHUB_HEXHACKING_XDL_UTIL +#define IO_GITHUB_HEXHACKING_XDL_UTIL + +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> + +#ifndef __LP64__ +#define XDL_UTIL_LINKER_BASENAME "linker" +#define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker" +#define XDL_UTIL_APP_PROCESS_BASENAME "app_process32" +#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process32" +#define XDL_UTIL_APP_PROCESS_BASENAME_K "app_process" +#define XDL_UTIL_APP_PROCESS_PATHNAME_K "/system/bin/app_process" +#else +#define XDL_UTIL_LINKER_BASENAME "linker64" +#define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker64" +#define XDL_UTIL_APP_PROCESS_BASENAME "app_process64" +#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process64" +#endif +#define XDL_UTIL_VDSO_BASENAME "[vdso]" + +#define XDL_UTIL_TEMP_FAILURE_RETRY(exp) \ + ({ \ + __typeof__(exp) _rc; \ + do { \ + errno = 0; \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; \ + }) + +#ifdef __cplusplus +extern "C" { +#endif + +bool xdl_util_starts_with(const char *str, const char *start); +bool xdl_util_ends_with(const char *str, const char *ending); + +size_t xdl_util_trim_ending(char *start); + +int xdl_util_get_api_level(void); + +#ifdef __cplusplus +} +#endif + +#endif