replace_malloc_bridge.h (8165B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef replace_malloc_bridge_h 8 #define replace_malloc_bridge_h 9 10 // The replace-malloc bridge allows bidirectional method calls between 11 // a program and the replace-malloc library that has been loaded for it. 12 // In Firefox, this is used to allow method calls between code in libxul 13 // and code in the replace-malloc library, without libxul needing to link 14 // against that library or vice-versa. 15 // 16 // Subsystems can add methods for their own need. Replace-malloc libraries 17 // can decide to implement those methods or not. 18 // 19 // Replace-malloc libraries can provide such a bridge by implementing 20 // a ReplaceMallocBridge-derived class, and a get_bridge function 21 // returning an instance of that class. The default methods in 22 // ReplaceMallocBridge are expected to return values that callers would 23 // understand as "the bridge doesn't implement this method", so that a 24 // replace-malloc library doesn't have to implement all methods. 25 // 26 // The ReplaceMallocBridge class contains definitions for methods for 27 // all replace-malloc libraries. Each library picks the methods it wants 28 // to reply to in its ReplaceMallocBridge-derived class instance. 29 // All methods of ReplaceMallocBridge must be virtual. Similarly, 30 // anything passed as an argument to those methods must be plain data, or 31 // an instance of a class with only virtual methods. 32 // 33 // Binary compatibility is expected to be maintained, such that a newer 34 // Firefox can be used with an old replace-malloc library, or an old 35 // Firefox can be used with a newer replace-malloc library. As such, only 36 // new virtual methods should be added to ReplaceMallocBridge, and 37 // each change should have a corresponding bump of the mVersion value. 38 // At the same time, each virtual method should have a corresponding 39 // wrapper calling the virtual method on the instance from 40 // ReplaceMallocBridge::Get(), giving it the version the virtual method 41 // was added. 42 // 43 // Parts that are not relevant to the replace-malloc library end of the 44 // bridge are hidden when REPLACE_MALLOC_IMPL is not defined, which is 45 // the case when including replace_malloc.h. 46 47 struct ReplaceMallocBridge; 48 49 #include "mozilla/Types.h" 50 51 #ifdef _WIN32 52 typedef void* platform_handle_t; 53 #else 54 typedef int platform_handle_t; 55 #endif 56 57 // Include once without MALLOC_DECL set so it can include other headers. 58 #include "malloc_decls.h" 59 60 MOZ_BEGIN_EXTERN_C 61 62 #ifndef REPLACE_MALLOC_IMPL 63 // Returns the replace-malloc bridge if there is one to be returned. 64 MFBT_API ReplaceMallocBridge* get_bridge(); 65 #endif 66 67 // Table of malloc functions. 68 // e.g. void* (*malloc)(size_t), etc. 69 70 #define MALLOC_DECL(name, return_type, ...) \ 71 typedef return_type(name##_impl_t)(__VA_ARGS__); 72 73 #include "malloc_decls.h" 74 75 #define MALLOC_DECL(name, return_type, ...) name##_impl_t* name; 76 77 typedef struct { 78 #include "malloc_decls.h" 79 } malloc_table_t; 80 81 MOZ_END_EXTERN_C 82 83 #ifdef __cplusplus 84 85 // Table of malloc hook functions. 86 // Those functions are called with the arguments and results of malloc 87 // functions after they are called. 88 // e.g. void* (*malloc_hook)(void*, size_t), etc. 89 // They can either return the result they're given, or alter it before 90 // returning it. 91 // The hooks corresponding to functions, like free(void*), that return no 92 // value, don't take an extra argument. 93 // The table must at least contain a pointer for malloc_hook and free_hook 94 // functions. They will be used as fallback if no pointer is given for 95 // other allocation functions, like calloc_hook. 96 namespace mozilla { 97 namespace detail { 98 template <typename R, typename... Args> 99 struct AllocHookType { 100 using Type = R (*)(R, Args...); 101 }; 102 103 template <typename... Args> 104 struct AllocHookType<void, Args...> { 105 using Type = void (*)(Args...); 106 }; 107 108 } // namespace detail 109 } // namespace mozilla 110 111 # define MALLOC_DECL(name, return_type, ...) \ 112 typename mozilla::detail::AllocHookType<return_type, ##__VA_ARGS__>::Type \ 113 name##_hook; 114 115 typedef struct { 116 # include "malloc_decls.h" 117 // Like free_hook, but called before realloc_hook. free_hook is called 118 // instead of not given. 119 void (*realloc_hook_before)(void* aPtr); 120 } malloc_hook_table_t; 121 122 namespace mozilla { 123 namespace dmd { 124 struct DMDFuncs; 125 } // namespace dmd 126 127 namespace phc { 128 129 class AddrInfo; 130 131 } // namespace phc 132 133 // Callbacks to register debug file handles for Poison IO interpose. 134 // See Mozilla(|Un)RegisterDebugHandle in xpcom/build/PoisonIOInterposer.h 135 struct DebugFdRegistry { 136 virtual void RegisterHandle(platform_handle_t aFd); 137 138 virtual void UnRegisterHandle(platform_handle_t aFd); 139 }; 140 } // namespace mozilla 141 142 struct ReplaceMallocBridge { 143 ReplaceMallocBridge() : mVersion(6) {} 144 145 // This method was added in version 1 of the bridge. 146 virtual mozilla::dmd::DMDFuncs* GetDMDFuncs() { return nullptr; } 147 148 // Send a DebugFdRegistry instance to the replace-malloc library so that 149 // it can register/unregister file descriptors whenever needed. The 150 // instance is valid until the process dies. 151 // This method was added in version 2 of the bridge. 152 virtual void InitDebugFd(mozilla::DebugFdRegistry&) {} 153 154 // Register a list of malloc functions and hook functions to the 155 // replace-malloc library so that it can choose to dispatch to them 156 // when needed. The details of what is dispatched when is left to the 157 // replace-malloc library. 158 // Passing a nullptr for either table will unregister a previously 159 // registered table under the same name. 160 // Returns nullptr if registration failed. 161 // If registration succeeded, a table of "pure" malloc functions is 162 // returned. Those "pure" malloc functions won't call hooks. 163 // /!\ Do not rely on registration/unregistration to be instantaneous. 164 // Functions from a previously registered table may still be called for 165 // a brief time after RegisterHook returns. 166 // This method was added in version 3 of the bridge. 167 virtual const malloc_table_t* RegisterHook( 168 const char* aName, const malloc_table_t* aTable, 169 const malloc_hook_table_t* aHookTable) { 170 return nullptr; 171 } 172 173 # ifndef REPLACE_MALLOC_IMPL 174 // Returns the replace-malloc bridge if its version is at least the 175 // requested one. 176 static ReplaceMallocBridge* Get(int aMinimumVersion) { 177 static ReplaceMallocBridge* sSingleton = get_bridge(); 178 return (sSingleton && sSingleton->mVersion >= aMinimumVersion) ? sSingleton 179 : nullptr; 180 } 181 # endif 182 183 protected: 184 const int mVersion; 185 }; 186 187 # ifndef REPLACE_MALLOC_IMPL 188 // Class containing wrappers for calls to ReplaceMallocBridge methods. 189 // Those wrappers need to be static methods in a class because compilers 190 // complain about unused static global functions, and linkers complain 191 // about multiple definitions of non-static global functions. 192 // Using a separate class from ReplaceMallocBridge allows the function 193 // names to be identical. 194 struct ReplaceMalloc { 195 // Don't call this method from performance critical code. Use 196 // mozilla::dmd::DMDFuncs::Get() instead, it has less overhead. 197 static mozilla::dmd::DMDFuncs* GetDMDFuncs() { 198 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 1); 199 return singleton ? singleton->GetDMDFuncs() : nullptr; 200 } 201 202 static void InitDebugFd(mozilla::DebugFdRegistry& aRegistry) { 203 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 2); 204 if (singleton) { 205 singleton->InitDebugFd(aRegistry); 206 } 207 } 208 209 static const malloc_table_t* RegisterHook( 210 const char* aName, const malloc_table_t* aTable, 211 const malloc_hook_table_t* aHookTable) { 212 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 3); 213 return singleton ? singleton->RegisterHook(aName, aTable, aHookTable) 214 : nullptr; 215 } 216 }; 217 # endif 218 219 #endif // __cplusplus 220 221 #endif // replace_malloc_bridge_h