// Copyright (c) 2009, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef GOOGLE_BREAKPAD_COMMON_MEMORY_H_ #define GOOGLE_BREAKPAD_COMMON_MEMORY_H_ #include #include #include #include #ifdef __APPLE__ #define sys_mmap mmap #define sys_mmap2 mmap #define sys_munmap munmap #define MAP_ANONYMOUS MAP_ANON #else #include "third_party/lss/linux_syscall_support.h" #endif namespace google_breakpad { // This is very simple allocator which fetches pages from the kernel directly. // Thus, it can be used even when the heap may be corrupted. // // There is no free operation. The pages are only freed when the object is // destroyed. class PageAllocator { public: PageAllocator() : page_size_(getpagesize()), last_(NULL), current_page_(NULL), page_offset_(0) { } ~PageAllocator() { FreeAll(); } void *Alloc(unsigned bytes) { if (!bytes) return NULL; if (current_page_ && page_size_ - page_offset_ >= bytes) { uint8_t *const ret = current_page_ + page_offset_; page_offset_ += bytes; if (page_offset_ == page_size_) { page_offset_ = 0; current_page_ = NULL; } return ret; } const unsigned pages = (bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_; uint8_t *const ret = GetNPages(pages); if (!ret) return NULL; page_offset_ = (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) % page_size_; current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL; return ret + sizeof(PageHeader); } private: uint8_t *GetNPages(unsigned num_pages) { #ifdef __x86_64 void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); #else void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); #endif if (a == MAP_FAILED) return NULL; struct PageHeader *header = reinterpret_cast(a); header->next = last_; header->num_pages = num_pages; last_ = header; return reinterpret_cast(a); } void FreeAll() { PageHeader *next; for (PageHeader *cur = last_; cur; cur = next) { next = cur->next; sys_munmap(cur, cur->num_pages * page_size_); } } struct PageHeader { PageHeader *next; // pointer to the start of the next set of pages. unsigned num_pages; // the number of pages in this set. }; const unsigned page_size_; PageHeader *last_; uint8_t *current_page_; unsigned page_offset_; }; // A wasteful vector is like a normal std::vector, except that it's very much // simplier and it allocates memory from a PageAllocator. It's wasteful // because, when resizing, it always allocates a whole new array since the // PageAllocator doesn't support realloc. template class wasteful_vector { public: wasteful_vector(PageAllocator *allocator, unsigned size_hint = 16) : allocator_(allocator), a_((T*) allocator->Alloc(sizeof(T) * size_hint)), allocated_(size_hint), used_(0) { } T& back() { return a_[used_ - 1]; } const T& back() const { return a_[used_ - 1]; } bool empty() const { return used_ == 0; } void push_back(const T& new_element) { if (used_ == allocated_) Realloc(allocated_ * 2); a_[used_++] = new_element; } size_t size() const { return used_; } void resize(unsigned sz, T c = T()) { // No need to test "sz >= 0", as "sz" is unsigned. if (sz <= used_) { used_ = sz; } else { unsigned a = allocated_; if (sz > a) { while (sz > a) { a *= 2; } Realloc(a); } while (sz > used_) { a_[used_++] = c; } } } T& operator[](size_t index) { return a_[index]; } const T& operator[](size_t index) const { return a_[index]; } private: void Realloc(unsigned new_size) { T *new_array = reinterpret_cast(allocator_->Alloc(sizeof(T) * new_size)); memcpy(new_array, a_, used_ * sizeof(T)); a_ = new_array; allocated_ = new_size; } PageAllocator *const allocator_; T *a_; // pointer to an array of |allocated_| elements. unsigned allocated_; // size of |a_|, in elements. unsigned used_; // number of used slots in |a_|. }; } // namespace google_breakpad inline void* operator new(size_t nbytes, google_breakpad::PageAllocator& allocator) { return allocator.Alloc(nbytes); } #endif // GOOGLE_BREAKPAD_COMMON_MEMORY_H_