Rewrite __cxa_atexit / __cxa_finalize
Simplify: - Use a single memory-mapped region to hold the table of destructors. Double its capacity each time it is expanded. - Add a recompaction pass at the end of __cxa_finalize that shifts entries forward and uses madvise to clean pages. Bug: http://b/148055738 Test: bionic-unit-tests Change-Id: Ieb9da2b88640a8a5277d217b43826b5b7e246781
This commit is contained in:
parent
1a1888f801
commit
afa983c8d4
7 changed files with 352 additions and 315 deletions
|
@ -1219,7 +1219,7 @@ cc_library_static {
|
|||
// so we can't include them in libc_ndk.a.
|
||||
"bionic/__cxa_thread_atexit_impl.cpp",
|
||||
"bionic/android_unsafe_frame_pointer_chase.cpp",
|
||||
"stdlib/atexit.c",
|
||||
"bionic/atexit.cpp",
|
||||
"bionic/fork.cpp",
|
||||
],
|
||||
|
||||
|
|
112
libc/NOTICE
112
libc/NOTICE
|
@ -1,32 +1,3 @@
|
|||
Copyright (c) 2014, ARM Limited
|
||||
All rights Reserved.
|
||||
Copyright (c) 2014, Linaro Ltd.
|
||||
|
||||
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 the company 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
|
||||
HOLDER 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.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2014, Linaro Limited
|
||||
All rights reserved.
|
||||
|
||||
|
@ -5225,34 +5196,6 @@ SUCH DAMAGE.
|
|||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2012, Linaro Limited
|
||||
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 the Linaro 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
|
||||
HOLDER 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.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2012-2013, Linaro Limited
|
||||
All rights reserved.
|
||||
|
||||
|
@ -5465,33 +5408,6 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2013-2015, Linaro Limited
|
||||
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 the Linaro 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
|
||||
HOLDER 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
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2014, Intel Corporation
|
||||
All rights reserved.
|
||||
|
||||
|
@ -5632,34 +5548,6 @@ SUCH DAMAGE.
|
|||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2017 ARM Ltd
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. 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.
|
||||
3. The name of the company may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY ARM LTD ``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 ARM LTD 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.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Copyright (c)1999 Citrus Project,
|
||||
All rights reserved.
|
||||
|
||||
|
|
263
libc/bionic/atexit.cpp
Normal file
263
libc/bionic/atexit.cpp
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "atexit.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include <async_safe/CHECK.h>
|
||||
#include <async_safe/log.h>
|
||||
|
||||
#include "platform/bionic/page.h"
|
||||
|
||||
extern "C" void __libc_stdio_cleanup();
|
||||
extern "C" void __unregister_atfork(void* dso);
|
||||
|
||||
namespace {
|
||||
|
||||
struct AtexitEntry {
|
||||
void (*fn)(void*); // the __cxa_atexit callback
|
||||
void* arg; // argument for `fn` callback
|
||||
void* dso; // shared module handle
|
||||
};
|
||||
|
||||
class AtexitArray {
|
||||
public:
|
||||
size_t size() const { return size_; }
|
||||
uint64_t total_appends() const { return total_appends_; }
|
||||
const AtexitEntry& operator[](size_t idx) const { return array_[idx]; }
|
||||
|
||||
bool append_entry(const AtexitEntry& entry);
|
||||
AtexitEntry extract_entry(size_t idx);
|
||||
void recompact();
|
||||
|
||||
private:
|
||||
AtexitEntry* array_;
|
||||
size_t size_;
|
||||
size_t extracted_count_;
|
||||
size_t capacity_;
|
||||
|
||||
// An entry can be appended by a __cxa_finalize callback. Track the number of appends so we
|
||||
// restart concurrent __cxa_finalize passes.
|
||||
uint64_t total_appends_;
|
||||
|
||||
static size_t round_up_to_page_bytes(size_t capacity) {
|
||||
return PAGE_END(capacity * sizeof(AtexitEntry));
|
||||
}
|
||||
|
||||
static size_t next_capacity(size_t capacity) {
|
||||
// Double the capacity each time.
|
||||
size_t result = round_up_to_page_bytes(MAX(1, capacity * 2)) / sizeof(AtexitEntry);
|
||||
CHECK(result > capacity);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Recompact the array if it will save at least one page of memory at the end.
|
||||
bool needs_recompaction() {
|
||||
return round_up_to_page_bytes(size_ - extracted_count_) < round_up_to_page_bytes(size_);
|
||||
}
|
||||
|
||||
void set_writable(bool writable);
|
||||
bool expand_capacity();
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool AtexitArray::append_entry(const AtexitEntry& entry) {
|
||||
bool result = false;
|
||||
|
||||
set_writable(true);
|
||||
if (size_ < capacity_ || expand_capacity()) {
|
||||
array_[size_++] = entry;
|
||||
++total_appends_;
|
||||
result = true;
|
||||
}
|
||||
set_writable(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Extract an entry and return it.
|
||||
AtexitEntry AtexitArray::extract_entry(size_t idx) {
|
||||
AtexitEntry result = array_[idx];
|
||||
|
||||
set_writable(true);
|
||||
array_[idx] = {};
|
||||
++extracted_count_;
|
||||
set_writable(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AtexitArray::recompact() {
|
||||
if (!needs_recompaction()) return;
|
||||
|
||||
set_writable(true);
|
||||
|
||||
// Optimization: quickly skip over the initial non-null entries.
|
||||
size_t src = 0, dst = 0;
|
||||
while (src < size_ && array_[src].fn != nullptr) {
|
||||
++src;
|
||||
++dst;
|
||||
}
|
||||
|
||||
// Shift the non-null entries forward, and zero out the removed entries at the end of the array.
|
||||
for (; src < size_; ++src) {
|
||||
const AtexitEntry entry = array_[src];
|
||||
array_[src] = {};
|
||||
if (entry.fn != nullptr) {
|
||||
array_[dst++] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
// If the table uses fewer pages, clean the pages at the end.
|
||||
size_t old_bytes = round_up_to_page_bytes(size_);
|
||||
size_t new_bytes = round_up_to_page_bytes(dst);
|
||||
if (new_bytes < old_bytes) {
|
||||
madvise(reinterpret_cast<char*>(array_) + new_bytes, old_bytes - new_bytes, MADV_DONTNEED);
|
||||
}
|
||||
|
||||
size_ = dst;
|
||||
extracted_count_ = 0;
|
||||
|
||||
set_writable(false);
|
||||
}
|
||||
|
||||
// Use mprotect to make the array writable or read-only. Returns true on success. Making the array
|
||||
// read-only could protect against either unintentional or malicious corruption of the array.
|
||||
void AtexitArray::set_writable(bool writable) {
|
||||
if (array_ == nullptr) return;
|
||||
const int prot = PROT_READ | (writable ? PROT_WRITE : 0);
|
||||
if (mprotect(array_, round_up_to_page_bytes(capacity_), prot) != 0) {
|
||||
async_safe_fatal("mprotect failed on atexit array: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
bool AtexitArray::expand_capacity() {
|
||||
const size_t new_capacity = next_capacity(capacity_);
|
||||
const size_t new_capacity_bytes = round_up_to_page_bytes(new_capacity);
|
||||
|
||||
void* new_pages;
|
||||
if (array_ == nullptr) {
|
||||
new_pages = mmap(nullptr, new_capacity_bytes, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
} else {
|
||||
new_pages =
|
||||
mremap(array_, round_up_to_page_bytes(capacity_), new_capacity_bytes, MREMAP_MAYMOVE);
|
||||
}
|
||||
if (new_pages == MAP_FAILED) {
|
||||
async_safe_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"__cxa_atexit: mmap/mremap failed to allocate %zu bytes: %s",
|
||||
new_capacity_bytes, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, new_pages, new_capacity_bytes, "atexit handlers");
|
||||
array_ = static_cast<AtexitEntry*>(new_pages);
|
||||
capacity_ = new_capacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
static AtexitArray g_array;
|
||||
static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static inline void atexit_lock() {
|
||||
pthread_mutex_lock(&g_atexit_lock);
|
||||
}
|
||||
|
||||
static inline void atexit_unlock() {
|
||||
pthread_mutex_unlock(&g_atexit_lock);
|
||||
}
|
||||
|
||||
// Register a function to be called either when a library is unloaded (dso != nullptr), or when the
|
||||
// program exits (dso == nullptr). The `dso` argument is typically the address of a hidden
|
||||
// __dso_handle variable. This function is also used as the backend for the atexit function.
|
||||
//
|
||||
// See https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dso-dtor.
|
||||
//
|
||||
int __cxa_atexit(void (*func)(void*), void* arg, void* dso) {
|
||||
int result = -1;
|
||||
|
||||
if (func != nullptr) {
|
||||
atexit_lock();
|
||||
if (g_array.append_entry({.fn = func, .arg = arg, .dso = dso})) {
|
||||
result = 0;
|
||||
}
|
||||
atexit_unlock();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void __cxa_finalize(void* dso) {
|
||||
atexit_lock();
|
||||
|
||||
static uint32_t call_depth = 0;
|
||||
++call_depth;
|
||||
|
||||
restart:
|
||||
const uint64_t total_appends = g_array.total_appends();
|
||||
|
||||
for (ssize_t i = g_array.size() - 1; i >= 0; --i) {
|
||||
if (g_array[i].fn == nullptr || (dso != nullptr && g_array[i].dso != dso)) continue;
|
||||
|
||||
// Clear the entry in the array because its DSO handle will become invalid, and to avoid calling
|
||||
// an entry again if __cxa_finalize is called recursively.
|
||||
const AtexitEntry entry = g_array.extract_entry(i);
|
||||
|
||||
atexit_unlock();
|
||||
entry.fn(entry.arg);
|
||||
atexit_lock();
|
||||
|
||||
if (g_array.total_appends() != total_appends) goto restart;
|
||||
}
|
||||
|
||||
// Avoid recompaction on recursive calls because it's unnecessary and would require earlier,
|
||||
// concurrent __cxa_finalize calls to restart. Skip recompaction on program exit too
|
||||
// (dso == nullptr), because the memory will be reclaimed soon anyway.
|
||||
--call_depth;
|
||||
if (call_depth == 0 && dso != nullptr) {
|
||||
g_array.recompact();
|
||||
}
|
||||
|
||||
atexit_unlock();
|
||||
|
||||
if (dso != nullptr) {
|
||||
__unregister_atfork(dso);
|
||||
} else {
|
||||
// If called via exit(), flush output of all open files.
|
||||
__libc_stdio_cleanup();
|
||||
}
|
||||
}
|
|
@ -30,5 +30,13 @@
|
|||
*
|
||||
*/
|
||||
|
||||
int __cxa_atexit(void (*)(void *), void *, void *);
|
||||
void __cxa_finalize(void *);
|
||||
#pragma once
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int __cxa_atexit(void (*)(void*), void*, void*);
|
||||
void __cxa_finalize(void*);
|
||||
|
||||
__END_DECLS
|
|
@ -1,200 +0,0 @@
|
|||
/* $OpenBSD: atexit.c,v 1.20 2014/07/11 09:51:37 kettenis Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2002 Daniel Hartmeier
|
||||
* 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.
|
||||
*
|
||||
* 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 HOLDERS 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "atexit.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define _ATEXIT_LOCK() pthread_mutex_lock(&g_atexit_lock)
|
||||
#define _ATEXIT_UNLOCK() pthread_mutex_unlock(&g_atexit_lock)
|
||||
|
||||
struct atexit {
|
||||
struct atexit *next; /* next in list */
|
||||
int ind; /* next index in this table */
|
||||
int max; /* max entries >= ATEXIT_SIZE */
|
||||
struct atexit_fn {
|
||||
void (*fn_ptr)(void *);
|
||||
void *fn_arg; /* argument for CXA callback */
|
||||
void *fn_dso; /* shared module handle */
|
||||
} fns[1]; /* the table itself */
|
||||
};
|
||||
|
||||
static struct atexit *__atexit;
|
||||
static int restartloop;
|
||||
|
||||
/* BEGIN android-changed: __unregister_atfork is used by __cxa_finalize */
|
||||
extern void __unregister_atfork(void* dso);
|
||||
/* END android-changed */
|
||||
|
||||
/*
|
||||
* Function pointers are stored in a linked list of pages. The list
|
||||
* is initially empty, and pages are allocated on demand. The first
|
||||
* function pointer in the first allocated page (the last one in
|
||||
* the linked list) is reserved for the cleanup function.
|
||||
*
|
||||
* Outside the following functions, all pages are mprotect()'ed
|
||||
* to prevent unintentional/malicious corruption.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Register a function to be performed at exit or when a shared object
|
||||
* with the given dso handle is unloaded dynamically. Also used as
|
||||
* the backend for atexit(). For more info on this API, see:
|
||||
*
|
||||
* http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor
|
||||
*/
|
||||
int
|
||||
__cxa_atexit(void (*func)(void *), void *arg, void *dso)
|
||||
{
|
||||
struct atexit_fn *fnp;
|
||||
size_t pgsize = getpagesize();
|
||||
int ret = -1;
|
||||
|
||||
if (pgsize < sizeof(struct atexit))
|
||||
return (-1);
|
||||
_ATEXIT_LOCK();
|
||||
struct atexit *p = __atexit;
|
||||
if (p != NULL) {
|
||||
if (p->ind + 1 >= p->max)
|
||||
p = NULL;
|
||||
else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
|
||||
goto unlock;
|
||||
}
|
||||
if (p == NULL) {
|
||||
p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
|
||||
MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (p == MAP_FAILED)
|
||||
goto unlock;
|
||||
/* BEGIN android-changed */
|
||||
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, p, pgsize,
|
||||
"atexit handlers");
|
||||
/* END android-changed */
|
||||
if (__atexit == NULL) {
|
||||
memset(&p->fns[0], 0, sizeof(p->fns[0]));
|
||||
p->ind = 1;
|
||||
} else
|
||||
p->ind = 0;
|
||||
p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
|
||||
sizeof(p->fns[0]);
|
||||
p->next = __atexit;
|
||||
__atexit = p;
|
||||
}
|
||||
fnp = &p->fns[p->ind++];
|
||||
fnp->fn_ptr = func;
|
||||
fnp->fn_arg = arg;
|
||||
fnp->fn_dso = dso;
|
||||
if (mprotect(p, pgsize, PROT_READ))
|
||||
goto unlock;
|
||||
restartloop = 1;
|
||||
ret = 0;
|
||||
unlock:
|
||||
_ATEXIT_UNLOCK();
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call all handlers registered with __cxa_atexit() for the shared
|
||||
* object owning 'dso'.
|
||||
* Note: if 'dso' is NULL, then all remaining handlers are called.
|
||||
*/
|
||||
void
|
||||
__cxa_finalize(void *dso)
|
||||
{
|
||||
struct atexit *p, *q;
|
||||
struct atexit_fn fn;
|
||||
int n, pgsize = getpagesize();
|
||||
static int call_depth;
|
||||
|
||||
_ATEXIT_LOCK();
|
||||
call_depth++;
|
||||
|
||||
restart:
|
||||
restartloop = 0;
|
||||
for (p = __atexit; p != NULL; p = p->next) {
|
||||
for (n = p->ind; --n >= 0;) {
|
||||
if (p->fns[n].fn_ptr == NULL)
|
||||
continue; /* already called */
|
||||
if (dso != NULL && dso != p->fns[n].fn_dso)
|
||||
continue; /* wrong DSO */
|
||||
|
||||
/*
|
||||
* Mark handler as having been already called to avoid
|
||||
* dupes and loops, then call the appropriate function.
|
||||
*/
|
||||
fn = p->fns[n];
|
||||
if (mprotect(p, pgsize, PROT_READ | PROT_WRITE) == 0) {
|
||||
p->fns[n].fn_ptr = NULL;
|
||||
mprotect(p, pgsize, PROT_READ);
|
||||
}
|
||||
_ATEXIT_UNLOCK();
|
||||
(*fn.fn_ptr)(fn.fn_arg);
|
||||
_ATEXIT_LOCK();
|
||||
if (restartloop)
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
call_depth--;
|
||||
|
||||
/*
|
||||
* If called via exit(), unmap the pages since we have now run
|
||||
* all the handlers. We defer this until calldepth == 0 so that
|
||||
* we don't unmap things prematurely if called recursively.
|
||||
*/
|
||||
if (dso == NULL && call_depth == 0) {
|
||||
for (p = __atexit; p != NULL; ) {
|
||||
q = p;
|
||||
p = p->next;
|
||||
munmap(q, pgsize);
|
||||
}
|
||||
__atexit = NULL;
|
||||
}
|
||||
_ATEXIT_UNLOCK();
|
||||
|
||||
/* If called via exit(), flush output of all open files. */
|
||||
if (dso == NULL) {
|
||||
extern void __libc_stdio_cleanup(void);
|
||||
__libc_stdio_cleanup();
|
||||
}
|
||||
|
||||
/* BEGIN android-changed: call __unregister_atfork if dso is not null */
|
||||
if (dso != NULL) {
|
||||
__unregister_atfork(dso);
|
||||
}
|
||||
/* END android-changed */
|
||||
}
|
|
@ -83,6 +83,7 @@ cc_test_library {
|
|||
defaults: ["bionic_tests_defaults"],
|
||||
srcs: [
|
||||
"__aeabi_read_tp_test.cpp",
|
||||
"__cxa_atexit_test.cpp",
|
||||
"alloca_test.cpp",
|
||||
"android_get_device_api_level.cpp",
|
||||
"arpa_inet_test.cpp",
|
||||
|
|
77
tests/__cxa_atexit_test.cpp
Normal file
77
tests/__cxa_atexit_test.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(__cxa_atexit, simple) {
|
||||
int counter = 0;
|
||||
|
||||
__cxxabiv1::__cxa_atexit([](void* arg) { ++*static_cast<int*>(arg); }, &counter, &counter);
|
||||
|
||||
__cxxabiv1::__cxa_finalize(&counter);
|
||||
ASSERT_EQ(counter, 1);
|
||||
|
||||
// The handler won't be called twice.
|
||||
__cxxabiv1::__cxa_finalize(&counter);
|
||||
ASSERT_EQ(counter, 1);
|
||||
}
|
||||
|
||||
TEST(__cxa_atexit, order) {
|
||||
static std::vector<int> actual;
|
||||
|
||||
char handles[2];
|
||||
|
||||
auto append_to_actual = [](void* arg) {
|
||||
int* idx = static_cast<int*>(arg);
|
||||
actual.push_back(*idx);
|
||||
delete idx;
|
||||
};
|
||||
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
__cxxabiv1::__cxa_atexit(append_to_actual, new int{i}, &handles[i % 2]);
|
||||
}
|
||||
|
||||
__cxxabiv1::__cxa_finalize(&handles[0]);
|
||||
|
||||
for (int i = 500; i < 750; ++i) {
|
||||
__cxxabiv1::__cxa_atexit(append_to_actual, new int{i}, &handles[1]);
|
||||
}
|
||||
|
||||
__cxxabiv1::__cxa_finalize(&handles[1]);
|
||||
|
||||
std::vector<int> expected;
|
||||
for (int i = 498; i >= 0; i -= 2) expected.push_back(i);
|
||||
for (int i = 749; i >= 500; --i) expected.push_back(i);
|
||||
for (int i = 499; i >= 1; i -= 2) expected.push_back(i);
|
||||
|
||||
ASSERT_EQ(expected.size(), actual.size());
|
||||
for (size_t i = 0; i < expected.size(); ++i) {
|
||||
ASSERT_EQ(expected[i], actual[i]) << "index=" << i;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue