diff options
Diffstat (limited to 'memory/mozalloc')
-rw-r--r-- | memory/mozalloc/moz.build | 57 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc.cpp | 221 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc.h | 361 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc_abort.cpp | 89 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc_abort.h | 28 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc_oom.cpp | 53 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc_oom.h | 31 | ||||
-rw-r--r-- | memory/mozalloc/msvc_raise_wrappers.cpp | 63 | ||||
-rw-r--r-- | memory/mozalloc/msvc_raise_wrappers.h | 41 | ||||
-rw-r--r-- | memory/mozalloc/staticruntime/moz.build | 35 | ||||
-rw-r--r-- | memory/mozalloc/throw_gcc.h | 145 | ||||
-rw-r--r-- | memory/mozalloc/throw_msvc.h | 17 | ||||
-rw-r--r-- | memory/mozalloc/winheap.cpp | 74 |
13 files changed, 1215 insertions, 0 deletions
diff --git a/memory/mozalloc/moz.build b/memory/mozalloc/moz.build new file mode 100644 index 000000000..56d4b8d80 --- /dev/null +++ b/memory/mozalloc/moz.build @@ -0,0 +1,57 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. +NO_VISIBILITY_FLAGS = True + +EXPORTS.mozilla += [ + 'mozalloc.h', + 'mozalloc_abort.h', + 'mozalloc_oom.h', +] + +if CONFIG['WRAP_STL_INCLUDES']: + if CONFIG['GNU_CXX']: + EXPORTS.mozilla += ['throw_gcc.h'] + elif CONFIG['_MSC_VER']: + DEFINES['_HAS_EXCEPTIONS'] = 0 + if CONFIG['MOZ_MSVC_STL_WRAP_RAISE']: + EXPORTS.mozilla += [ + 'msvc_raise_wrappers.h', + 'throw_msvc.h', + ] + SOURCES += [ + 'msvc_raise_wrappers.cpp', + ] + +if CONFIG['OS_TARGET'] == 'WINNT': + # Keep this file separate to avoid #include'ing windows.h everywhere. + SOURCES += [ + 'winheap.cpp', + ] + +UNIFIED_SOURCES += [ + 'mozalloc.cpp', + 'mozalloc_abort.cpp', + 'mozalloc_oom.cpp', +] + +FINAL_LIBRARY = 'mozglue' + +# The strndup declaration in string.h is in an ifdef __USE_GNU section +DEFINES['_GNU_SOURCE'] = True + +DISABLE_STL_WRAPPING = True + +DEFINES['IMPL_MFBT'] = True + +if CONFIG['_MSC_VER']: + DIRS += ['staticruntime'] + +LOCAL_INCLUDES += [ + '!/xpcom', + '/memory/build', +] + +DIST_INSTALL = True diff --git a/memory/mozalloc/mozalloc.cpp b/memory/mozalloc/mozalloc.cpp new file mode 100644 index 000000000..55d36d80b --- /dev/null +++ b/memory/mozalloc/mozalloc.cpp @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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/. */ + +#include <stddef.h> // for size_t + +// Building with USE_STATIC_LIBS = True sets -MT instead of -MD. -MT sets _MT, +// while -MD sets _MT and _DLL. +#if defined(_MT) && !defined(_DLL) +#define MOZ_STATIC_RUNTIME +#endif + +#if defined(MOZ_MEMORY) && !defined(MOZ_STATIC_RUNTIME) +// mozalloc.cpp is part of the same library as mozmemory, thus MOZ_MEMORY_IMPL +// is needed. +#define MOZ_MEMORY_IMPL +#include "mozmemory_wrap.h" + +#if defined(XP_DARWIN) +#include <malloc/malloc.h> // for malloc_size +#endif + +// See mozmemory_wrap.h for more details. This file is part of libmozglue, so +// it needs to use _impl suffixes. However, with libmozglue growing, this is +// becoming cumbersome, so we will likely use a malloc.h wrapper of some sort +// and allow the use of the functions without a _impl suffix. +#define MALLOC_DECL(name, return_type, ...) \ + extern "C" MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__); +#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC +#include "malloc_decls.h" + +extern "C" MOZ_MEMORY_API char *strdup_impl(const char *); +extern "C" MOZ_MEMORY_API char *strndup_impl(const char *, size_t); + +#else +// When jemalloc is disabled, or when building the static runtime variant, +// we need not to use the suffixes. + +#if defined(MALLOC_H) +# include MALLOC_H // for memalign, valloc, malloc_size, malloc_us +#endif // if defined(MALLOC_H) +#include <stdlib.h> // for malloc, free +#if defined(XP_UNIX) +# include <unistd.h> // for valloc on *BSD +#endif //if defined(XP_UNIX) + +#define malloc_impl malloc +#define posix_memalign_impl posix_memalign +#define calloc_impl calloc +#define realloc_impl realloc +#define free_impl free +#define memalign_impl memalign +#define valloc_impl valloc +#define malloc_usable_size_impl malloc_usable_size +#define strdup_impl strdup +#define strndup_impl strndup + +#endif + +#include <errno.h> +#include <new> // for std::bad_alloc +#include <string.h> + +#include <sys/types.h> + +#include "mozilla/mozalloc.h" +#include "mozilla/mozalloc_oom.h" // for mozalloc_handle_oom + +#ifdef __GNUC__ +#define LIKELY(x) (__builtin_expect(!!(x), 1)) +#define UNLIKELY(x) (__builtin_expect(!!(x), 0)) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif + +void* +moz_xmalloc(size_t size) +{ + void* ptr = malloc_impl(size); + if (UNLIKELY(!ptr && size)) { + mozalloc_handle_oom(size); + return moz_xmalloc(size); + } + return ptr; +} + +void* +moz_xcalloc(size_t nmemb, size_t size) +{ + void* ptr = calloc_impl(nmemb, size); + if (UNLIKELY(!ptr && nmemb && size)) { + mozalloc_handle_oom(size); + return moz_xcalloc(nmemb, size); + } + return ptr; +} + +void* +moz_xrealloc(void* ptr, size_t size) +{ + void* newptr = realloc_impl(ptr, size); + if (UNLIKELY(!newptr && size)) { + mozalloc_handle_oom(size); + return moz_xrealloc(ptr, size); + } + return newptr; +} + +char* +moz_xstrdup(const char* str) +{ + char* dup = strdup_impl(str); + if (UNLIKELY(!dup)) { + mozalloc_handle_oom(0); + return moz_xstrdup(str); + } + return dup; +} + +#if defined(HAVE_STRNDUP) +char* +moz_xstrndup(const char* str, size_t strsize) +{ + char* dup = strndup_impl(str, strsize); + if (UNLIKELY(!dup)) { + mozalloc_handle_oom(strsize); + return moz_xstrndup(str, strsize); + } + return dup; +} +#endif // if defined(HAVE_STRNDUP) + +#if defined(HAVE_POSIX_MEMALIGN) +int +moz_xposix_memalign(void **ptr, size_t alignment, size_t size) +{ + int err = posix_memalign_impl(ptr, alignment, size); + if (UNLIKELY(err && ENOMEM == err)) { + mozalloc_handle_oom(size); + return moz_xposix_memalign(ptr, alignment, size); + } + // else: (0 == err) or (EINVAL == err) + return err; +} +int +moz_posix_memalign(void **ptr, size_t alignment, size_t size) +{ + int code = posix_memalign_impl(ptr, alignment, size); + if (code) + return code; + +#if defined(XP_DARWIN) + // Workaround faulty OSX posix_memalign, which provides memory with the + // incorrect alignment sometimes, but returns 0 as if nothing was wrong. + size_t mask = alignment - 1; + if (((size_t)(*ptr) & mask) != 0) { + void* old = *ptr; + code = moz_posix_memalign(ptr, alignment, size); + free(old); + } +#endif + + return code; + +} +#endif // if defined(HAVE_POSIX_MEMALIGN) + +#if defined(HAVE_MEMALIGN) +void* +moz_xmemalign(size_t boundary, size_t size) +{ + void* ptr = memalign_impl(boundary, size); + if (UNLIKELY(!ptr && EINVAL != errno)) { + mozalloc_handle_oom(size); + return moz_xmemalign(boundary, size); + } + // non-NULL ptr or errno == EINVAL + return ptr; +} +#endif // if defined(HAVE_MEMALIGN) + +#if defined(HAVE_VALLOC) +void* +moz_xvalloc(size_t size) +{ + void* ptr = valloc_impl(size); + if (UNLIKELY(!ptr)) { + mozalloc_handle_oom(size); + return moz_xvalloc(size); + } + return ptr; +} +#endif // if defined(HAVE_VALLOC) + +#ifndef MOZ_STATIC_RUNTIME +size_t +moz_malloc_usable_size(void *ptr) +{ + if (!ptr) + return 0; + +#if defined(XP_DARWIN) + return malloc_size(ptr); +#elif defined(HAVE_MALLOC_USABLE_SIZE) || defined(MOZ_MEMORY) + return malloc_usable_size_impl(ptr); +#elif defined(XP_WIN) + return _msize(ptr); +#else + return 0; +#endif +} + +size_t moz_malloc_size_of(const void *ptr) +{ + return moz_malloc_usable_size((void *)ptr); +} +#endif diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h new file mode 100644 index 000000000..f7ddb7e6d --- /dev/null +++ b/memory/mozalloc/mozalloc.h @@ -0,0 +1,361 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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 mozilla_mozalloc_h +#define mozilla_mozalloc_h + +/* + * https://bugzilla.mozilla.org/show_bug.cgi?id=427099 + */ + +#if defined(__cplusplus) +# include <new> +// Since libstdc++ 6, including the C headers (e.g. stdlib.h) instead of the +// corresponding C++ header (e.g. cstdlib) can cause confusion in C++ code +// using things defined there. Specifically, with stdlib.h, the use of abs() +// in gfx/graphite2/src/inc/UtfCodec.h somehow ends up picking the wrong abs() +# include <cstdlib> +# include <cstring> +#else +# include <stdlib.h> +# include <string.h> +#endif + +#if defined(__cplusplus) +#include "mozilla/fallible.h" +#include "mozilla/mozalloc_abort.h" +#include "mozilla/TemplateLib.h" +#endif +#include "mozilla/Attributes.h" +#include "mozilla/Types.h" + +#define MOZALLOC_HAVE_XMALLOC + +#if defined(MOZ_ALWAYS_INLINE_EVEN_DEBUG) +# define MOZALLOC_INLINE MOZ_ALWAYS_INLINE_EVEN_DEBUG +#elif defined(HAVE_FORCEINLINE) +# define MOZALLOC_INLINE __forceinline +#else +# define MOZALLOC_INLINE inline +#endif + +/* Workaround build problem with Sun Studio 12 */ +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# undef MOZ_MUST_USE +# define MOZ_MUST_USE +# undef MOZ_ALLOCATOR +# define MOZ_ALLOCATOR +#endif + +#if defined(__cplusplus) +extern "C" { +#endif /* ifdef __cplusplus */ + +/* + * We need to use malloc_impl and free_impl in this file when they are + * defined, because of how mozglue.dll is linked on Windows, where using + * malloc/free would end up using the symbols from the MSVCRT instead of + * ours. + */ +#ifndef free_impl +#define free_impl free +#define free_impl_ +#endif +#ifndef malloc_impl +#define malloc_impl malloc +#define malloc_impl_ +#endif + +/* + * Each declaration below is analogous to a "standard" allocation + * function, except that the out-of-memory handling is made explicit. + * The |moz_x| versions will never return a NULL pointer; if memory + * is exhausted, they abort. The |moz_| versions may return NULL + * pointers if memory is exhausted: their return value must be checked. + * + * All these allocation functions are *guaranteed* to return a pointer + * to memory allocated in such a way that that memory can be freed by + * passing that pointer to |free()|. + */ + +MFBT_API void* moz_xmalloc(size_t size) + MOZ_ALLOCATOR; + +MFBT_API void* moz_xcalloc(size_t nmemb, size_t size) + MOZ_ALLOCATOR; + +MFBT_API void* moz_xrealloc(void* ptr, size_t size) + MOZ_ALLOCATOR; + +MFBT_API char* moz_xstrdup(const char* str) + MOZ_ALLOCATOR; + +MFBT_API size_t moz_malloc_usable_size(void *ptr); + +MFBT_API size_t moz_malloc_size_of(const void *ptr); + +#if defined(HAVE_STRNDUP) +MFBT_API char* moz_xstrndup(const char* str, size_t strsize) + MOZ_ALLOCATOR; +#endif /* if defined(HAVE_STRNDUP) */ + + +#if defined(HAVE_POSIX_MEMALIGN) +MFBT_API MOZ_MUST_USE +int moz_xposix_memalign(void **ptr, size_t alignment, size_t size); + +MFBT_API MOZ_MUST_USE +int moz_posix_memalign(void **ptr, size_t alignment, size_t size); +#endif /* if defined(HAVE_POSIX_MEMALIGN) */ + + +#if defined(HAVE_MEMALIGN) +MFBT_API void* moz_xmemalign(size_t boundary, size_t size) + MOZ_ALLOCATOR; +#endif /* if defined(HAVE_MEMALIGN) */ + + +#if defined(HAVE_VALLOC) +MFBT_API void* moz_xvalloc(size_t size) + MOZ_ALLOCATOR; +#endif /* if defined(HAVE_VALLOC) */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* ifdef __cplusplus */ + + +#ifdef __cplusplus + +/* + * We implement the default operators new/delete as part of + * libmozalloc, replacing their definitions in libstdc++. The + * operator new* definitions in libmozalloc will never return a NULL + * pointer. + * + * Each operator new immediately below returns a pointer to memory + * that can be delete'd by any of + * + * (1) the matching infallible operator delete immediately below + * (2) the matching "fallible" operator delete further below + * (3) the matching system |operator delete(void*, std::nothrow)| + * (4) the matching system |operator delete(void*) throw(std::bad_alloc)| + * + * NB: these are declared |throw(std::bad_alloc)|, though they will never + * throw that exception. This declaration is consistent with the rule + * that |::operator new() throw(std::bad_alloc)| will never return NULL. + */ + +/* NB: This is defined just to silence vacuous warnings about symbol + * visibility on OS X/gcc. These symbols are force-inline and not + * exported. */ +#if defined(XP_MACOSX) +# define MOZALLOC_EXPORT_NEW MFBT_API +#else +# define MOZALLOC_EXPORT_NEW +#endif + +#if defined(ANDROID) +/* + * It's important to always specify 'throw()' in GCC because it's used to tell + * GCC that 'new' may return null. That makes GCC null-check the result before + * potentially initializing the memory to zero. + * Also, the Android minimalistic headers don't include std::bad_alloc. + */ +#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS throw() +#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS +#elif defined(_MSC_VER) +/* + * Suppress build warning spam (bug 578546). + */ +#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS +#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS +#else +#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS throw() +#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS throw(std::bad_alloc) +#endif + +#define MOZALLOC_THROW_BAD_ALLOC MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS + +MOZALLOC_EXPORT_NEW +#if defined(__GNUC__) && !defined(__clang__) && defined(__SANITIZE_ADDRESS__) +/* gcc's asan somehow doesn't like always_inline on this function. */ +__attribute__((gnu_inline)) inline +#else +MOZALLOC_INLINE +#endif +void* operator new(size_t size) MOZALLOC_THROW_BAD_ALLOC +{ + return moz_xmalloc(size); +} + +MOZALLOC_EXPORT_NEW MOZALLOC_INLINE +void* operator new(size_t size, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + return malloc_impl(size); +} + +MOZALLOC_EXPORT_NEW MOZALLOC_INLINE +void* operator new[](size_t size) MOZALLOC_THROW_BAD_ALLOC +{ + return moz_xmalloc(size); +} + +MOZALLOC_EXPORT_NEW MOZALLOC_INLINE +void* operator new[](size_t size, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + return malloc_impl(size); +} + +MOZALLOC_EXPORT_NEW MOZALLOC_INLINE +void operator delete(void* ptr) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + return free_impl(ptr); +} + +MOZALLOC_EXPORT_NEW MOZALLOC_INLINE +void operator delete(void* ptr, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + return free_impl(ptr); +} + +MOZALLOC_EXPORT_NEW MOZALLOC_INLINE +void operator delete[](void* ptr) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + return free_impl(ptr); +} + +MOZALLOC_EXPORT_NEW MOZALLOC_INLINE +void operator delete[](void* ptr, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + return free_impl(ptr); +} + + +/* + * We also add a new allocator variant: "fallible operator new." + * Unlike libmozalloc's implementations of the standard nofail + * allocators, this allocator is allowed to return NULL. It can be used + * as follows + * + * Foo* f = new (mozilla::fallible) Foo(...); + * + * operator delete(fallible) is defined for completeness only. + * + * Each operator new below returns a pointer to memory that can be + * delete'd by any of + * + * (1) the matching "fallible" operator delete below + * (2) the matching infallible operator delete above + * (3) the matching system |operator delete(void*, std::nothrow)| + * (4) the matching system |operator delete(void*) throw(std::bad_alloc)| + */ + +MOZALLOC_INLINE +void* operator new(size_t size, const mozilla::fallible_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + return malloc_impl(size); +} + +MOZALLOC_INLINE +void* operator new[](size_t size, const mozilla::fallible_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + return malloc_impl(size); +} + +MOZALLOC_INLINE +void operator delete(void* ptr, const mozilla::fallible_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + free_impl(ptr); +} + +MOZALLOC_INLINE +void operator delete[](void* ptr, const mozilla::fallible_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS +{ + free_impl(ptr); +} + + +/* + * This policy is identical to MallocAllocPolicy, except it uses + * moz_xmalloc/moz_xcalloc/moz_xrealloc instead of + * malloc/calloc/realloc. + */ +class InfallibleAllocPolicy +{ +public: + template <typename T> + T* maybe_pod_malloc(size_t aNumElems) + { + return pod_malloc<T>(aNumElems); + } + + template <typename T> + T* maybe_pod_calloc(size_t aNumElems) + { + return pod_calloc<T>(aNumElems); + } + + template <typename T> + T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) + { + return pod_realloc<T>(aPtr, aOldSize, aNewSize); + } + + template <typename T> + T* pod_malloc(size_t aNumElems) + { + if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) { + reportAllocOverflow(); + } + return static_cast<T*>(moz_xmalloc(aNumElems * sizeof(T))); + } + + template <typename T> + T* pod_calloc(size_t aNumElems) + { + return static_cast<T*>(moz_xcalloc(aNumElems, sizeof(T))); + } + + template <typename T> + T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) + { + if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) { + reportAllocOverflow(); + } + return static_cast<T*>(moz_xrealloc(aPtr, aNewSize * sizeof(T))); + } + + void free_(void* aPtr) + { + free_impl(aPtr); + } + + void reportAllocOverflow() const + { + mozalloc_abort("alloc overflow"); + } + + bool checkSimulatedOOM() const + { + return true; + } +}; + +#endif /* ifdef __cplusplus */ + +#ifdef malloc_impl_ +#undef malloc_impl_ +#undef malloc_impl +#endif +#ifdef free_impl_ +#undef free_impl_ +#undef free_impl +#endif + +#endif /* ifndef mozilla_mozalloc_h */ diff --git a/memory/mozalloc/mozalloc_abort.cpp b/memory/mozalloc/mozalloc_abort.cpp new file mode 100644 index 000000000..a998d8164 --- /dev/null +++ b/memory/mozalloc/mozalloc_abort.cpp @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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/. */ + +#include "mozilla/mozalloc_abort.h" + +#ifdef ANDROID +# include <android/log.h> +#endif +#ifdef MOZ_WIDGET_ANDROID +# include "APKOpen.h" +# include "dlfcn.h" +#endif +#include <stdio.h> + +#include "mozilla/Assertions.h" + +void +mozalloc_abort(const char* const msg) +{ +#ifndef ANDROID + fputs(msg, stderr); + fputs("\n", stderr); +#else + __android_log_print(ANDROID_LOG_ERROR, "Gecko", "mozalloc_abort: %s", msg); +#endif +#ifdef MOZ_WIDGET_ANDROID + abortThroughJava(msg); +#endif + MOZ_CRASH(); +} + +#ifdef MOZ_WIDGET_ANDROID +template <size_t N> +void fillAbortMessage(char (&msg)[N], uintptr_t retAddress) { + /* + * On Android, we often don't have reliable backtrace when crashing inside + * abort(). Therefore, we try to find out who is calling abort() and add + * that to the message. + */ + Dl_info info = {}; + dladdr(reinterpret_cast<void*>(retAddress), &info); + + const char* const module = info.dli_fname ? info.dli_fname : ""; + const char* const base_module = strrchr(module, '/'); + const void* const module_offset = + reinterpret_cast<void*>(retAddress - uintptr_t(info.dli_fbase)); + const char* const sym = info.dli_sname ? info.dli_sname : ""; + + snprintf(msg, sizeof(msg), "abort() called from %s:%p (%s)", + base_module ? base_module + 1 : module, module_offset, sym); +} +#endif + +#if defined(XP_UNIX) && !defined(MOZ_ASAN) +// Define abort() here, so that it is used instead of the system abort(). This +// lets us control the behavior when aborting, in order to get better results +// on *NIX platforms. See mozalloc_abort for details. +// +// For AddressSanitizer, we must not redefine system abort because the ASan +// option "abort_on_error=1" calls abort() and therefore causes the following +// call chain with our redefined abort: +// +// ASan -> abort() -> moz_abort() -> MOZ_CRASH() -> Segmentation fault +// +// That segmentation fault will be interpreted as another bug by ASan and as a +// result, ASan will just exit(1) instead of aborting. +void abort(void) +{ +#ifdef MOZ_WIDGET_ANDROID + char msg[64] = {}; + fillAbortMessage(msg, uintptr_t(__builtin_return_address(0))); +#else + const char* const msg = "Redirecting call to abort() to mozalloc_abort\n"; +#endif + + mozalloc_abort(msg); + + // We won't reach here because mozalloc_abort() is MOZ_NORETURN. But that + // annotation isn't used on ARM (see mozalloc_abort.h for why) so we add a + // redundant MOZ_CRASH() here to avoid a "'noreturn' function does return" + // warning. + MOZ_CRASH(); +} +#endif + diff --git a/memory/mozalloc/mozalloc_abort.h b/memory/mozalloc/mozalloc_abort.h new file mode 100644 index 000000000..065cebcb3 --- /dev/null +++ b/memory/mozalloc/mozalloc_abort.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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 mozilla_mozalloc_abort_h +#define mozilla_mozalloc_abort_h + +#include "mozilla/Attributes.h" +#include "mozilla/Types.h" + +/** + * Terminate this process in such a way that breakpad is triggered, if + * at all possible. + * + * Note: MOZ_NORETURN seems to break crash stacks on ARM, so we don't + * use that annotation there. + */ +MFBT_API +#if !defined(__arm__) + MOZ_NORETURN +#endif + void mozalloc_abort(const char* const msg); + + +#endif /* ifndef mozilla_mozalloc_abort_h */ diff --git a/memory/mozalloc/mozalloc_oom.cpp b/memory/mozalloc/mozalloc_oom.cpp new file mode 100644 index 000000000..820888cdb --- /dev/null +++ b/memory/mozalloc/mozalloc_oom.cpp @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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/. */ + +#include "mozilla/mozalloc_abort.h" +#include "mozilla/mozalloc_oom.h" +#include "mozilla/Assertions.h" + +static mozalloc_oom_abort_handler gAbortHandler; + +#define OOM_MSG_LEADER "out of memory: 0x" +#define OOM_MSG_DIGITS "0000000000000000" // large enough for 2^64 +#define OOM_MSG_TRAILER " bytes requested" +#define OOM_MSG_FIRST_DIGIT_OFFSET sizeof(OOM_MSG_LEADER) - 1 +#define OOM_MSG_LAST_DIGIT_OFFSET sizeof(OOM_MSG_LEADER) + \ + sizeof(OOM_MSG_DIGITS) - 3 + +static const char *hex = "0123456789ABCDEF"; + +void +mozalloc_handle_oom(size_t size) +{ + char oomMsg[] = OOM_MSG_LEADER OOM_MSG_DIGITS OOM_MSG_TRAILER; + size_t i; + + // NB: this is handle_oom() stage 1, which simply aborts on OOM. + // we might proceed to a stage 2 in which an attempt is made to + // reclaim memory + + if (gAbortHandler) + gAbortHandler(size); + + static_assert(OOM_MSG_FIRST_DIGIT_OFFSET > 0, + "Loop below will never terminate (i can't go below 0)"); + + // Insert size into the diagnostic message using only primitive operations + for (i = OOM_MSG_LAST_DIGIT_OFFSET; + size && i >= OOM_MSG_FIRST_DIGIT_OFFSET; i--) { + oomMsg[i] = hex[size % 16]; + size /= 16; + } + + mozalloc_abort(oomMsg); +} + +void +mozalloc_set_oom_abort_handler(mozalloc_oom_abort_handler handler) +{ + gAbortHandler = handler; +} diff --git a/memory/mozalloc/mozalloc_oom.h b/memory/mozalloc/mozalloc_oom.h new file mode 100644 index 000000000..35bb9acc8 --- /dev/null +++ b/memory/mozalloc/mozalloc_oom.h @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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 mozilla_mozalloc_oom_h +#define mozilla_mozalloc_oom_h + +#include "mozalloc.h" + +/** + * Called when memory is critically low. Returns iff it was able to + * remedy the critical memory situation; if not, it will abort(). + */ +MFBT_API void mozalloc_handle_oom(size_t requestedSize); + +/** + * Called by embedders (specifically Mozilla breakpad) which wants to be + * notified of an intentional abort, to annotate any crash report with + * the size of the allocation on which we aborted. + */ +typedef void (*mozalloc_oom_abort_handler)(size_t size); +MFBT_API void mozalloc_set_oom_abort_handler(mozalloc_oom_abort_handler handler); + +/* TODO: functions to query system memory usage and register + * critical-memory handlers. */ + + +#endif /* ifndef mozilla_mozalloc_oom_h */ diff --git a/memory/mozalloc/msvc_raise_wrappers.cpp b/memory/mozalloc/msvc_raise_wrappers.cpp new file mode 100644 index 000000000..820663f63 --- /dev/null +++ b/memory/mozalloc/msvc_raise_wrappers.cpp @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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/. */ + +#include <stdio.h> + +#include "mozalloc_abort.h" + +__declspec(noreturn) static void abort_from_exception(const char* const which, + const char* const what); +static void +abort_from_exception(const char* const which, const char* const what) +{ + fprintf(stderr, "fatal: STL threw %s: ", which); + mozalloc_abort(what); +} + +namespace std { + +// NB: user code is not supposed to touch the std:: namespace. We're +// doing this after careful review because we want to define our own +// exception throwing semantics. Don't try this at home! + +MFBT_API __declspec(noreturn) void +moz_Xinvalid_argument(const char* what) +{ + abort_from_exception("invalid_argument", what); +} + +MFBT_API __declspec(noreturn) void +moz_Xlength_error(const char* what) +{ + abort_from_exception("length_error", what); +} + +MFBT_API __declspec(noreturn) void +moz_Xout_of_range(const char* what) +{ + abort_from_exception("out_of_range", what); +} + +MFBT_API __declspec(noreturn) void +moz_Xoverflow_error(const char* what) +{ + abort_from_exception("overflow_error", what); +} + +MFBT_API __declspec(noreturn) void +moz_Xruntime_error(const char* what) +{ + abort_from_exception("runtime_error", what); +} + +MFBT_API __declspec(noreturn) void +moz_Xbad_function_call() +{ + abort_from_exception("bad_function_call", "bad function call"); +} + +} // namespace std diff --git a/memory/mozalloc/msvc_raise_wrappers.h b/memory/mozalloc/msvc_raise_wrappers.h new file mode 100644 index 000000000..91b77f453 --- /dev/null +++ b/memory/mozalloc/msvc_raise_wrappers.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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 mozilla_msvc_raise_wrappers_h +#define mozilla_msvc_raise_wrappers_h + +#ifdef _XSTDDEF_ +# error "Unable to wrap _RAISE(); CRT _RAISE() already defined" +#endif +#ifdef _XUTILITY_ +# error "Unable to wrap _X[exception](); CRT versions already declared" +#endif +#ifdef _FUNCTIONAL_ +# error "Unable to wrap _Xbad_function_call(); CRT version already declared" +#endif + +#include "mozilla/mozalloc_abort.h" + +// xutility will declare the following functions in the std namespace. +// We #define them to be named differently so we can ensure the exception +// throwing semantics of these functions work exactly the way we want, by +// defining our own versions in msvc_raise_wrappers.cpp. +# define _Xinvalid_argument moz_Xinvalid_argument +# define _Xlength_error moz_Xlength_error +# define _Xout_of_range moz_Xout_of_range +# define _Xoverflow_error moz_Xoverflow_error +# define _Xruntime_error moz_Xruntime_error +// used by <functional> +# define _Xbad_function_call moz_Xbad_function_call + +# include <xstddef> +# include <xutility> + +# undef _RAISE +# define _RAISE(x) mozalloc_abort((x).what()) + +#endif // ifndef mozilla_msvc_raise_wrappers_h diff --git a/memory/mozalloc/staticruntime/moz.build b/memory/mozalloc/staticruntime/moz.build new file mode 100644 index 000000000..55c71907d --- /dev/null +++ b/memory/mozalloc/staticruntime/moz.build @@ -0,0 +1,35 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +NO_VISIBILITY_FLAGS = True + +if CONFIG['WRAP_STL_INCLUDES']: + DEFINES['_HAS_EXCEPTIONS'] = 0 + if CONFIG['MOZ_MSVC_STL_WRAP_RAISE']: + SOURCES += [ + '../msvc_raise_wrappers.cpp', + ] + +UNIFIED_SOURCES += [ + '../mozalloc.cpp', + '../mozalloc_abort.cpp', + '../mozalloc_oom.cpp', +] + +# Keep this file separate to avoid #include'ing windows.h everywhere. +SOURCES += [ + '../winheap.cpp', +] + +LOCAL_INCLUDES += ['!/xpcom'] + +DISABLE_STL_WRAPPING = True + +DEFINES['IMPL_MFBT'] = True + +USE_STATIC_LIBS = True + +Library('mozalloc_staticruntime') diff --git a/memory/mozalloc/throw_gcc.h b/memory/mozalloc/throw_gcc.h new file mode 100644 index 000000000..4264df63d --- /dev/null +++ b/memory/mozalloc/throw_gcc.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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 mozilla_throw_gcc_h +#define mozilla_throw_gcc_h + +#include "mozilla/Attributes.h" + +#include <stdio.h> // snprintf +#include <string.h> // strerror + +// For gcc, we define these inline to abort so that we're absolutely +// certain that (i) no exceptions are thrown from Gecko; (ii) these +// errors are always terminal and caught by breakpad. + +#include "mozilla/mozalloc_abort.h" + +// libc++ 4.0.0 and higher use C++11 [[noreturn]] attributes for the functions +// below, and since clang does not allow mixing __attribute__((noreturn)) and +// [[noreturn]], we have to explicitly use the latter here. See bug 1329520. +#if defined(__clang__) +# if __has_feature(cxx_attributes) && \ + defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 4000 +# define MOZ_THROW_NORETURN [[noreturn]] +# endif +#endif +#ifndef MOZ_THROW_NORETURN +# define MOZ_THROW_NORETURN MOZ_NORETURN +#endif + +namespace std { + +// NB: user code is not supposed to touch the std:: namespace. We're +// doing this after careful review because we want to define our own +// exception throwing semantics. Don't try this at home! + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_bad_exception(void) +{ + mozalloc_abort("fatal: STL threw bad_exception"); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_bad_alloc(void) +{ + mozalloc_abort("fatal: STL threw bad_alloc"); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_bad_cast(void) +{ + mozalloc_abort("fatal: STL threw bad_cast"); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_bad_typeid(void) +{ + mozalloc_abort("fatal: STL threw bad_typeid"); +} + +// used by <functional> +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_bad_function_call(void) +{ + mozalloc_abort("fatal: STL threw bad_function_call"); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_logic_error(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_domain_error(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_invalid_argument(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_length_error(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_out_of_range(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_runtime_error(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_range_error(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_overflow_error(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_underflow_error(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_ios_failure(const char* msg) +{ + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void +__throw_system_error(int err) +{ + char error[128]; + snprintf(error, sizeof(error)-1, + "fatal: STL threw system_error: %s (%d)", strerror(err), err); + mozalloc_abort(error); +} + +} // namespace std + +#undef MOZ_THROW_NORETURN + +#endif // mozilla_throw_gcc_h diff --git a/memory/mozalloc/throw_msvc.h b/memory/mozalloc/throw_msvc.h new file mode 100644 index 000000000..e6ebf46dc --- /dev/null +++ b/memory/mozalloc/throw_msvc.h @@ -0,0 +1,17 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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 mozilla_throw_msvc_h +#define mozilla_throw_msvc_h + +#if defined(MOZ_MSVC_STL_WRAP_RAISE) +# include "msvc_raise_wrappers.h" +#else +# error "Unknown STL wrapper tactic" +#endif + +#endif // mozilla_throw_msvc_h diff --git a/memory/mozalloc/winheap.cpp b/memory/mozalloc/winheap.cpp new file mode 100644 index 000000000..79ff35fff --- /dev/null +++ b/memory/mozalloc/winheap.cpp @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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/. */ + +#include "mozilla/Types.h" +#include <windows.h> + +// Building with USE_STATIC_LIBS = True sets -MT instead of -MD. -MT sets _MT, +// while -MD sets _MT and _DLL. +#if defined(_MT) && !defined(_DLL) +#define MOZ_STATIC_RUNTIME +#endif + +#if defined(MOZ_MEMORY) && !defined(MOZ_STATIC_RUNTIME) +// mozalloc.cpp is part of the same library as mozmemory, thus MOZ_MEMORY_IMPL +// is needed. +#define MOZ_MEMORY_IMPL +#include "mozmemory_wrap.h" + +// See mozmemory_wrap.h for more details. This file is part of libmozglue, so +// it needs to use _impl suffixes. However, with libmozglue growing, this is +// becoming cumbersome, so we will likely use a malloc.h wrapper of some sort +// and allow the use of the functions without a _impl suffix. +#define MALLOC_DECL(name, return_type, ...) \ + extern "C" MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__); +#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC +#include "malloc_decls.h" + +// Warning: C4273: 'HeapAlloc': inconsistent dll linkage +// The Windows headers define HeapAlloc as dllimport, but we define it as +// dllexport, which is a voluntary inconsistency. +#pragma warning(disable: 4273) + +MFBT_API +LPVOID WINAPI HeapAlloc(_In_ HANDLE hHeap, _In_ DWORD dwFlags, + _In_ SIZE_T dwBytes) +{ + if (dwFlags & HEAP_ZERO_MEMORY) { + return calloc_impl(1, dwBytes); + } + return malloc_impl(dwBytes); +} + +MFBT_API +LPVOID WINAPI HeapReAlloc(_In_ HANDLE hHeap, _In_ DWORD dwFlags, + _In_ LPVOID lpMem, _In_ SIZE_T dwBytes) +{ + // The HeapReAlloc contract is that failures preserve the existing + // allocation. We can't try to realloc in-place without possibly + // freeing the original allocation, breaking the contract. + // We also can't guarantee we zero all the memory from the end of + // the original allocation to the end of the new one because of the + // difference between the originally requested size and what + // malloc_usable_size would return us. + // So for both cases, just tell the caller we can't do what they + // requested. + if (dwFlags & (HEAP_REALLOC_IN_PLACE_ONLY | HEAP_ZERO_MEMORY)) { + return NULL; + } + return realloc_impl(lpMem, dwBytes); +} + +MFBT_API +BOOL WINAPI HeapFree(_In_ HANDLE hHeap, _In_ DWORD dwFlags, + _In_ LPVOID lpMem) +{ + free_impl(lpMem); + return true; +} + +#endif |