summaryrefslogtreecommitdiffstats
path: root/memory/build/replace_malloc_bridge.h
blob: 358d9f25c4b3db3abe0e185b69a3a64d2cd21b2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef replace_malloc_bridge_h
#define replace_malloc_bridge_h

// The replace-malloc bridge allows bidirectional method calls between
// a program and the replace-malloc library that has been loaded for it.
// In Firefox, this is used to allow method calls between code in libxul
// and code in the replace-malloc library, without libxul needing to link
// against that library or vice-versa.
//
// Subsystems can add methods for their own need. Replace-malloc libraries
// can decide to implement those methods or not.
//
// Replace-malloc libraries can provide such a bridge by implementing
// a ReplaceMallocBridge-derived class, and a replace_get_bridge function
// returning an instance of that class. The default methods in
// ReplaceMallocBridge are expected to return values that callers would
// understand as "the bridge doesn't implement this method", so that a
// replace-malloc library doesn't have to implement all methods.
//
// The ReplaceMallocBridge class contains definitions for methods for
// all replace-malloc libraries. Each library picks the methods it wants
// to reply to in its ReplaceMallocBridge-derived class instance.
// All methods of ReplaceMallocBridge must be virtual. Similarly,
// anything passed as an argument to those methods must be plain data, or
// an instance of a class with only virtual methods.
//
// Binary compatibility is expected to be maintained, such that a newer
// Firefox can be used with an old replace-malloc library, or an old
// Firefox can be used with a newer replace-malloc library. As such, only
// new virtual methods should be added to ReplaceMallocBridge, and
// each change should have a corresponding bump of the mVersion value.
// At the same time, each virtual method should have a corresponding
// wrapper calling the virtual method on the instance from
// ReplaceMallocBridge::Get(), giving it the version the virtual method
// was added.
//
// Parts that are not relevant to the replace-malloc library end of the
// bridge are hidden when REPLACE_MALLOC_IMPL is not defined, which is
// the case when including replace_malloc.h.

struct ReplaceMallocBridge;

#include "mozilla/Types.h"

MOZ_BEGIN_EXTERN_C

#ifndef REPLACE_MALLOC_IMPL
// Returns the replace-malloc bridge if there is one to be returned.
MFBT_API ReplaceMallocBridge* get_bridge();
#endif

// Table of malloc functions.
//   e.g. void* (*malloc)(size_t), etc.

#define MALLOC_DECL(name, return_type, ...) \
  typedef return_type(name##_impl_t)(__VA_ARGS__);

#include "malloc_decls.h"

#define MALLOC_DECL(name, return_type, ...) name##_impl_t* name;

typedef struct {
#include "malloc_decls.h"
} malloc_table_t;

MOZ_END_EXTERN_C

#ifdef __cplusplus

// Table of malloc hook functions.
// Those functions are called with the arguments and results of malloc
// functions after they are called.
//   e.g. void* (*malloc_hook)(void*, size_t), etc.
// They can either return the result they're given, or alter it before
// returning it.
// The hooks corresponding to functions, like free(void*), that return no
// value, don't take an extra argument.
// The table must at least contain a pointer for malloc_hook and free_hook
// functions. They will be used as fallback if no pointer is given for
// other allocation functions, like calloc_hook.
namespace mozilla {
namespace detail {
template <typename R, typename... Args>
struct AllocHookType {
  using Type = R (*)(R, Args...);
};

template <typename... Args>
struct AllocHookType<void, Args...> {
  using Type = void (*)(Args...);
};

}  // namespace detail
}  // namespace mozilla

#  define MALLOC_DECL(name, return_type, ...)                                 \
    typename mozilla::detail::AllocHookType<return_type, ##__VA_ARGS__>::Type \
        name##_hook;

typedef struct {
#  include "malloc_decls.h"
  // Like free_hook, but called before realloc_hook. free_hook is called
  // instead of not given.
  void (*realloc_hook_before)(void* aPtr);
} malloc_hook_table_t;

namespace mozilla {
namespace dmd {
struct DMDFuncs;
}  // namespace dmd

namespace phc {
class AddrInfo;
}  // namespace phc

// Callbacks to register debug file handles for Poison IO interpose.
// See Mozilla(|Un)RegisterDebugHandle in xpcom/build/PoisonIOInterposer.h
struct DebugFdRegistry {
  virtual void RegisterHandle(intptr_t aFd);

  virtual void UnRegisterHandle(intptr_t aFd);
};

}  // namespace mozilla

struct ReplaceMallocBridge {
  ReplaceMallocBridge() : mVersion(4) {}

  // This method was added in version 1 of the bridge.
  virtual mozilla::dmd::DMDFuncs* GetDMDFuncs() { return nullptr; }

  // Send a DebugFdRegistry instance to the replace-malloc library so that
  // it can register/unregister file descriptors whenever needed. The
  // instance is valid until the process dies.
  // This method was added in version 2 of the bridge.
  virtual void InitDebugFd(mozilla::DebugFdRegistry&) {}

  // Register a list of malloc functions and hook functions to the
  // replace-malloc library so that it can choose to dispatch to them
  // when needed. The details of what is dispatched when is left to the
  // replace-malloc library.
  // Passing a nullptr for either table will unregister a previously
  // registered table under the same name.
  // Returns nullptr if registration failed.
  // If registration succeeded, a table of "pure" malloc functions is
  // returned. Those "pure" malloc functions won't call hooks.
  // /!\ Do not rely on registration/unregistration to be instantaneous.
  // Functions from a previously registered table may still be called for
  // a brief time after RegisterHook returns.
  // This method was added in version 3 of the bridge.
  virtual const malloc_table_t* RegisterHook(
      const char* aName, const malloc_table_t* aTable,
      const malloc_hook_table_t* aHookTable) {
    return nullptr;
  }

  // If this is a PHC-handled address, return true, and if an AddrInfo is
  // provided, fill in all of its fields. Otherwise, return false and leave
  // AddrInfo unchanged.
  // This method was added in version 4 of the bridge.
  virtual bool IsPHCAllocation(const void*, mozilla::phc::AddrInfo*) {
    return false;
  }

  // Disable PHC allocations on the current thread. Only useful for tests. Note
  // that PHC deallocations will still occur as needed.
  // This method was added in version 4 of the bridge.
  virtual void DisablePHCOnCurrentThread() {}

  // Re-enable PHC allocations on the current thread. Only useful for tests.
  // This method was added in version 4 of the bridge.
  virtual void ReenablePHCOnCurrentThread() {}

  // Test whether PHC allocations are enabled on the current thread. Only
  // useful for tests.
  // This method was added in version 4 of the bridge.
  virtual bool IsPHCEnabledOnCurrentThread() { return false; }

#  ifndef REPLACE_MALLOC_IMPL
  // Returns the replace-malloc bridge if its version is at least the
  // requested one.
  static ReplaceMallocBridge* Get(int aMinimumVersion) {
    static ReplaceMallocBridge* sSingleton = get_bridge();
    return (sSingleton && sSingleton->mVersion >= aMinimumVersion) ? sSingleton
                                                                   : nullptr;
  }
#  endif

 protected:
  const int mVersion;
};

#  ifndef REPLACE_MALLOC_IMPL
// Class containing wrappers for calls to ReplaceMallocBridge methods.
// Those wrappers need to be static methods in a class because compilers
// complain about unused static global functions, and linkers complain
// about multiple definitions of non-static global functions.
// Using a separate class from ReplaceMallocBridge allows the function
// names to be identical.
struct ReplaceMalloc {
  // Don't call this method from performance critical code. Use
  // mozilla::dmd::DMDFuncs::Get() instead, it has less overhead.
  static mozilla::dmd::DMDFuncs* GetDMDFuncs() {
    auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 1);
    return singleton ? singleton->GetDMDFuncs() : nullptr;
  }

  static void InitDebugFd(mozilla::DebugFdRegistry& aRegistry) {
    auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 2);
    if (singleton) {
      singleton->InitDebugFd(aRegistry);
    }
  }

  static const malloc_table_t* RegisterHook(
      const char* aName, const malloc_table_t* aTable,
      const malloc_hook_table_t* aHookTable) {
    auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 3);
    return singleton ? singleton->RegisterHook(aName, aTable, aHookTable)
                     : nullptr;
  }

  static bool IsPHCAllocation(const void* aPtr, mozilla::phc::AddrInfo* aOut) {
    auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
    return singleton ? singleton->IsPHCAllocation(aPtr, aOut) : false;
  }

  static void DisablePHCOnCurrentThread() {
    auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
    if (singleton) {
      singleton->DisablePHCOnCurrentThread();
    }
  }

  static void ReenablePHCOnCurrentThread() {
    auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
    if (singleton) {
      singleton->ReenablePHCOnCurrentThread();
    }
  }

  static bool IsPHCEnabledOnCurrentThread() {
    auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4);
    return singleton ? singleton->IsPHCEnabledOnCurrentThread() : false;
  }
};
#  endif

#endif  // __cplusplus

#endif  // replace_malloc_bridge_h