xref: /aosp_15_r20/bionic/libc/bionic/atexit.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1*8d67ca89SAndroid Build Coastguard Worker /*
2*8d67ca89SAndroid Build Coastguard Worker  * Copyright (C) 2020 The Android Open Source Project
3*8d67ca89SAndroid Build Coastguard Worker  * All rights reserved.
4*8d67ca89SAndroid Build Coastguard Worker  *
5*8d67ca89SAndroid Build Coastguard Worker  * Redistribution and use in source and binary forms, with or without
6*8d67ca89SAndroid Build Coastguard Worker  * modification, are permitted provided that the following conditions
7*8d67ca89SAndroid Build Coastguard Worker  * are met:
8*8d67ca89SAndroid Build Coastguard Worker  *  * Redistributions of source code must retain the above copyright
9*8d67ca89SAndroid Build Coastguard Worker  *    notice, this list of conditions and the following disclaimer.
10*8d67ca89SAndroid Build Coastguard Worker  *  * Redistributions in binary form must reproduce the above copyright
11*8d67ca89SAndroid Build Coastguard Worker  *    notice, this list of conditions and the following disclaimer in
12*8d67ca89SAndroid Build Coastguard Worker  *    the documentation and/or other materials provided with the
13*8d67ca89SAndroid Build Coastguard Worker  *    distribution.
14*8d67ca89SAndroid Build Coastguard Worker  *
15*8d67ca89SAndroid Build Coastguard Worker  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16*8d67ca89SAndroid Build Coastguard Worker  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17*8d67ca89SAndroid Build Coastguard Worker  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18*8d67ca89SAndroid Build Coastguard Worker  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19*8d67ca89SAndroid Build Coastguard Worker  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20*8d67ca89SAndroid Build Coastguard Worker  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21*8d67ca89SAndroid Build Coastguard Worker  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22*8d67ca89SAndroid Build Coastguard Worker  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23*8d67ca89SAndroid Build Coastguard Worker  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*8d67ca89SAndroid Build Coastguard Worker  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25*8d67ca89SAndroid Build Coastguard Worker  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*8d67ca89SAndroid Build Coastguard Worker  * SUCH DAMAGE.
27*8d67ca89SAndroid Build Coastguard Worker  */
28*8d67ca89SAndroid Build Coastguard Worker 
29*8d67ca89SAndroid Build Coastguard Worker #include "atexit.h"
30*8d67ca89SAndroid Build Coastguard Worker 
31*8d67ca89SAndroid Build Coastguard Worker #include <errno.h>
32*8d67ca89SAndroid Build Coastguard Worker #include <pthread.h>
33*8d67ca89SAndroid Build Coastguard Worker #include <stdint.h>
34*8d67ca89SAndroid Build Coastguard Worker #include <stdlib.h>
35*8d67ca89SAndroid Build Coastguard Worker #include <string.h>
36*8d67ca89SAndroid Build Coastguard Worker #include <sys/mman.h>
37*8d67ca89SAndroid Build Coastguard Worker #include <sys/param.h>
38*8d67ca89SAndroid Build Coastguard Worker #include <sys/prctl.h>
39*8d67ca89SAndroid Build Coastguard Worker 
40*8d67ca89SAndroid Build Coastguard Worker #include <async_safe/CHECK.h>
41*8d67ca89SAndroid Build Coastguard Worker #include <async_safe/log.h>
42*8d67ca89SAndroid Build Coastguard Worker 
43*8d67ca89SAndroid Build Coastguard Worker #include "platform/bionic/page.h"
44*8d67ca89SAndroid Build Coastguard Worker 
45*8d67ca89SAndroid Build Coastguard Worker extern "C" void __libc_stdio_cleanup();
46*8d67ca89SAndroid Build Coastguard Worker extern "C" void __unregister_atfork(void* dso);
47*8d67ca89SAndroid Build Coastguard Worker 
48*8d67ca89SAndroid Build Coastguard Worker namespace {
49*8d67ca89SAndroid Build Coastguard Worker 
50*8d67ca89SAndroid Build Coastguard Worker struct AtexitEntry {
51*8d67ca89SAndroid Build Coastguard Worker   void (*fn)(void*);  // the __cxa_atexit callback
52*8d67ca89SAndroid Build Coastguard Worker   void* arg;          // argument for `fn` callback
53*8d67ca89SAndroid Build Coastguard Worker   void* dso;          // shared module handle
54*8d67ca89SAndroid Build Coastguard Worker };
55*8d67ca89SAndroid Build Coastguard Worker 
56*8d67ca89SAndroid Build Coastguard Worker class AtexitArray {
57*8d67ca89SAndroid Build Coastguard Worker  public:
size() const58*8d67ca89SAndroid Build Coastguard Worker   size_t size() const { return size_; }
total_appends() const59*8d67ca89SAndroid Build Coastguard Worker   uint64_t total_appends() const { return total_appends_; }
operator [](size_t idx) const60*8d67ca89SAndroid Build Coastguard Worker   const AtexitEntry& operator[](size_t idx) const { return array_[idx]; }
61*8d67ca89SAndroid Build Coastguard Worker 
62*8d67ca89SAndroid Build Coastguard Worker   bool append_entry(const AtexitEntry& entry);
63*8d67ca89SAndroid Build Coastguard Worker   AtexitEntry extract_entry(size_t idx);
64*8d67ca89SAndroid Build Coastguard Worker   void recompact();
65*8d67ca89SAndroid Build Coastguard Worker 
66*8d67ca89SAndroid Build Coastguard Worker  private:
67*8d67ca89SAndroid Build Coastguard Worker   AtexitEntry* array_;
68*8d67ca89SAndroid Build Coastguard Worker   size_t size_;
69*8d67ca89SAndroid Build Coastguard Worker   size_t extracted_count_;
70*8d67ca89SAndroid Build Coastguard Worker   size_t capacity_;
71*8d67ca89SAndroid Build Coastguard Worker 
72*8d67ca89SAndroid Build Coastguard Worker   // An entry can be appended by a __cxa_finalize callback. Track the number of appends so we
73*8d67ca89SAndroid Build Coastguard Worker   // restart concurrent __cxa_finalize passes.
74*8d67ca89SAndroid Build Coastguard Worker   uint64_t total_appends_;
75*8d67ca89SAndroid Build Coastguard Worker 
page_start_of_index(size_t idx)76*8d67ca89SAndroid Build Coastguard Worker   static size_t page_start_of_index(size_t idx) { return page_start(idx * sizeof(AtexitEntry)); }
page_end_of_index(size_t idx)77*8d67ca89SAndroid Build Coastguard Worker   static size_t page_end_of_index(size_t idx) { return page_end(idx * sizeof(AtexitEntry)); }
78*8d67ca89SAndroid Build Coastguard Worker 
79*8d67ca89SAndroid Build Coastguard Worker   // Recompact the array if it will save at least one page of memory at the end.
needs_recompaction() const80*8d67ca89SAndroid Build Coastguard Worker   bool needs_recompaction() const {
81*8d67ca89SAndroid Build Coastguard Worker     return page_end_of_index(size_ - extracted_count_) < page_end_of_index(size_);
82*8d67ca89SAndroid Build Coastguard Worker   }
83*8d67ca89SAndroid Build Coastguard Worker 
84*8d67ca89SAndroid Build Coastguard Worker   void set_writable(bool writable, size_t start_idx, size_t num_entries);
85*8d67ca89SAndroid Build Coastguard Worker   static bool next_capacity(size_t capacity, size_t* result);
86*8d67ca89SAndroid Build Coastguard Worker   bool expand_capacity();
87*8d67ca89SAndroid Build Coastguard Worker };
88*8d67ca89SAndroid Build Coastguard Worker 
89*8d67ca89SAndroid Build Coastguard Worker }  // anonymous namespace
90*8d67ca89SAndroid Build Coastguard Worker 
append_entry(const AtexitEntry & entry)91*8d67ca89SAndroid Build Coastguard Worker bool AtexitArray::append_entry(const AtexitEntry& entry) {
92*8d67ca89SAndroid Build Coastguard Worker   if (size_ >= capacity_ && !expand_capacity()) return false;
93*8d67ca89SAndroid Build Coastguard Worker 
94*8d67ca89SAndroid Build Coastguard Worker   size_t idx = size_++;
95*8d67ca89SAndroid Build Coastguard Worker 
96*8d67ca89SAndroid Build Coastguard Worker   set_writable(true, idx, 1);
97*8d67ca89SAndroid Build Coastguard Worker   array_[idx] = entry;
98*8d67ca89SAndroid Build Coastguard Worker   ++total_appends_;
99*8d67ca89SAndroid Build Coastguard Worker   set_writable(false, idx, 1);
100*8d67ca89SAndroid Build Coastguard Worker 
101*8d67ca89SAndroid Build Coastguard Worker   return true;
102*8d67ca89SAndroid Build Coastguard Worker }
103*8d67ca89SAndroid Build Coastguard Worker 
104*8d67ca89SAndroid Build Coastguard Worker // Extract an entry and return it.
extract_entry(size_t idx)105*8d67ca89SAndroid Build Coastguard Worker AtexitEntry AtexitArray::extract_entry(size_t idx) {
106*8d67ca89SAndroid Build Coastguard Worker   AtexitEntry result = array_[idx];
107*8d67ca89SAndroid Build Coastguard Worker 
108*8d67ca89SAndroid Build Coastguard Worker   set_writable(true, idx, 1);
109*8d67ca89SAndroid Build Coastguard Worker   array_[idx] = {};
110*8d67ca89SAndroid Build Coastguard Worker   ++extracted_count_;
111*8d67ca89SAndroid Build Coastguard Worker   set_writable(false, idx, 1);
112*8d67ca89SAndroid Build Coastguard Worker 
113*8d67ca89SAndroid Build Coastguard Worker   return result;
114*8d67ca89SAndroid Build Coastguard Worker }
115*8d67ca89SAndroid Build Coastguard Worker 
recompact()116*8d67ca89SAndroid Build Coastguard Worker void AtexitArray::recompact() {
117*8d67ca89SAndroid Build Coastguard Worker   if (!needs_recompaction()) return;
118*8d67ca89SAndroid Build Coastguard Worker 
119*8d67ca89SAndroid Build Coastguard Worker   set_writable(true, 0, size_);
120*8d67ca89SAndroid Build Coastguard Worker 
121*8d67ca89SAndroid Build Coastguard Worker   // Optimization: quickly skip over the initial non-null entries.
122*8d67ca89SAndroid Build Coastguard Worker   size_t src = 0, dst = 0;
123*8d67ca89SAndroid Build Coastguard Worker   while (src < size_ && array_[src].fn != nullptr) {
124*8d67ca89SAndroid Build Coastguard Worker     ++src;
125*8d67ca89SAndroid Build Coastguard Worker     ++dst;
126*8d67ca89SAndroid Build Coastguard Worker   }
127*8d67ca89SAndroid Build Coastguard Worker 
128*8d67ca89SAndroid Build Coastguard Worker   // Shift the non-null entries forward, and zero out the removed entries at the end of the array.
129*8d67ca89SAndroid Build Coastguard Worker   for (; src < size_; ++src) {
130*8d67ca89SAndroid Build Coastguard Worker     const AtexitEntry entry = array_[src];
131*8d67ca89SAndroid Build Coastguard Worker     array_[src] = {};
132*8d67ca89SAndroid Build Coastguard Worker     if (entry.fn != nullptr) {
133*8d67ca89SAndroid Build Coastguard Worker       array_[dst++] = entry;
134*8d67ca89SAndroid Build Coastguard Worker     }
135*8d67ca89SAndroid Build Coastguard Worker   }
136*8d67ca89SAndroid Build Coastguard Worker 
137*8d67ca89SAndroid Build Coastguard Worker   // If the table uses fewer pages, clean the pages at the end.
138*8d67ca89SAndroid Build Coastguard Worker   size_t old_bytes = page_end_of_index(size_);
139*8d67ca89SAndroid Build Coastguard Worker   size_t new_bytes = page_end_of_index(dst);
140*8d67ca89SAndroid Build Coastguard Worker   if (new_bytes < old_bytes) {
141*8d67ca89SAndroid Build Coastguard Worker     madvise(reinterpret_cast<char*>(array_) + new_bytes, old_bytes - new_bytes, MADV_DONTNEED);
142*8d67ca89SAndroid Build Coastguard Worker   }
143*8d67ca89SAndroid Build Coastguard Worker 
144*8d67ca89SAndroid Build Coastguard Worker   set_writable(false, 0, size_);
145*8d67ca89SAndroid Build Coastguard Worker 
146*8d67ca89SAndroid Build Coastguard Worker   size_ = dst;
147*8d67ca89SAndroid Build Coastguard Worker   extracted_count_ = 0;
148*8d67ca89SAndroid Build Coastguard Worker }
149*8d67ca89SAndroid Build Coastguard Worker 
150*8d67ca89SAndroid Build Coastguard Worker // Use mprotect to make the array writable or read-only. Returns true on success. Making the array
151*8d67ca89SAndroid Build Coastguard Worker // read-only could protect against either unintentional or malicious corruption of the array.
set_writable(bool writable,size_t start_idx,size_t num_entries)152*8d67ca89SAndroid Build Coastguard Worker void AtexitArray::set_writable(bool writable, size_t start_idx, size_t num_entries) {
153*8d67ca89SAndroid Build Coastguard Worker   if (array_ == nullptr) return;
154*8d67ca89SAndroid Build Coastguard Worker 
155*8d67ca89SAndroid Build Coastguard Worker   const size_t start_byte = page_start_of_index(start_idx);
156*8d67ca89SAndroid Build Coastguard Worker   const size_t stop_byte = page_end_of_index(start_idx + num_entries);
157*8d67ca89SAndroid Build Coastguard Worker   const size_t byte_len = stop_byte - start_byte;
158*8d67ca89SAndroid Build Coastguard Worker 
159*8d67ca89SAndroid Build Coastguard Worker   const int prot = PROT_READ | (writable ? PROT_WRITE : 0);
160*8d67ca89SAndroid Build Coastguard Worker   if (mprotect(reinterpret_cast<char*>(array_) + start_byte, byte_len, prot) != 0) {
161*8d67ca89SAndroid Build Coastguard Worker     async_safe_fatal("mprotect failed on atexit array: %m");
162*8d67ca89SAndroid Build Coastguard Worker   }
163*8d67ca89SAndroid Build Coastguard Worker }
164*8d67ca89SAndroid Build Coastguard Worker 
165*8d67ca89SAndroid Build Coastguard Worker // Approximately double the capacity. Returns true if successful (no overflow). AtexitEntry is
166*8d67ca89SAndroid Build Coastguard Worker // smaller than a page, but this function should still be correct even if AtexitEntry were larger
167*8d67ca89SAndroid Build Coastguard Worker // than one.
next_capacity(size_t capacity,size_t * result)168*8d67ca89SAndroid Build Coastguard Worker bool AtexitArray::next_capacity(size_t capacity, size_t* result) {
169*8d67ca89SAndroid Build Coastguard Worker   if (capacity == 0) {
170*8d67ca89SAndroid Build Coastguard Worker     *result = page_end(sizeof(AtexitEntry)) / sizeof(AtexitEntry);
171*8d67ca89SAndroid Build Coastguard Worker     return true;
172*8d67ca89SAndroid Build Coastguard Worker   }
173*8d67ca89SAndroid Build Coastguard Worker   size_t num_bytes;
174*8d67ca89SAndroid Build Coastguard Worker   if (__builtin_mul_overflow(page_end_of_index(capacity), 2, &num_bytes)) {
175*8d67ca89SAndroid Build Coastguard Worker     async_safe_format_log(ANDROID_LOG_WARN, "libc", "__cxa_atexit: capacity calculation overflow");
176*8d67ca89SAndroid Build Coastguard Worker     return false;
177*8d67ca89SAndroid Build Coastguard Worker   }
178*8d67ca89SAndroid Build Coastguard Worker   *result = num_bytes / sizeof(AtexitEntry);
179*8d67ca89SAndroid Build Coastguard Worker   return true;
180*8d67ca89SAndroid Build Coastguard Worker }
181*8d67ca89SAndroid Build Coastguard Worker 
expand_capacity()182*8d67ca89SAndroid Build Coastguard Worker bool AtexitArray::expand_capacity() {
183*8d67ca89SAndroid Build Coastguard Worker   size_t new_capacity;
184*8d67ca89SAndroid Build Coastguard Worker   if (!next_capacity(capacity_, &new_capacity)) return false;
185*8d67ca89SAndroid Build Coastguard Worker   const size_t new_capacity_bytes = page_end_of_index(new_capacity);
186*8d67ca89SAndroid Build Coastguard Worker 
187*8d67ca89SAndroid Build Coastguard Worker   set_writable(true, 0, capacity_);
188*8d67ca89SAndroid Build Coastguard Worker 
189*8d67ca89SAndroid Build Coastguard Worker   bool result = false;
190*8d67ca89SAndroid Build Coastguard Worker   void* new_pages;
191*8d67ca89SAndroid Build Coastguard Worker   if (array_ == nullptr) {
192*8d67ca89SAndroid Build Coastguard Worker     new_pages = mmap(nullptr, new_capacity_bytes, PROT_READ | PROT_WRITE,
193*8d67ca89SAndroid Build Coastguard Worker                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
194*8d67ca89SAndroid Build Coastguard Worker   } else {
195*8d67ca89SAndroid Build Coastguard Worker     // mremap fails if the source buffer crosses a boundary between two VMAs. When a single array
196*8d67ca89SAndroid Build Coastguard Worker     // element is modified, the kernel should split then rejoin the buffer's VMA.
197*8d67ca89SAndroid Build Coastguard Worker     new_pages = mremap(array_, page_end_of_index(capacity_), new_capacity_bytes, MREMAP_MAYMOVE);
198*8d67ca89SAndroid Build Coastguard Worker   }
199*8d67ca89SAndroid Build Coastguard Worker   if (new_pages == MAP_FAILED) {
200*8d67ca89SAndroid Build Coastguard Worker     async_safe_format_log(ANDROID_LOG_WARN, "libc",
201*8d67ca89SAndroid Build Coastguard Worker                           "__cxa_atexit: mmap/mremap failed to allocate %zu bytes: %m",
202*8d67ca89SAndroid Build Coastguard Worker                           new_capacity_bytes);
203*8d67ca89SAndroid Build Coastguard Worker   } else {
204*8d67ca89SAndroid Build Coastguard Worker     result = true;
205*8d67ca89SAndroid Build Coastguard Worker     prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, new_pages, new_capacity_bytes, "atexit handlers");
206*8d67ca89SAndroid Build Coastguard Worker     array_ = static_cast<AtexitEntry*>(new_pages);
207*8d67ca89SAndroid Build Coastguard Worker     capacity_ = new_capacity;
208*8d67ca89SAndroid Build Coastguard Worker   }
209*8d67ca89SAndroid Build Coastguard Worker   set_writable(false, 0, capacity_);
210*8d67ca89SAndroid Build Coastguard Worker   return result;
211*8d67ca89SAndroid Build Coastguard Worker }
212*8d67ca89SAndroid Build Coastguard Worker 
213*8d67ca89SAndroid Build Coastguard Worker static AtexitArray g_array;
214*8d67ca89SAndroid Build Coastguard Worker static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
215*8d67ca89SAndroid Build Coastguard Worker 
atexit_lock()216*8d67ca89SAndroid Build Coastguard Worker static inline void atexit_lock() {
217*8d67ca89SAndroid Build Coastguard Worker   pthread_mutex_lock(&g_atexit_lock);
218*8d67ca89SAndroid Build Coastguard Worker }
219*8d67ca89SAndroid Build Coastguard Worker 
atexit_unlock()220*8d67ca89SAndroid Build Coastguard Worker static inline void atexit_unlock() {
221*8d67ca89SAndroid Build Coastguard Worker   pthread_mutex_unlock(&g_atexit_lock);
222*8d67ca89SAndroid Build Coastguard Worker }
223*8d67ca89SAndroid Build Coastguard Worker 
224*8d67ca89SAndroid Build Coastguard Worker // Register a function to be called either when a library is unloaded (dso != nullptr), or when the
225*8d67ca89SAndroid Build Coastguard Worker // program exits (dso == nullptr). The `dso` argument is typically the address of a hidden
226*8d67ca89SAndroid Build Coastguard Worker // __dso_handle variable. This function is also used as the backend for the atexit function.
227*8d67ca89SAndroid Build Coastguard Worker //
228*8d67ca89SAndroid Build Coastguard Worker // See https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dso-dtor.
229*8d67ca89SAndroid Build Coastguard Worker //
__cxa_atexit(void (* func)(void *),void * arg,void * dso)230*8d67ca89SAndroid Build Coastguard Worker int __cxa_atexit(void (*func)(void*), void* arg, void* dso) {
231*8d67ca89SAndroid Build Coastguard Worker   int result = -1;
232*8d67ca89SAndroid Build Coastguard Worker 
233*8d67ca89SAndroid Build Coastguard Worker   if (func != nullptr) {
234*8d67ca89SAndroid Build Coastguard Worker     atexit_lock();
235*8d67ca89SAndroid Build Coastguard Worker     if (g_array.append_entry({.fn = func, .arg = arg, .dso = dso})) {
236*8d67ca89SAndroid Build Coastguard Worker       result = 0;
237*8d67ca89SAndroid Build Coastguard Worker     }
238*8d67ca89SAndroid Build Coastguard Worker     atexit_unlock();
239*8d67ca89SAndroid Build Coastguard Worker   }
240*8d67ca89SAndroid Build Coastguard Worker 
241*8d67ca89SAndroid Build Coastguard Worker   return result;
242*8d67ca89SAndroid Build Coastguard Worker }
243*8d67ca89SAndroid Build Coastguard Worker 
__cxa_finalize(void * dso)244*8d67ca89SAndroid Build Coastguard Worker void __cxa_finalize(void* dso) {
245*8d67ca89SAndroid Build Coastguard Worker   atexit_lock();
246*8d67ca89SAndroid Build Coastguard Worker 
247*8d67ca89SAndroid Build Coastguard Worker   static uint32_t call_depth = 0;
248*8d67ca89SAndroid Build Coastguard Worker   ++call_depth;
249*8d67ca89SAndroid Build Coastguard Worker 
250*8d67ca89SAndroid Build Coastguard Worker restart:
251*8d67ca89SAndroid Build Coastguard Worker   const uint64_t total_appends = g_array.total_appends();
252*8d67ca89SAndroid Build Coastguard Worker 
253*8d67ca89SAndroid Build Coastguard Worker   for (ssize_t i = g_array.size() - 1; i >= 0; --i) {
254*8d67ca89SAndroid Build Coastguard Worker     if (g_array[i].fn == nullptr || (dso != nullptr && g_array[i].dso != dso)) continue;
255*8d67ca89SAndroid Build Coastguard Worker 
256*8d67ca89SAndroid Build Coastguard Worker     // Clear the entry in the array because its DSO handle will become invalid, and to avoid calling
257*8d67ca89SAndroid Build Coastguard Worker     // an entry again if __cxa_finalize is called recursively.
258*8d67ca89SAndroid Build Coastguard Worker     const AtexitEntry entry = g_array.extract_entry(i);
259*8d67ca89SAndroid Build Coastguard Worker 
260*8d67ca89SAndroid Build Coastguard Worker     atexit_unlock();
261*8d67ca89SAndroid Build Coastguard Worker     entry.fn(entry.arg);
262*8d67ca89SAndroid Build Coastguard Worker     atexit_lock();
263*8d67ca89SAndroid Build Coastguard Worker 
264*8d67ca89SAndroid Build Coastguard Worker     if (g_array.total_appends() != total_appends) goto restart;
265*8d67ca89SAndroid Build Coastguard Worker   }
266*8d67ca89SAndroid Build Coastguard Worker 
267*8d67ca89SAndroid Build Coastguard Worker   // Avoid recompaction on recursive calls because it's unnecessary and would require earlier,
268*8d67ca89SAndroid Build Coastguard Worker   // concurrent __cxa_finalize calls to restart. Skip recompaction on program exit too
269*8d67ca89SAndroid Build Coastguard Worker   // (dso == nullptr), because the memory will be reclaimed soon anyway.
270*8d67ca89SAndroid Build Coastguard Worker   --call_depth;
271*8d67ca89SAndroid Build Coastguard Worker   if (call_depth == 0 && dso != nullptr) {
272*8d67ca89SAndroid Build Coastguard Worker     g_array.recompact();
273*8d67ca89SAndroid Build Coastguard Worker   }
274*8d67ca89SAndroid Build Coastguard Worker 
275*8d67ca89SAndroid Build Coastguard Worker   atexit_unlock();
276*8d67ca89SAndroid Build Coastguard Worker 
277*8d67ca89SAndroid Build Coastguard Worker   if (dso != nullptr) {
278*8d67ca89SAndroid Build Coastguard Worker     __unregister_atfork(dso);
279*8d67ca89SAndroid Build Coastguard Worker   } else {
280*8d67ca89SAndroid Build Coastguard Worker     // If called via exit(), flush output of all open files.
281*8d67ca89SAndroid Build Coastguard Worker     __libc_stdio_cleanup();
282*8d67ca89SAndroid Build Coastguard Worker   }
283*8d67ca89SAndroid Build Coastguard Worker }
284