summaryrefslogtreecommitdiff
path: root/memory/build/replace_malloc_bridge.h
blob: 301b165ebbaf5a7e3752df082ecf44523fd41171 (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
/* -*- 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;


/* 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.
 */
#define MALLOC_DECL(name, return_type, ...) \
  return_type (*name ## _hook)(return_type, __VA_ARGS__);
#define MALLOC_DECL_VOID(name, ...) \
  void (*name ## _hook)(__VA_ARGS__);

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;

MOZ_END_EXTERN_C

#ifdef __cplusplus

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

/* 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(3) {}

  /* 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; }

#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;
  }
};
#endif

#endif /* __cplusplus */

#endif /* replace_malloc_bridge_h */