xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/lsan/lsan_allocator.cpp (revision 480093f4440d54b30b3025afeac24b48f2ba7a2e)
168d75effSDimitry Andric //=-- lsan_allocator.cpp --------------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of LeakSanitizer.
1068d75effSDimitry Andric // See lsan_allocator.h for details.
1168d75effSDimitry Andric //
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric 
1468d75effSDimitry Andric #include "lsan_allocator.h"
1568d75effSDimitry Andric 
1668d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator.h"
1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_checks.h"
1868d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h"
1968d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_report.h"
2068d75effSDimitry Andric #include "sanitizer_common/sanitizer_errno.h"
2168d75effSDimitry Andric #include "sanitizer_common/sanitizer_internal_defs.h"
2268d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
2368d75effSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h"
2468d75effSDimitry Andric #include "lsan_common.h"
2568d75effSDimitry Andric 
2668d75effSDimitry Andric extern "C" void *memset(void *ptr, int value, uptr num);
2768d75effSDimitry Andric 
2868d75effSDimitry Andric namespace __lsan {
2968d75effSDimitry Andric #if defined(__i386__) || defined(__arm__)
3068d75effSDimitry Andric static const uptr kMaxAllowedMallocSize = 1UL << 30;
3168d75effSDimitry Andric #elif defined(__mips64) || defined(__aarch64__)
3268d75effSDimitry Andric static const uptr kMaxAllowedMallocSize = 4UL << 30;
3368d75effSDimitry Andric #else
3468d75effSDimitry Andric static const uptr kMaxAllowedMallocSize = 8UL << 30;
3568d75effSDimitry Andric #endif
3668d75effSDimitry Andric 
3768d75effSDimitry Andric static Allocator allocator;
3868d75effSDimitry Andric 
39*480093f4SDimitry Andric static uptr max_malloc_size;
40*480093f4SDimitry Andric 
4168d75effSDimitry Andric void InitializeAllocator() {
4268d75effSDimitry Andric   SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
4368d75effSDimitry Andric   allocator.InitLinkerInitialized(
4468d75effSDimitry Andric       common_flags()->allocator_release_to_os_interval_ms);
45*480093f4SDimitry Andric   if (common_flags()->max_allocation_size_mb)
46*480093f4SDimitry Andric     max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20,
47*480093f4SDimitry Andric                           kMaxAllowedMallocSize);
48*480093f4SDimitry Andric   else
49*480093f4SDimitry Andric     max_malloc_size = kMaxAllowedMallocSize;
5068d75effSDimitry Andric }
5168d75effSDimitry Andric 
5268d75effSDimitry Andric void AllocatorThreadFinish() {
5368d75effSDimitry Andric   allocator.SwallowCache(GetAllocatorCache());
5468d75effSDimitry Andric }
5568d75effSDimitry Andric 
5668d75effSDimitry Andric static ChunkMetadata *Metadata(const void *p) {
5768d75effSDimitry Andric   return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
5868d75effSDimitry Andric }
5968d75effSDimitry Andric 
6068d75effSDimitry Andric static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) {
6168d75effSDimitry Andric   if (!p) return;
6268d75effSDimitry Andric   ChunkMetadata *m = Metadata(p);
6368d75effSDimitry Andric   CHECK(m);
6468d75effSDimitry Andric   m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked;
6568d75effSDimitry Andric   m->stack_trace_id = StackDepotPut(stack);
6668d75effSDimitry Andric   m->requested_size = size;
6768d75effSDimitry Andric   atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed);
6868d75effSDimitry Andric }
6968d75effSDimitry Andric 
7068d75effSDimitry Andric static void RegisterDeallocation(void *p) {
7168d75effSDimitry Andric   if (!p) return;
7268d75effSDimitry Andric   ChunkMetadata *m = Metadata(p);
7368d75effSDimitry Andric   CHECK(m);
7468d75effSDimitry Andric   atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 0, memory_order_relaxed);
7568d75effSDimitry Andric }
7668d75effSDimitry Andric 
7768d75effSDimitry Andric static void *ReportAllocationSizeTooBig(uptr size, const StackTrace &stack) {
7868d75effSDimitry Andric   if (AllocatorMayReturnNull()) {
7968d75effSDimitry Andric     Report("WARNING: LeakSanitizer failed to allocate 0x%zx bytes\n", size);
8068d75effSDimitry Andric     return nullptr;
8168d75effSDimitry Andric   }
82*480093f4SDimitry Andric   ReportAllocationSizeTooBig(size, max_malloc_size, &stack);
8368d75effSDimitry Andric }
8468d75effSDimitry Andric 
8568d75effSDimitry Andric void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
8668d75effSDimitry Andric                bool cleared) {
8768d75effSDimitry Andric   if (size == 0)
8868d75effSDimitry Andric     size = 1;
89*480093f4SDimitry Andric   if (size > max_malloc_size)
9068d75effSDimitry Andric     return ReportAllocationSizeTooBig(size, stack);
9168d75effSDimitry Andric   void *p = allocator.Allocate(GetAllocatorCache(), size, alignment);
9268d75effSDimitry Andric   if (UNLIKELY(!p)) {
9368d75effSDimitry Andric     SetAllocatorOutOfMemory();
9468d75effSDimitry Andric     if (AllocatorMayReturnNull())
9568d75effSDimitry Andric       return nullptr;
9668d75effSDimitry Andric     ReportOutOfMemory(size, &stack);
9768d75effSDimitry Andric   }
9868d75effSDimitry Andric   // Do not rely on the allocator to clear the memory (it's slow).
9968d75effSDimitry Andric   if (cleared && allocator.FromPrimary(p))
10068d75effSDimitry Andric     memset(p, 0, size);
10168d75effSDimitry Andric   RegisterAllocation(stack, p, size);
10268d75effSDimitry Andric   if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
10368d75effSDimitry Andric   RunMallocHooks(p, size);
10468d75effSDimitry Andric   return p;
10568d75effSDimitry Andric }
10668d75effSDimitry Andric 
10768d75effSDimitry Andric static void *Calloc(uptr nmemb, uptr size, const StackTrace &stack) {
10868d75effSDimitry Andric   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
10968d75effSDimitry Andric     if (AllocatorMayReturnNull())
11068d75effSDimitry Andric       return nullptr;
11168d75effSDimitry Andric     ReportCallocOverflow(nmemb, size, &stack);
11268d75effSDimitry Andric   }
11368d75effSDimitry Andric   size *= nmemb;
11468d75effSDimitry Andric   return Allocate(stack, size, 1, true);
11568d75effSDimitry Andric }
11668d75effSDimitry Andric 
11768d75effSDimitry Andric void Deallocate(void *p) {
11868d75effSDimitry Andric   if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
11968d75effSDimitry Andric   RunFreeHooks(p);
12068d75effSDimitry Andric   RegisterDeallocation(p);
12168d75effSDimitry Andric   allocator.Deallocate(GetAllocatorCache(), p);
12268d75effSDimitry Andric }
12368d75effSDimitry Andric 
12468d75effSDimitry Andric void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
12568d75effSDimitry Andric                  uptr alignment) {
12668d75effSDimitry Andric   RegisterDeallocation(p);
127*480093f4SDimitry Andric   if (new_size > max_malloc_size) {
12868d75effSDimitry Andric     allocator.Deallocate(GetAllocatorCache(), p);
12968d75effSDimitry Andric     return ReportAllocationSizeTooBig(new_size, stack);
13068d75effSDimitry Andric   }
13168d75effSDimitry Andric   p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment);
13268d75effSDimitry Andric   RegisterAllocation(stack, p, new_size);
13368d75effSDimitry Andric   return p;
13468d75effSDimitry Andric }
13568d75effSDimitry Andric 
13668d75effSDimitry Andric void GetAllocatorCacheRange(uptr *begin, uptr *end) {
13768d75effSDimitry Andric   *begin = (uptr)GetAllocatorCache();
13868d75effSDimitry Andric   *end = *begin + sizeof(AllocatorCache);
13968d75effSDimitry Andric }
14068d75effSDimitry Andric 
14168d75effSDimitry Andric uptr GetMallocUsableSize(const void *p) {
14268d75effSDimitry Andric   ChunkMetadata *m = Metadata(p);
14368d75effSDimitry Andric   if (!m) return 0;
14468d75effSDimitry Andric   return m->requested_size;
14568d75effSDimitry Andric }
14668d75effSDimitry Andric 
14768d75effSDimitry Andric int lsan_posix_memalign(void **memptr, uptr alignment, uptr size,
14868d75effSDimitry Andric                         const StackTrace &stack) {
14968d75effSDimitry Andric   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
15068d75effSDimitry Andric     if (AllocatorMayReturnNull())
15168d75effSDimitry Andric       return errno_EINVAL;
15268d75effSDimitry Andric     ReportInvalidPosixMemalignAlignment(alignment, &stack);
15368d75effSDimitry Andric   }
15468d75effSDimitry Andric   void *ptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
15568d75effSDimitry Andric   if (UNLIKELY(!ptr))
15668d75effSDimitry Andric     // OOM error is already taken care of by Allocate.
15768d75effSDimitry Andric     return errno_ENOMEM;
15868d75effSDimitry Andric   CHECK(IsAligned((uptr)ptr, alignment));
15968d75effSDimitry Andric   *memptr = ptr;
16068d75effSDimitry Andric   return 0;
16168d75effSDimitry Andric }
16268d75effSDimitry Andric 
16368d75effSDimitry Andric void *lsan_aligned_alloc(uptr alignment, uptr size, const StackTrace &stack) {
16468d75effSDimitry Andric   if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
16568d75effSDimitry Andric     errno = errno_EINVAL;
16668d75effSDimitry Andric     if (AllocatorMayReturnNull())
16768d75effSDimitry Andric       return nullptr;
16868d75effSDimitry Andric     ReportInvalidAlignedAllocAlignment(size, alignment, &stack);
16968d75effSDimitry Andric   }
17068d75effSDimitry Andric   return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory));
17168d75effSDimitry Andric }
17268d75effSDimitry Andric 
17368d75effSDimitry Andric void *lsan_memalign(uptr alignment, uptr size, const StackTrace &stack) {
17468d75effSDimitry Andric   if (UNLIKELY(!IsPowerOfTwo(alignment))) {
17568d75effSDimitry Andric     errno = errno_EINVAL;
17668d75effSDimitry Andric     if (AllocatorMayReturnNull())
17768d75effSDimitry Andric       return nullptr;
17868d75effSDimitry Andric     ReportInvalidAllocationAlignment(alignment, &stack);
17968d75effSDimitry Andric   }
18068d75effSDimitry Andric   return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory));
18168d75effSDimitry Andric }
18268d75effSDimitry Andric 
18368d75effSDimitry Andric void *lsan_malloc(uptr size, const StackTrace &stack) {
18468d75effSDimitry Andric   return SetErrnoOnNull(Allocate(stack, size, 1, kAlwaysClearMemory));
18568d75effSDimitry Andric }
18668d75effSDimitry Andric 
18768d75effSDimitry Andric void lsan_free(void *p) {
18868d75effSDimitry Andric   Deallocate(p);
18968d75effSDimitry Andric }
19068d75effSDimitry Andric 
19168d75effSDimitry Andric void *lsan_realloc(void *p, uptr size, const StackTrace &stack) {
19268d75effSDimitry Andric   return SetErrnoOnNull(Reallocate(stack, p, size, 1));
19368d75effSDimitry Andric }
19468d75effSDimitry Andric 
19568d75effSDimitry Andric void *lsan_reallocarray(void *ptr, uptr nmemb, uptr size,
19668d75effSDimitry Andric                         const StackTrace &stack) {
19768d75effSDimitry Andric   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
19868d75effSDimitry Andric     errno = errno_ENOMEM;
19968d75effSDimitry Andric     if (AllocatorMayReturnNull())
20068d75effSDimitry Andric       return nullptr;
20168d75effSDimitry Andric     ReportReallocArrayOverflow(nmemb, size, &stack);
20268d75effSDimitry Andric   }
20368d75effSDimitry Andric   return lsan_realloc(ptr, nmemb * size, stack);
20468d75effSDimitry Andric }
20568d75effSDimitry Andric 
20668d75effSDimitry Andric void *lsan_calloc(uptr nmemb, uptr size, const StackTrace &stack) {
20768d75effSDimitry Andric   return SetErrnoOnNull(Calloc(nmemb, size, stack));
20868d75effSDimitry Andric }
20968d75effSDimitry Andric 
21068d75effSDimitry Andric void *lsan_valloc(uptr size, const StackTrace &stack) {
21168d75effSDimitry Andric   return SetErrnoOnNull(
21268d75effSDimitry Andric       Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory));
21368d75effSDimitry Andric }
21468d75effSDimitry Andric 
21568d75effSDimitry Andric void *lsan_pvalloc(uptr size, const StackTrace &stack) {
21668d75effSDimitry Andric   uptr PageSize = GetPageSizeCached();
21768d75effSDimitry Andric   if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
21868d75effSDimitry Andric     errno = errno_ENOMEM;
21968d75effSDimitry Andric     if (AllocatorMayReturnNull())
22068d75effSDimitry Andric       return nullptr;
22168d75effSDimitry Andric     ReportPvallocOverflow(size, &stack);
22268d75effSDimitry Andric   }
22368d75effSDimitry Andric   // pvalloc(0) should allocate one page.
22468d75effSDimitry Andric   size = size ? RoundUpTo(size, PageSize) : PageSize;
22568d75effSDimitry Andric   return SetErrnoOnNull(Allocate(stack, size, PageSize, kAlwaysClearMemory));
22668d75effSDimitry Andric }
22768d75effSDimitry Andric 
22868d75effSDimitry Andric uptr lsan_mz_size(const void *p) {
22968d75effSDimitry Andric   return GetMallocUsableSize(p);
23068d75effSDimitry Andric }
23168d75effSDimitry Andric 
23268d75effSDimitry Andric ///// Interface to the common LSan module. /////
23368d75effSDimitry Andric 
23468d75effSDimitry Andric void LockAllocator() {
23568d75effSDimitry Andric   allocator.ForceLock();
23668d75effSDimitry Andric }
23768d75effSDimitry Andric 
23868d75effSDimitry Andric void UnlockAllocator() {
23968d75effSDimitry Andric   allocator.ForceUnlock();
24068d75effSDimitry Andric }
24168d75effSDimitry Andric 
24268d75effSDimitry Andric void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
24368d75effSDimitry Andric   *begin = (uptr)&allocator;
24468d75effSDimitry Andric   *end = *begin + sizeof(allocator);
24568d75effSDimitry Andric }
24668d75effSDimitry Andric 
24768d75effSDimitry Andric uptr PointsIntoChunk(void* p) {
24868d75effSDimitry Andric   uptr addr = reinterpret_cast<uptr>(p);
24968d75effSDimitry Andric   uptr chunk = reinterpret_cast<uptr>(allocator.GetBlockBeginFastLocked(p));
25068d75effSDimitry Andric   if (!chunk) return 0;
25168d75effSDimitry Andric   // LargeMmapAllocator considers pointers to the meta-region of a chunk to be
25268d75effSDimitry Andric   // valid, but we don't want that.
25368d75effSDimitry Andric   if (addr < chunk) return 0;
25468d75effSDimitry Andric   ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
25568d75effSDimitry Andric   CHECK(m);
25668d75effSDimitry Andric   if (!m->allocated)
25768d75effSDimitry Andric     return 0;
25868d75effSDimitry Andric   if (addr < chunk + m->requested_size)
25968d75effSDimitry Andric     return chunk;
26068d75effSDimitry Andric   if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr))
26168d75effSDimitry Andric     return chunk;
26268d75effSDimitry Andric   return 0;
26368d75effSDimitry Andric }
26468d75effSDimitry Andric 
26568d75effSDimitry Andric uptr GetUserBegin(uptr chunk) {
26668d75effSDimitry Andric   return chunk;
26768d75effSDimitry Andric }
26868d75effSDimitry Andric 
26968d75effSDimitry Andric LsanMetadata::LsanMetadata(uptr chunk) {
27068d75effSDimitry Andric   metadata_ = Metadata(reinterpret_cast<void *>(chunk));
27168d75effSDimitry Andric   CHECK(metadata_);
27268d75effSDimitry Andric }
27368d75effSDimitry Andric 
27468d75effSDimitry Andric bool LsanMetadata::allocated() const {
27568d75effSDimitry Andric   return reinterpret_cast<ChunkMetadata *>(metadata_)->allocated;
27668d75effSDimitry Andric }
27768d75effSDimitry Andric 
27868d75effSDimitry Andric ChunkTag LsanMetadata::tag() const {
27968d75effSDimitry Andric   return reinterpret_cast<ChunkMetadata *>(metadata_)->tag;
28068d75effSDimitry Andric }
28168d75effSDimitry Andric 
28268d75effSDimitry Andric void LsanMetadata::set_tag(ChunkTag value) {
28368d75effSDimitry Andric   reinterpret_cast<ChunkMetadata *>(metadata_)->tag = value;
28468d75effSDimitry Andric }
28568d75effSDimitry Andric 
28668d75effSDimitry Andric uptr LsanMetadata::requested_size() const {
28768d75effSDimitry Andric   return reinterpret_cast<ChunkMetadata *>(metadata_)->requested_size;
28868d75effSDimitry Andric }
28968d75effSDimitry Andric 
29068d75effSDimitry Andric u32 LsanMetadata::stack_trace_id() const {
29168d75effSDimitry Andric   return reinterpret_cast<ChunkMetadata *>(metadata_)->stack_trace_id;
29268d75effSDimitry Andric }
29368d75effSDimitry Andric 
29468d75effSDimitry Andric void ForEachChunk(ForEachChunkCallback callback, void *arg) {
29568d75effSDimitry Andric   allocator.ForEachChunk(callback, arg);
29668d75effSDimitry Andric }
29768d75effSDimitry Andric 
29868d75effSDimitry Andric IgnoreObjectResult IgnoreObjectLocked(const void *p) {
29968d75effSDimitry Andric   void *chunk = allocator.GetBlockBegin(p);
30068d75effSDimitry Andric   if (!chunk || p < chunk) return kIgnoreObjectInvalid;
30168d75effSDimitry Andric   ChunkMetadata *m = Metadata(chunk);
30268d75effSDimitry Andric   CHECK(m);
30368d75effSDimitry Andric   if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) {
30468d75effSDimitry Andric     if (m->tag == kIgnored)
30568d75effSDimitry Andric       return kIgnoreObjectAlreadyIgnored;
30668d75effSDimitry Andric     m->tag = kIgnored;
30768d75effSDimitry Andric     return kIgnoreObjectSuccess;
30868d75effSDimitry Andric   } else {
30968d75effSDimitry Andric     return kIgnoreObjectInvalid;
31068d75effSDimitry Andric   }
31168d75effSDimitry Andric }
31268d75effSDimitry Andric } // namespace __lsan
31368d75effSDimitry Andric 
31468d75effSDimitry Andric using namespace __lsan;
31568d75effSDimitry Andric 
31668d75effSDimitry Andric extern "C" {
31768d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
31868d75effSDimitry Andric uptr __sanitizer_get_current_allocated_bytes() {
31968d75effSDimitry Andric   uptr stats[AllocatorStatCount];
32068d75effSDimitry Andric   allocator.GetStats(stats);
32168d75effSDimitry Andric   return stats[AllocatorStatAllocated];
32268d75effSDimitry Andric }
32368d75effSDimitry Andric 
32468d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
32568d75effSDimitry Andric uptr __sanitizer_get_heap_size() {
32668d75effSDimitry Andric   uptr stats[AllocatorStatCount];
32768d75effSDimitry Andric   allocator.GetStats(stats);
32868d75effSDimitry Andric   return stats[AllocatorStatMapped];
32968d75effSDimitry Andric }
33068d75effSDimitry Andric 
33168d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
33268d75effSDimitry Andric uptr __sanitizer_get_free_bytes() { return 0; }
33368d75effSDimitry Andric 
33468d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
33568d75effSDimitry Andric uptr __sanitizer_get_unmapped_bytes() { return 0; }
33668d75effSDimitry Andric 
33768d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
33868d75effSDimitry Andric uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
33968d75effSDimitry Andric 
34068d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
34168d75effSDimitry Andric int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; }
34268d75effSDimitry Andric 
34368d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
34468d75effSDimitry Andric uptr __sanitizer_get_allocated_size(const void *p) {
34568d75effSDimitry Andric   return GetMallocUsableSize(p);
34668d75effSDimitry Andric }
34768d75effSDimitry Andric 
34868d75effSDimitry Andric #if !SANITIZER_SUPPORTS_WEAK_HOOKS
34968d75effSDimitry Andric // Provide default (no-op) implementation of malloc hooks.
35068d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
35168d75effSDimitry Andric void __sanitizer_malloc_hook(void *ptr, uptr size) {
35268d75effSDimitry Andric   (void)ptr;
35368d75effSDimitry Andric   (void)size;
35468d75effSDimitry Andric }
35568d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
35668d75effSDimitry Andric void __sanitizer_free_hook(void *ptr) {
35768d75effSDimitry Andric   (void)ptr;
35868d75effSDimitry Andric }
35968d75effSDimitry Andric #endif
36068d75effSDimitry Andric } // extern "C"
361