annotate libsanitizer/sanitizer_common/sanitizer_common.cc @ 138:fc828634a951

merge
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Thu, 08 Nov 2018 14:17:14 +0900
parents 04ced10e8804
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 //===-- sanitizer_common.cc -----------------------------------------------===//
kono
parents:
diff changeset
2 //
kono
parents:
diff changeset
3 // This file is distributed under the University of Illinois Open Source
kono
parents:
diff changeset
4 // License. See LICENSE.TXT for details.
kono
parents:
diff changeset
5 //
kono
parents:
diff changeset
6 //===----------------------------------------------------------------------===//
kono
parents:
diff changeset
7 //
kono
parents:
diff changeset
8 // This file is shared between AddressSanitizer and ThreadSanitizer
kono
parents:
diff changeset
9 // run-time libraries.
kono
parents:
diff changeset
10 //===----------------------------------------------------------------------===//
kono
parents:
diff changeset
11
kono
parents:
diff changeset
12 #include "sanitizer_common.h"
kono
parents:
diff changeset
13 #include "sanitizer_allocator_interface.h"
kono
parents:
diff changeset
14 #include "sanitizer_allocator_internal.h"
kono
parents:
diff changeset
15 #include "sanitizer_flags.h"
kono
parents:
diff changeset
16 #include "sanitizer_libc.h"
kono
parents:
diff changeset
17 #include "sanitizer_placement_new.h"
kono
parents:
diff changeset
18 #include "sanitizer_stacktrace_printer.h"
kono
parents:
diff changeset
19 #include "sanitizer_symbolizer.h"
kono
parents:
diff changeset
20
kono
parents:
diff changeset
21 namespace __sanitizer {
kono
parents:
diff changeset
22
kono
parents:
diff changeset
23 const char *SanitizerToolName = "SanitizerTool";
kono
parents:
diff changeset
24
kono
parents:
diff changeset
25 atomic_uint32_t current_verbosity;
kono
parents:
diff changeset
26 uptr PageSizeCached;
kono
parents:
diff changeset
27
kono
parents:
diff changeset
28 // PID of the tracer task in StopTheWorld. It shares the address space with the
kono
parents:
diff changeset
29 // main process, but has a different PID and thus requires special handling.
kono
parents:
diff changeset
30 uptr stoptheworld_tracer_pid = 0;
kono
parents:
diff changeset
31 // Cached pid of parent process - if the parent process dies, we want to keep
kono
parents:
diff changeset
32 // writing to the same log file.
kono
parents:
diff changeset
33 uptr stoptheworld_tracer_ppid = 0;
kono
parents:
diff changeset
34
kono
parents:
diff changeset
35 StaticSpinMutex CommonSanitizerReportMutex;
kono
parents:
diff changeset
36
kono
parents:
diff changeset
37 void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
kono
parents:
diff changeset
38 const char *mmap_type, error_t err,
kono
parents:
diff changeset
39 bool raw_report) {
kono
parents:
diff changeset
40 static int recursion_count;
kono
parents:
diff changeset
41 if (raw_report || recursion_count) {
kono
parents:
diff changeset
42 // If raw report is requested or we went into recursion, just die.
kono
parents:
diff changeset
43 // The Report() and CHECK calls below may call mmap recursively and fail.
kono
parents:
diff changeset
44 RawWrite("ERROR: Failed to mmap\n");
kono
parents:
diff changeset
45 Die();
kono
parents:
diff changeset
46 }
kono
parents:
diff changeset
47 recursion_count++;
kono
parents:
diff changeset
48 Report("ERROR: %s failed to "
kono
parents:
diff changeset
49 "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
kono
parents:
diff changeset
50 SanitizerToolName, mmap_type, size, size, mem_type, err);
kono
parents:
diff changeset
51 #if !SANITIZER_GO
kono
parents:
diff changeset
52 DumpProcessMap();
kono
parents:
diff changeset
53 #endif
kono
parents:
diff changeset
54 UNREACHABLE("unable to mmap");
kono
parents:
diff changeset
55 }
kono
parents:
diff changeset
56
kono
parents:
diff changeset
57 typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
kono
parents:
diff changeset
58 typedef bool U32ComparisonFunction(const u32 &a, const u32 &b);
kono
parents:
diff changeset
59
kono
parents:
diff changeset
60 template<class T>
kono
parents:
diff changeset
61 static inline bool CompareLess(const T &a, const T &b) {
kono
parents:
diff changeset
62 return a < b;
kono
parents:
diff changeset
63 }
kono
parents:
diff changeset
64
kono
parents:
diff changeset
65 void SortArray(uptr *array, uptr size) {
kono
parents:
diff changeset
66 InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);
kono
parents:
diff changeset
67 }
kono
parents:
diff changeset
68
kono
parents:
diff changeset
69 void SortArray(u32 *array, uptr size) {
kono
parents:
diff changeset
70 InternalSort<u32*, U32ComparisonFunction>(&array, size, CompareLess);
kono
parents:
diff changeset
71 }
kono
parents:
diff changeset
72
kono
parents:
diff changeset
73 const char *StripPathPrefix(const char *filepath,
kono
parents:
diff changeset
74 const char *strip_path_prefix) {
kono
parents:
diff changeset
75 if (!filepath) return nullptr;
kono
parents:
diff changeset
76 if (!strip_path_prefix) return filepath;
kono
parents:
diff changeset
77 const char *res = filepath;
kono
parents:
diff changeset
78 if (const char *pos = internal_strstr(filepath, strip_path_prefix))
kono
parents:
diff changeset
79 res = pos + internal_strlen(strip_path_prefix);
kono
parents:
diff changeset
80 if (res[0] == '.' && res[1] == '/')
kono
parents:
diff changeset
81 res += 2;
kono
parents:
diff changeset
82 return res;
kono
parents:
diff changeset
83 }
kono
parents:
diff changeset
84
kono
parents:
diff changeset
85 const char *StripModuleName(const char *module) {
kono
parents:
diff changeset
86 if (!module)
kono
parents:
diff changeset
87 return nullptr;
kono
parents:
diff changeset
88 if (SANITIZER_WINDOWS) {
kono
parents:
diff changeset
89 // On Windows, both slash and backslash are possible.
kono
parents:
diff changeset
90 // Pick the one that goes last.
kono
parents:
diff changeset
91 if (const char *bslash_pos = internal_strrchr(module, '\\'))
kono
parents:
diff changeset
92 return StripModuleName(bslash_pos + 1);
kono
parents:
diff changeset
93 }
kono
parents:
diff changeset
94 if (const char *slash_pos = internal_strrchr(module, '/')) {
kono
parents:
diff changeset
95 return slash_pos + 1;
kono
parents:
diff changeset
96 }
kono
parents:
diff changeset
97 return module;
kono
parents:
diff changeset
98 }
kono
parents:
diff changeset
99
kono
parents:
diff changeset
100 void ReportErrorSummary(const char *error_message, const char *alt_tool_name) {
kono
parents:
diff changeset
101 if (!common_flags()->print_summary)
kono
parents:
diff changeset
102 return;
kono
parents:
diff changeset
103 InternalScopedString buff(kMaxSummaryLength);
kono
parents:
diff changeset
104 buff.append("SUMMARY: %s: %s",
kono
parents:
diff changeset
105 alt_tool_name ? alt_tool_name : SanitizerToolName, error_message);
kono
parents:
diff changeset
106 __sanitizer_report_error_summary(buff.data());
kono
parents:
diff changeset
107 }
kono
parents:
diff changeset
108
kono
parents:
diff changeset
109 #if !SANITIZER_GO
kono
parents:
diff changeset
110 void ReportErrorSummary(const char *error_type, const AddressInfo &info,
kono
parents:
diff changeset
111 const char *alt_tool_name) {
kono
parents:
diff changeset
112 if (!common_flags()->print_summary) return;
kono
parents:
diff changeset
113 InternalScopedString buff(kMaxSummaryLength);
kono
parents:
diff changeset
114 buff.append("%s ", error_type);
kono
parents:
diff changeset
115 RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
kono
parents:
diff changeset
116 common_flags()->strip_path_prefix);
kono
parents:
diff changeset
117 ReportErrorSummary(buff.data(), alt_tool_name);
kono
parents:
diff changeset
118 }
kono
parents:
diff changeset
119 #endif
kono
parents:
diff changeset
120
kono
parents:
diff changeset
121 // Removes the ANSI escape sequences from the input string (in-place).
kono
parents:
diff changeset
122 void RemoveANSIEscapeSequencesFromString(char *str) {
kono
parents:
diff changeset
123 if (!str)
kono
parents:
diff changeset
124 return;
kono
parents:
diff changeset
125
kono
parents:
diff changeset
126 // We are going to remove the escape sequences in place.
kono
parents:
diff changeset
127 char *s = str;
kono
parents:
diff changeset
128 char *z = str;
kono
parents:
diff changeset
129 while (*s != '\0') {
kono
parents:
diff changeset
130 CHECK_GE(s, z);
kono
parents:
diff changeset
131 // Skip over ANSI escape sequences with pointer 's'.
kono
parents:
diff changeset
132 if (*s == '\033' && *(s + 1) == '[') {
kono
parents:
diff changeset
133 s = internal_strchrnul(s, 'm');
kono
parents:
diff changeset
134 if (*s == '\0') {
kono
parents:
diff changeset
135 break;
kono
parents:
diff changeset
136 }
kono
parents:
diff changeset
137 s++;
kono
parents:
diff changeset
138 continue;
kono
parents:
diff changeset
139 }
kono
parents:
diff changeset
140 // 's' now points at a character we want to keep. Copy over the buffer
kono
parents:
diff changeset
141 // content if the escape sequence has been perviously skipped andadvance
kono
parents:
diff changeset
142 // both pointers.
kono
parents:
diff changeset
143 if (s != z)
kono
parents:
diff changeset
144 *z = *s;
kono
parents:
diff changeset
145
kono
parents:
diff changeset
146 // If we have not seen an escape sequence, just advance both pointers.
kono
parents:
diff changeset
147 z++;
kono
parents:
diff changeset
148 s++;
kono
parents:
diff changeset
149 }
kono
parents:
diff changeset
150
kono
parents:
diff changeset
151 // Null terminate the string.
kono
parents:
diff changeset
152 *z = '\0';
kono
parents:
diff changeset
153 }
kono
parents:
diff changeset
154
kono
parents:
diff changeset
155 void LoadedModule::set(const char *module_name, uptr base_address) {
kono
parents:
diff changeset
156 clear();
kono
parents:
diff changeset
157 full_name_ = internal_strdup(module_name);
kono
parents:
diff changeset
158 base_address_ = base_address;
kono
parents:
diff changeset
159 }
kono
parents:
diff changeset
160
kono
parents:
diff changeset
161 void LoadedModule::set(const char *module_name, uptr base_address,
kono
parents:
diff changeset
162 ModuleArch arch, u8 uuid[kModuleUUIDSize],
kono
parents:
diff changeset
163 bool instrumented) {
kono
parents:
diff changeset
164 set(module_name, base_address);
kono
parents:
diff changeset
165 arch_ = arch;
kono
parents:
diff changeset
166 internal_memcpy(uuid_, uuid, sizeof(uuid_));
kono
parents:
diff changeset
167 instrumented_ = instrumented;
kono
parents:
diff changeset
168 }
kono
parents:
diff changeset
169
kono
parents:
diff changeset
170 void LoadedModule::clear() {
kono
parents:
diff changeset
171 InternalFree(full_name_);
kono
parents:
diff changeset
172 base_address_ = 0;
kono
parents:
diff changeset
173 max_executable_address_ = 0;
kono
parents:
diff changeset
174 full_name_ = nullptr;
kono
parents:
diff changeset
175 arch_ = kModuleArchUnknown;
kono
parents:
diff changeset
176 internal_memset(uuid_, 0, kModuleUUIDSize);
kono
parents:
diff changeset
177 instrumented_ = false;
kono
parents:
diff changeset
178 while (!ranges_.empty()) {
kono
parents:
diff changeset
179 AddressRange *r = ranges_.front();
kono
parents:
diff changeset
180 ranges_.pop_front();
kono
parents:
diff changeset
181 InternalFree(r);
kono
parents:
diff changeset
182 }
kono
parents:
diff changeset
183 }
kono
parents:
diff changeset
184
kono
parents:
diff changeset
185 void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable,
kono
parents:
diff changeset
186 bool writable, const char *name) {
kono
parents:
diff changeset
187 void *mem = InternalAlloc(sizeof(AddressRange));
kono
parents:
diff changeset
188 AddressRange *r =
kono
parents:
diff changeset
189 new(mem) AddressRange(beg, end, executable, writable, name);
kono
parents:
diff changeset
190 ranges_.push_back(r);
kono
parents:
diff changeset
191 if (executable && end > max_executable_address_)
kono
parents:
diff changeset
192 max_executable_address_ = end;
kono
parents:
diff changeset
193 }
kono
parents:
diff changeset
194
kono
parents:
diff changeset
195 bool LoadedModule::containsAddress(uptr address) const {
kono
parents:
diff changeset
196 for (const AddressRange &r : ranges()) {
kono
parents:
diff changeset
197 if (r.beg <= address && address < r.end)
kono
parents:
diff changeset
198 return true;
kono
parents:
diff changeset
199 }
kono
parents:
diff changeset
200 return false;
kono
parents:
diff changeset
201 }
kono
parents:
diff changeset
202
kono
parents:
diff changeset
203 static atomic_uintptr_t g_total_mmaped;
kono
parents:
diff changeset
204
kono
parents:
diff changeset
205 void IncreaseTotalMmap(uptr size) {
kono
parents:
diff changeset
206 if (!common_flags()->mmap_limit_mb) return;
kono
parents:
diff changeset
207 uptr total_mmaped =
kono
parents:
diff changeset
208 atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
kono
parents:
diff changeset
209 // Since for now mmap_limit_mb is not a user-facing flag, just kill
kono
parents:
diff changeset
210 // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
kono
parents:
diff changeset
211 RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
kono
parents:
diff changeset
212 }
kono
parents:
diff changeset
213
kono
parents:
diff changeset
214 void DecreaseTotalMmap(uptr size) {
kono
parents:
diff changeset
215 if (!common_flags()->mmap_limit_mb) return;
kono
parents:
diff changeset
216 atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
kono
parents:
diff changeset
217 }
kono
parents:
diff changeset
218
kono
parents:
diff changeset
219 bool TemplateMatch(const char *templ, const char *str) {
kono
parents:
diff changeset
220 if ((!str) || str[0] == 0)
kono
parents:
diff changeset
221 return false;
kono
parents:
diff changeset
222 bool start = false;
kono
parents:
diff changeset
223 if (templ && templ[0] == '^') {
kono
parents:
diff changeset
224 start = true;
kono
parents:
diff changeset
225 templ++;
kono
parents:
diff changeset
226 }
kono
parents:
diff changeset
227 bool asterisk = false;
kono
parents:
diff changeset
228 while (templ && templ[0]) {
kono
parents:
diff changeset
229 if (templ[0] == '*') {
kono
parents:
diff changeset
230 templ++;
kono
parents:
diff changeset
231 start = false;
kono
parents:
diff changeset
232 asterisk = true;
kono
parents:
diff changeset
233 continue;
kono
parents:
diff changeset
234 }
kono
parents:
diff changeset
235 if (templ[0] == '$')
kono
parents:
diff changeset
236 return str[0] == 0 || asterisk;
kono
parents:
diff changeset
237 if (str[0] == 0)
kono
parents:
diff changeset
238 return false;
kono
parents:
diff changeset
239 char *tpos = (char*)internal_strchr(templ, '*');
kono
parents:
diff changeset
240 char *tpos1 = (char*)internal_strchr(templ, '$');
kono
parents:
diff changeset
241 if ((!tpos) || (tpos1 && tpos1 < tpos))
kono
parents:
diff changeset
242 tpos = tpos1;
kono
parents:
diff changeset
243 if (tpos)
kono
parents:
diff changeset
244 tpos[0] = 0;
kono
parents:
diff changeset
245 const char *str0 = str;
kono
parents:
diff changeset
246 const char *spos = internal_strstr(str, templ);
kono
parents:
diff changeset
247 str = spos + internal_strlen(templ);
kono
parents:
diff changeset
248 templ = tpos;
kono
parents:
diff changeset
249 if (tpos)
kono
parents:
diff changeset
250 tpos[0] = tpos == tpos1 ? '$' : '*';
kono
parents:
diff changeset
251 if (!spos)
kono
parents:
diff changeset
252 return false;
kono
parents:
diff changeset
253 if (start && spos != str0)
kono
parents:
diff changeset
254 return false;
kono
parents:
diff changeset
255 start = false;
kono
parents:
diff changeset
256 asterisk = false;
kono
parents:
diff changeset
257 }
kono
parents:
diff changeset
258 return true;
kono
parents:
diff changeset
259 }
kono
parents:
diff changeset
260
kono
parents:
diff changeset
261 static char binary_name_cache_str[kMaxPathLength];
kono
parents:
diff changeset
262 static char process_name_cache_str[kMaxPathLength];
kono
parents:
diff changeset
263
kono
parents:
diff changeset
264 const char *GetProcessName() {
kono
parents:
diff changeset
265 return process_name_cache_str;
kono
parents:
diff changeset
266 }
kono
parents:
diff changeset
267
kono
parents:
diff changeset
268 static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
kono
parents:
diff changeset
269 ReadLongProcessName(buf, buf_len);
kono
parents:
diff changeset
270 char *s = const_cast<char *>(StripModuleName(buf));
kono
parents:
diff changeset
271 uptr len = internal_strlen(s);
kono
parents:
diff changeset
272 if (s != buf) {
kono
parents:
diff changeset
273 internal_memmove(buf, s, len);
kono
parents:
diff changeset
274 buf[len] = '\0';
kono
parents:
diff changeset
275 }
kono
parents:
diff changeset
276 return len;
kono
parents:
diff changeset
277 }
kono
parents:
diff changeset
278
kono
parents:
diff changeset
279 void UpdateProcessName() {
kono
parents:
diff changeset
280 ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
kono
parents:
diff changeset
281 }
kono
parents:
diff changeset
282
kono
parents:
diff changeset
283 // Call once to make sure that binary_name_cache_str is initialized
kono
parents:
diff changeset
284 void CacheBinaryName() {
kono
parents:
diff changeset
285 if (binary_name_cache_str[0] != '\0')
kono
parents:
diff changeset
286 return;
kono
parents:
diff changeset
287 ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
kono
parents:
diff changeset
288 ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
kono
parents:
diff changeset
289 }
kono
parents:
diff changeset
290
kono
parents:
diff changeset
291 uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
kono
parents:
diff changeset
292 CacheBinaryName();
kono
parents:
diff changeset
293 uptr name_len = internal_strlen(binary_name_cache_str);
kono
parents:
diff changeset
294 name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
kono
parents:
diff changeset
295 if (buf_len == 0)
kono
parents:
diff changeset
296 return 0;
kono
parents:
diff changeset
297 internal_memcpy(buf, binary_name_cache_str, name_len);
kono
parents:
diff changeset
298 buf[name_len] = '\0';
kono
parents:
diff changeset
299 return name_len;
kono
parents:
diff changeset
300 }
kono
parents:
diff changeset
301
kono
parents:
diff changeset
302 void PrintCmdline() {
kono
parents:
diff changeset
303 char **argv = GetArgv();
kono
parents:
diff changeset
304 if (!argv) return;
kono
parents:
diff changeset
305 Printf("\nCommand: ");
kono
parents:
diff changeset
306 for (uptr i = 0; argv[i]; ++i)
kono
parents:
diff changeset
307 Printf("%s ", argv[i]);
kono
parents:
diff changeset
308 Printf("\n\n");
kono
parents:
diff changeset
309 }
kono
parents:
diff changeset
310
kono
parents:
diff changeset
311 // Malloc hooks.
kono
parents:
diff changeset
312 static const int kMaxMallocFreeHooks = 5;
kono
parents:
diff changeset
313 struct MallocFreeHook {
kono
parents:
diff changeset
314 void (*malloc_hook)(const void *, uptr);
kono
parents:
diff changeset
315 void (*free_hook)(const void *);
kono
parents:
diff changeset
316 };
kono
parents:
diff changeset
317
kono
parents:
diff changeset
318 static MallocFreeHook MFHooks[kMaxMallocFreeHooks];
kono
parents:
diff changeset
319
kono
parents:
diff changeset
320 void RunMallocHooks(const void *ptr, uptr size) {
kono
parents:
diff changeset
321 for (int i = 0; i < kMaxMallocFreeHooks; i++) {
kono
parents:
diff changeset
322 auto hook = MFHooks[i].malloc_hook;
kono
parents:
diff changeset
323 if (!hook) return;
kono
parents:
diff changeset
324 hook(ptr, size);
kono
parents:
diff changeset
325 }
kono
parents:
diff changeset
326 }
kono
parents:
diff changeset
327
kono
parents:
diff changeset
328 void RunFreeHooks(const void *ptr) {
kono
parents:
diff changeset
329 for (int i = 0; i < kMaxMallocFreeHooks; i++) {
kono
parents:
diff changeset
330 auto hook = MFHooks[i].free_hook;
kono
parents:
diff changeset
331 if (!hook) return;
kono
parents:
diff changeset
332 hook(ptr);
kono
parents:
diff changeset
333 }
kono
parents:
diff changeset
334 }
kono
parents:
diff changeset
335
kono
parents:
diff changeset
336 static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
kono
parents:
diff changeset
337 void (*free_hook)(const void *)) {
kono
parents:
diff changeset
338 if (!malloc_hook || !free_hook) return 0;
kono
parents:
diff changeset
339 for (int i = 0; i < kMaxMallocFreeHooks; i++) {
kono
parents:
diff changeset
340 if (MFHooks[i].malloc_hook == nullptr) {
kono
parents:
diff changeset
341 MFHooks[i].malloc_hook = malloc_hook;
kono
parents:
diff changeset
342 MFHooks[i].free_hook = free_hook;
kono
parents:
diff changeset
343 return i + 1;
kono
parents:
diff changeset
344 }
kono
parents:
diff changeset
345 }
kono
parents:
diff changeset
346 return 0;
kono
parents:
diff changeset
347 }
kono
parents:
diff changeset
348
kono
parents:
diff changeset
349 } // namespace __sanitizer
kono
parents:
diff changeset
350
kono
parents:
diff changeset
351 using namespace __sanitizer; // NOLINT
kono
parents:
diff changeset
352
kono
parents:
diff changeset
353 extern "C" {
kono
parents:
diff changeset
354 SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary,
kono
parents:
diff changeset
355 const char *error_summary) {
kono
parents:
diff changeset
356 Printf("%s\n", error_summary);
kono
parents:
diff changeset
357 }
kono
parents:
diff changeset
358
kono
parents:
diff changeset
359 SANITIZER_INTERFACE_ATTRIBUTE
kono
parents:
diff changeset
360 void __sanitizer_set_death_callback(void (*callback)(void)) {
kono
parents:
diff changeset
361 SetUserDieCallback(callback);
kono
parents:
diff changeset
362 }
kono
parents:
diff changeset
363
kono
parents:
diff changeset
364 SANITIZER_INTERFACE_ATTRIBUTE
kono
parents:
diff changeset
365 int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
kono
parents:
diff changeset
366 uptr),
kono
parents:
diff changeset
367 void (*free_hook)(const void *)) {
kono
parents:
diff changeset
368 return InstallMallocFreeHooks(malloc_hook, free_hook);
kono
parents:
diff changeset
369 }
kono
parents:
diff changeset
370 } // extern "C"