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:
Ryan Prichard 2020-02-04 15:46:15 -08:00
parent 1a1888f801
commit afa983c8d4
7 changed files with 352 additions and 315 deletions

View file

@ -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",
],

View file

@ -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
View 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();
}
}

View file

@ -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

View file

@ -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 */
}

View file

@ -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",

View 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;
}
}