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 file, 5 * 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 /* 11 * The replace-malloc bridge allows bidirectional method calls between 12 * a program and the replace-malloc library that has been loaded for it. 13 * In Firefox, this is used to allow method calls between code in libxul 14 * and code in the replace-malloc library, without libxul needing to link 15 * against that library or vice-versa. 16 * 17 * Subsystems can add methods for their own need. Replace-malloc libraries 18 * can decide to implement those methods or not. 19 * 20 * Replace-malloc libraries can provide such a bridge by implementing 21 * a ReplaceMallocBridge-derived class, and a replace_get_bridge function 22 * returning an instance of that class. The default methods in 23 * ReplaceMallocBridge are expected to return values that callers would 24 * understand as "the bridge doesn't implement this method", so that a 25 * replace-malloc library doesn't have to implement all methods. 26 * 27 * The ReplaceMallocBridge class contains definitions for methods for 28 * all replace-malloc libraries. Each library picks the methods it wants 29 * to reply to in its ReplaceMallocBridge-derived class instance. 30 * All methods of ReplaceMallocBridge must be virtual. Similarly, 31 * anything passed as an argument to those methods must be plain data, or 32 * an instance of a class with only virtual methods. 33 * 34 * Binary compatibility is expected to be maintained, such that a newer 35 * Firefox can be used with an old replace-malloc library, or an old 36 * Firefox can be used with a newer replace-malloc library. As such, only 37 * new virtual methods should be added to ReplaceMallocBridge, and 38 * each change should have a corresponding bump of the mVersion value. 39 * At the same time, each virtual method should have a corresponding 40 * wrapper calling the virtual method on the instance from 41 * ReplaceMallocBridge::Get(), giving it the version the virtual method 42 * was added. 43 * 44 * Parts that are not relevant to the replace-malloc library end of the 45 * bridge are hidden when REPLACE_MALLOC_IMPL is not defined, which is 46 * the case when including replace_malloc.h. 47 */ 48 49 struct ReplaceMallocBridge; 50 51 #include "mozilla/Types.h" 52 53 MOZ_BEGIN_EXTERN_C 54 55 #ifndef REPLACE_MALLOC_IMPL 56 /* Returns the replace-malloc bridge if there is one to be returned. */ 57 MFBT_API ReplaceMallocBridge* get_bridge(); 58 #endif 59 60 /* Table of malloc functions. 61 * e.g. void* (*malloc)(size_t), etc. 62 */ 63 #define MALLOC_DECL(name, return_type, ...) \ 64 typedef return_type(name ## _impl_t)(__VA_ARGS__); 65 66 #include "malloc_decls.h" 67 68 #define MALLOC_DECL(name, return_type, ...) \ 69 name ## _impl_t * name; 70 71 typedef struct { 72 #include "malloc_decls.h" 73 } malloc_table_t; 74 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 */ 88 #define MALLOC_DECL(name, return_type, ...) \ 89 return_type (*name ## _hook)(return_type, __VA_ARGS__); 90 #define MALLOC_DECL_VOID(name, ...) \ 91 void (*name ## _hook)(__VA_ARGS__); 92 93 typedef struct { 94 #include "malloc_decls.h" 95 /* Like free_hook, but called before realloc_hook. free_hook is called 96 * instead of not given. */ 97 void (*realloc_hook_before)(void* aPtr); 98 } malloc_hook_table_t; 99 100 MOZ_END_EXTERN_C 101 102 #ifdef __cplusplus 103 104 namespace mozilla { 105 namespace dmd { 106 struct DMDFuncs; 107 } // namespace dmd 108 109 /* Callbacks to register debug file handles for Poison IO interpose. 110 * See Mozilla(|Un)RegisterDebugHandle in xpcom/build/PoisonIOInterposer.h */ 111 struct DebugFdRegistry 112 { 113 virtual void RegisterHandle(intptr_t aFd); 114 115 virtual void UnRegisterHandle(intptr_t aFd); 116 }; 117 118 } // namespace mozilla 119 120 struct ReplaceMallocBridge 121 { ReplaceMallocBridgeReplaceMallocBridge122 ReplaceMallocBridge() : mVersion(3) {} 123 124 /* This method was added in version 1 of the bridge. */ GetDMDFuncsReplaceMallocBridge125 virtual mozilla::dmd::DMDFuncs* GetDMDFuncs() { return nullptr; } 126 127 /* Send a DebugFdRegistry instance to the replace-malloc library so that 128 * it can register/unregister file descriptors whenever needed. The 129 * instance is valid until the process dies. 130 * This method was added in version 2 of the bridge. */ InitDebugFdReplaceMallocBridge131 virtual void InitDebugFd(mozilla::DebugFdRegistry&) {} 132 133 /* Register a list of malloc functions and hook functions to the 134 * replace-malloc library so that it can choose to dispatch to them 135 * when needed. The details of what is dispatched when is left to the 136 * replace-malloc library. 137 * Passing a nullptr for either table will unregister a previously 138 * registered table under the same name. 139 * Returns nullptr if registration failed. 140 * If registration succeeded, a table of "pure" malloc functions is 141 * returned. Those "pure" malloc functions won't call hooks. 142 * /!\ Do not rely on registration/unregistration to be instantaneous. 143 * Functions from a previously registered table may still be called for 144 * a brief time after RegisterHook returns. 145 * This method was added in version 3 of the bridge. */ 146 virtual const malloc_table_t* RegisterHookReplaceMallocBridge147 RegisterHook(const char* aName, const malloc_table_t* aTable, 148 const malloc_hook_table_t* aHookTable) { return nullptr; } 149 150 #ifndef REPLACE_MALLOC_IMPL 151 /* Returns the replace-malloc bridge if its version is at least the 152 * requested one. */ GetReplaceMallocBridge153 static ReplaceMallocBridge* Get(int aMinimumVersion) { 154 static ReplaceMallocBridge* sSingleton = get_bridge(); 155 return (sSingleton && sSingleton->mVersion >= aMinimumVersion) 156 ? sSingleton : nullptr; 157 } 158 #endif 159 160 protected: 161 const int mVersion; 162 }; 163 164 #ifndef REPLACE_MALLOC_IMPL 165 /* Class containing wrappers for calls to ReplaceMallocBridge methods. 166 * Those wrappers need to be static methods in a class because compilers 167 * complain about unused static global functions, and linkers complain 168 * about multiple definitions of non-static global functions. 169 * Using a separate class from ReplaceMallocBridge allows the function 170 * names to be identical. */ 171 struct ReplaceMalloc 172 { 173 /* Don't call this method from performance critical code. Use 174 * mozilla::dmd::DMDFuncs::Get() instead, it has less overhead. */ GetDMDFuncsReplaceMalloc175 static mozilla::dmd::DMDFuncs* GetDMDFuncs() 176 { 177 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 1); 178 return singleton ? singleton->GetDMDFuncs() : nullptr; 179 } 180 InitDebugFdReplaceMalloc181 static void InitDebugFd(mozilla::DebugFdRegistry& aRegistry) 182 { 183 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 2); 184 if (singleton) { 185 singleton->InitDebugFd(aRegistry); 186 } 187 } 188 189 static const malloc_table_t* RegisterHookReplaceMalloc190 RegisterHook(const char* aName, const malloc_table_t* aTable, 191 const malloc_hook_table_t* aHookTable) 192 { 193 auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 3); 194 return singleton ? singleton->RegisterHook(aName, aTable, aHookTable) 195 : nullptr; 196 } 197 }; 198 #endif 199 200 #endif /* __cplusplus */ 201 202 #endif /* replace_malloc_bridge_h */ 203