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 replace_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 MOZ_BEGIN_EXTERN_C 52 53 #ifndef REPLACE_MALLOC_IMPL 54 // Returns the replace-malloc bridge if there is one to be returned. 55 MFBT_API ReplaceMallocBridge* get_bridge(); 56 #endif 57 58 // Table of malloc functions. 59 // e.g. void* (*malloc)(size_t), etc. 60 61 #define MALLOC_DECL(name, return_type, ...) \ 62 typedef return_type(name##_impl_t)(__VA_ARGS__); 63 64 #include "malloc_decls.h" 65 66 #define MALLOC_DECL(name, return_type, ...) name##_impl_t* name; 67 68 typedef struct { 69 #include "malloc_decls.h" 70 } malloc_table_t; 71 72 MOZ_END_EXTERN_C 73 74 #ifdef __cplusplus 75 76 // Table of malloc hook functions. 77 // Those functions are called with the arguments and results of malloc 78 // functions after they are called. 79 // e.g. void* (*malloc_hook)(void*, size_t), etc. 80 // They can either return the result they're given, or alter it before 81 // returning it. 82 // The hooks corresponding to functions, like free(void*), that return no 83 // value, don't take an extra argument. 84 // The table must at least contain a pointer for malloc_hook and free_hook 85 // functions. They will be used as fallback if no pointer is given for 86 // other allocation functions, like calloc_hook. 87 namespace mozilla { 88 namespace detail { 89 template <typename R, typename... Args> 90 struct AllocHookType { 91 using Type = R (*)(R, Args...); 92 }; 93 94 template <typename... Args> 95 struct AllocHookType<void, Args...> { 96 using Type = void (*)(Args...); 97 }; 98 99 } // namespace detail 100 } // namespace mozilla 101 102 # define MALLOC_DECL(name, return_type, ...) \ 103 typename mozilla::detail::AllocHookType<return_type, ##__VA_ARGS__>::Type \ 104 name##_hook; 105 106 typedef struct { 107 # include "malloc_decls.h" 108 // Like free_hook, but called before realloc_hook. free_hook is called 109 // instead of not given. 110 void (*realloc_hook_before)(void* aPtr); 111 } malloc_hook_table_t; 112 113 namespace mozilla { 114 namespace dmd { 115 struct DMDFuncs; 116 } // namespace dmd 117 118 namespace phc { 119 class AddrInfo; 120 } // namespace phc 121 122 // Callbacks to register debug file handles for Poison IO interpose. 123 // See Mozilla(|Un)RegisterDebugHandle in xpcom/build/PoisonIOInterposer.h 124 struct DebugFdRegistry { 125 virtual void RegisterHandle(intptr_t aFd); 126 127 virtual void UnRegisterHandle(intptr_t aFd); 128 }; 129 130 } // namespace mozilla 131 132 struct ReplaceMallocBridge { 133 ReplaceMallocBridge() : mVersion(4) {} 134 135 // This method was added in version 1 of the bridge. 136 virtual mozilla::dmd::DMDFuncs* GetDMDFuncs() { return nullptr; } 137 138 // Send a DebugFdRegistry instance to the replace-malloc library so that 139 // it can register/unregister file descriptors whenever needed. The 140 // instance is valid until the process dies. 141 // This method was added in version 2 of the bridge. 142 virtual void InitDebugFd(mozilla::DebugFdRegistry&) {} 143 144 // Register a list of malloc functions and hook functions to the 145 // replace-malloc library so that it can choose to dispatch to them 146 // when needed. The details of what is dispatched when is left to the 147 // replace-malloc library. 148 // Passing a nullptr for either table will unregister a previously 149 // registered table under the same name. 150 // Returns nullptr if registration failed. 151 // If registration succeeded, a table of "pure" malloc functions is 152 // returned. Those "pure" malloc functions won't call hooks. 153 // /!\ Do not rely on registration/unregistration to be instantaneous. 154 // Functions from a previously registered table may still be called for 155 // a brief time after RegisterHook returns. 156 // This method was added in version 3 of the bridge. 157 virtual const malloc_table_t* RegisterHook( 158 const char* aName, const malloc_table_t* aTable, 159 const malloc_hook_table_t* aHookTable) { 160 return nullptr; 161 } 162 163 // If this is a PHC-handled address, return true, and if an AddrInfo is 164 // provided, fill in all of its fields. Otherwise, return false and leave 165 // AddrInfo unchanged. 166 // This method was added in version 4 of the bridge. 167 virtual bool IsPHCAllocation(const void*, mozilla::phc::AddrInfo*) { 168 return false; 169 } 170 171 // Disable PHC allocations on the current thread. Only useful for tests. Note 172 // that PHC deallocations will still occur as needed. 173 // This method was added in version 4 of the bridge. 174 virtual void DisablePHCOnCurrentThread() {} 175 176 // Re-enable PHC allocations on the current thread. Only useful for tests. 177 // This method was added in version 4 of the bridge. 178 virtual void ReenablePHCOnCurrentThread() {} 179 180 // Test whether PHC allocations are enabled on the current thread. Only 181 // useful for tests. 182 // This method was added in version 4 of the bridge. 183 virtual bool IsPHCEnabledOnCurrentThread() { return false; } 184 185 # ifndef REPLACE_MALLOC_IMPL 186 // Returns the replace-malloc bridge if its version is at least the 187 // requested one. 188 static ReplaceMallocBridge* Get(int aMinimumVersion) { 189 static ReplaceMallocBridge* sSingleton = get_bridge(); 190 return (sSingleton && sSingleton->mVersion >= aMinimumVersion) ? sSingleton 191 : nullptr; 192 } 193 # endif 194 195 protected: 196 const int mVersion; 197 }; 198 199 # ifndef REPLACE_MALLOC_IMPL 200 // Class containing wrappers for calls to ReplaceMallocBridge methods. 201 // Those wrappers need to be static methods in a class because compilers 202 // complain about unused static global functions, and linkers complain 203 // about multiple definitions of non-static global functions. 204 // Using a separate class from ReplaceMallocBridge allows the function 205 // names to be identical. 206 struct ReplaceMalloc { 207 // Don't call this method from performance critical code. Use 208 // mozilla::dmd::DMDFuncs::Get() instead, it has less overhead. 209 static mozilla::dmd::DMDFuncs* GetDMDFuncs() { 210 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 1); 211 return singleton ? singleton->GetDMDFuncs() : nullptr; 212 } 213 214 static void InitDebugFd(mozilla::DebugFdRegistry& aRegistry) { 215 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 2); 216 if (singleton) { 217 singleton->InitDebugFd(aRegistry); 218 } 219 } 220 221 static const malloc_table_t* RegisterHook( 222 const char* aName, const malloc_table_t* aTable, 223 const malloc_hook_table_t* aHookTable) { 224 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 3); 225 return singleton ? singleton->RegisterHook(aName, aTable, aHookTable) 226 : nullptr; 227 } 228 229 static bool IsPHCAllocation(const void* aPtr, mozilla::phc::AddrInfo* aOut) { 230 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4); 231 return singleton ? singleton->IsPHCAllocation(aPtr, aOut) : false; 232 } 233 234 static void DisablePHCOnCurrentThread() { 235 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4); 236 if (singleton) { 237 singleton->DisablePHCOnCurrentThread(); 238 } 239 } 240 241 static void ReenablePHCOnCurrentThread() { 242 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4); 243 if (singleton) { 244 singleton->ReenablePHCOnCurrentThread(); 245 } 246 } 247 248 static bool IsPHCEnabledOnCurrentThread() { 249 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4); 250 return singleton ? singleton->IsPHCEnabledOnCurrentThread() : false; 251 } 252 }; 253 # endif 254 255 #endif // __cplusplus 256 257 #endif // replace_malloc_bridge_h 258